Com Introduction
(wang hailong)
1.神话<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
我两年前学习使用COM,现在想起那段经历,还有些困惑不解。
我不明白,人们为什么要把一些很明了的事情,弄得玄之又璇,而对一些真正的杰出特性却避而不谈?我写本文的目的,是希望对COM感兴趣的开发人员,不再重蹈我的覆辙,不再被一些说法和资料误导。
刚开始接触到COM的概念,我虚心求教,从各方面得知了如下的COM神话:
(1)COM是位置无关的。你不用知道COM组件放在哪里。
(2)COM是二进制标准,语言无关。你可以用多种编程语言开发COM组件,调用COM组件。
等等。太神奇了。简直象魔术一般。
我这个一头雾水的初学者,对神秘的COM充满了敬仰,却又无从下手。
后来,我只好停止学习这些概念,直接从MSDN入手。我运行所有的COM例子,阅读MSDN资料。不由得惊叹,COM的构思之巧妙,但这些巧妙之处,却鲜有人提及。而那些被传得沸沸扬扬的神话,有很多故弄玄虚的成分。
(1)COM是位置无关的。你不用知道COM组件放在哪里。
是的,你不用知道COM组件放在哪里。但是你需要知道一个COM组件的ID(一个保证独一无二的数字串),这个组件ID存放在windows注册表里,里面记载着这个COM组件的位置。当你调用这个组件的时候,你需要把COM组件ID作为参数,获取这个组件。Windows系统根据这个组件ID,查找注册表,找到组件的位置,启动或者返回对应的组件。
等一下,问题不是这么简单。COM组件需要从组件工厂创建,所以,你获得的组件ID是组件工厂的ID,你先创建一个组件工厂,然后,通过这个组件工厂,创建你所需要的组件。(注意,你通过“工厂接口”使用组件工厂。)
组件工厂根据什么来创建你需要的组件呢?通过接口ID。接口ID也是一个保证独一无二的数字串,这个接口ID对应一个接口的定义。你的头文件必须包含这个接口的定义。
你把接口ID传给组件工厂,组件工厂返回一个组件接口给你。你通过这个接口,调用组件的功能。这就是,“你的头文件必须包含这个接口的定义”的原因。
我们来看,我们不需要位置信息,那我们需要什么信息?组件ID,接口ID,接口定义,(当然,还有“工厂接口”的定义)。
这种复杂的调用是一个优点?还是一个缺点?显然是一个缺点,却被粉饰成一个神话般的优点。这种方法,是为了把部署信息,尽量集中到注册表里面。你只要把组件正确注册,客户程序就可以正确运行。单纯为了部署的目的,这种代价是否值得?
如果只是为了部署的目的,我觉得,这种代价不值得。我们看看Java和.Net工程里面各种配置文件的使用,就知道,有很多更灵活有效的部署方法。
COM的构思远远超过了部署的目的,下一章会讲到。
(2)COM是二进制标准,语言无关。你可以用多种编程语言开发COM组件,调用COM组件。
COM确实是二进制标准,核心是一个VTable(虚表),这个“虚表”的概念和C++类的虚函数表的概念一样。所以,用C++实现COM,非常自然。
前面提到,你如果想使用组件,你必须先知道这个组件的接口定义,你的头文件必须包含这个接口的定义。假如我们是用C++实现COM,工程里面会包括一个C++头文件,头文件里面包括了接口的定义。如果客户端也使用C++,只要把这个C++头文件拷贝给用户就行了,用户把头文件包括在C++工程里面,就可以使用这个接口了。
如果你不用C++呢?那也好办,我不是把提供C++头文件给你了吗?里面已经包含了接口的定义。你先研究一下,这个接口的虚表是怎么构成的,然后,你用你的语言,照样写一个头文件,只要遵守我的虚表结构就行了。
:-) 只是一个玩笑。
我还有个折衷的办法,您用过CORBA吗?CORBA使用IDL进行接口定义,您可以用工具把IDL翻译成各种语言,C++,Java,等等。我也用MIDL写一套接口定义,您也可以用工具把MIDL翻译成各种语言,C++,VB,Delphi等等。什么?您没有这种工具?那么您写一个这样的工具就行了,也不是很难,只不过是生成一个虚表结构。
好了,我们有MIDL作为公用的交流语言,现在问题都解决了。您还有什么问题吗?哦,您需要头文件。您用什么语言?C++?好,我把C++头文件发给您。还有那位朋友,您使用什么语言?您用其它的语言,好,我把MIDL头文件发给您。您找到一个转换工具,把MIDL转换成您的语言就行了。
哎呀,这么多人来索要头文件,太麻烦了。我还有个方法。我把这些接口定义信息,和组件一起放在注册表里吧。我不是给了你组件ID吗?你到注册表里查到这个组件,里面会有一个叫做“类型库”的东西。好了,现在您可以从“类型库”导出您的头文件了。什么,您问我怎么用“类型库”?对不起,我忘了告诉您,您得通过ITypeInfo接口使用它。
您嫌麻烦?一点都不麻烦,您看,VC,VB,Delphi都提供了自动导出组件接口定义的向导。这些事情都不用您亲自动手。
现在,我们有了一套完整的解决方案。COM是二进制标准,语言无关。
您还有问题,VB不支持指针,怎么调用虚表VTable?您为什么要用VB调用COM呢?好吧,我再多做一步工作,让我的COM组件支持IDispathch接口,IDispathch接口叫做自动化接口。IDispathch接口会查表,你把函数名交给IDispathch接口,IDispathch接口会自动把请求派发,进行处理。C++的用户也不用着急,我还保留以前的虚表VTable。现在,我给我的组件接口命名为“双接口”dual interface.
终于松了一口气,现在,我们有了一套完整的解决方案。COM是二进制标准,语言无关。
2. “群件”—— powered by queryInterface
“群件”是Lotus莲花公司的概念,我这里借用一下,表达我对COM组件的赞叹。
COM组件的核心思想就是“二进制接口”——VTable。
我们来看IUnknow接口的三个方法。
addRef和release两个方法,管理组件引用计数,我对这两个方法,持保留态度(negative opinion :-)。
第三个方法,queryInterface,才是整个COM组件思想的精华体现。
通过queryInterface,你可以获取另一个接口,另一种组件服务。
为什么我把COM组件称为“群件”,原因就在于此。每个COM组件都支持一组接口,一“群”接口,一组功能服务,而不是单一的功能。
可能会有人说,这也太绝对了吧,有的时候,人们只需要一个简单功能的组件。我要说,最简单的COM组件,也要实现两个接口——其中一个是IUnkonw,另一个是自己定义的接口。(我这里有些抬杠了。:-)
queryInterface如此出色。我在EJB和CORBA中,都看不到类似的东西。也许有类似的情况,比如,组件的一个方法返回另一个组件。但远远做不到COM组件这种自然的程度。COM组件天生就支持一“群”接口。
下面我们来分析queryInterface的工作原理。
为了说明白这个问题,我先引入一些java和Design Pattern的概念。
Java对象能够实现多个接口。外部程序在使用Java对象的时候,有时候,就要判断Java对象是否支持某一个接口。比如,当外部程序要比较两个Java对象的大小的时候,就需要判断这些Java对象是否支持Comparable接口。诸如这样的语句if(obj instanceof Comparable)。我们来看,这个语句多么象queryInterface。queryInterface也是用来查询组件是否支持某个接口的。
我们在进行类设计的时候,不希望只提供一个很“宽”的接口,包含所有操作。总是试图为不同的调用者分配不同的“角色”,即,提供一组比较“窄”的接口。为不同要求,不同权限的调用者,返回不同的接口。在Design Pattern中,这称为Adapter Pattern。
COM组件正是基于Adapter Pattern创建的。首先,你获得IUnknow接口,你需要某个服务的时候,才从IUnknow接口出发,去查找特定的接口。
通过上面的分析,我们可以看到,Java对象和COM组件很相像。但两者的实现机制却大不相同,Java对象根据Java语言的特性实现,所有的接口实现都包含同一个类中。又有人要说了,Java对象也可以包含一些其它对象,把一些操作交给这些对象。我这里说的是,这同一个类必须显示地声明,实现所有的接口。比如
class MyObject implements Comparable, Serializable, Interface1, …, interface n.
只有这样,obj instanceof Comparable,obj instanceof Interface1这样的语句才能返回真。
COM组件没有这样的限制。COM组件只返回二进制的VTable。COM组件本身可以不实现这些VTable,可以返回其它组件的VTable。不同的接口,可以在同一个组件中实现,也可以在不同的组件中实现。这也就是COM重用的“聚合”和“包含”等概念。
而且,COM组件的生存状态也很灵活,可以存在于客户程序的进程空间,称为进程内组件,也可以存在于独立于客户程序的进程空间,称为进程间组件。
当一个COM组件(我们称这个组件为A)的queryInterface方法返回其它组件(我们称这个组件为B)时,我们来看,现在,A组件的地位很像B组件的组件工厂,通过A组件创建B组件。我们现在可以明白,为什么不直接创建组件,而要采用一种不直观的方法,通过另一个组件——工厂组件来创建组件。这样,组件创建的概念就统一起来了——组件通过工厂组件创建。
我不知道,这是一种巧合,还是一种设计上的深思熟虑。不管怎样,COM组件的设计构思给了我深深的震撼,真是巧夺天工。
注意,由于queryInterface的自反性要求,实际上的处理要复杂一些。queryInterface的自反性:组件A的queryInterface返回组件B的接口,那么,调用这个组件B的queryInterface,也能够查到组件A的接口。还有甚者,如果组件B的queryInterface返回组件C的接口,组件C的queryInterface,也能够查到组件A的接口。
想到queryInterface的种种好处,这些复杂程度,还是可以忍受的。:-)
这还不是COM的全部,只是COM的基础原理。更精彩的是建立在COM基础的一系列技术,Structured Storage,OLE Document,Automation,ActiveX等等。每一种技术都非常出色,构思之巧妙,令人赞叹不已。COM标准给我们带来的这么多麻烦,似乎都可以忍受了。
强烈建议使用这些建立在COM基础上的技术,至少尝试体会其中的思想,会有很多启发。从头开始,定义开发自己的基础COM组件?:-) 我不知道。限于眼界,我还从来没有看到过任何出色的基础COM组件,至少没有IStorage,IDispatch等微软自己定义的接口出色。
微软很多杰出的应用程序都是COM组件。Office系列,Word, Excel, PowerPoint, IE等等。其他的应用程序厂家,以前提供自己的插件标准,现在提供自己的COM组件。
分享到:
相关推荐
An Introduction Second edition, in progress November 5, 2017 Richard S. Sutton and Andrew G. Barto The text is now complete, except possibly for one more case study to be added to Chapter 16. The ...
“This ‘book’ is a small set of tutorials about using libuv [https://github.com/libuv/libuv] as a high performance evented I/O library which offers the same API on Windows and Unix. It is meant to ...
资源可在github上下载。地址:https://github.com/amueller/introduction_to_ml_with_python/blob/master/data/ram_price.csv
- **网站**:[www.wiley.com](http://www.wiley.com)。 #### 七、结语 《Electric Circuits》第九版作为一本经典的电路理论教材,不仅适合电气工程专业的本科生和研究生使用,也适合相关领域的研究人员和技术人员...
ChaptEr 1 an introduction to Programming 1 ChaptEr 2 Beginning the Problem-Solving Process 23 ChaptEr 3 Variables and Constants 51 ChaptEr 4 Completing the Problem-Solving Process 75 ChaptEr 5 The ...
版权归作者所有,任何形式转载请联系作者。 作者:breaker(来自豆瓣) 来源:...3. 请从这个版本开始读 Introduction to 3D Game Programming with DirectX 9.0c: A Shader Approach
An Introduction to libuv中文版来自于一个github上的帮助文档的翻译项目,原项目地址为https://github.com/forhappy/uvbook,这个项目是libuv这个C++库的中文帮助文档。但它是rst格式的文档,需要用python工具...
An Introduction to libuv中文版来自于一个github上的帮助文档的翻译项目,原项目地址为https://github.com/forhappy/uvbook,这个项目是libuv这个C++库的中文帮助文档。但它是rst格式的文档,需要用python工具...
This book provides a comprehensive introduction to the modern study of com- puter algorithms. It presents many algorithms and covers them in considerable depth, yet makes their design and analysis ...
Introduction: FlashFXP is the premier FTP, FTPS, SFTP, FXP file transfer program used to transfer files in an intuitive interface. FlashFXP provides an easy to use interface for common file transfer...
- 对于覆盖National Instruments产品的专利信息,用户可以在软件的帮助文档中找到相关链接,或者访问ni.com/legal/patents页面进行查询。 #### 7. 教程内容概述 - **LabVIEW Six Hour Course – Instructor Notes**...
网络分析技术详解 第二版 (introduction-to-network-analysis-2nd-edition.pdf) 是<高级网络分析技术> 的预读本。来源于 podbooks.com 找了很久才找到的。
In this book, you’ll see how to use the Wolfram Language to do a great many things. You’ll learn how to think computationally about what you want to do, and how to communicate it to a computer using...
Introduction to Robotics: Mechanics and Control, John J. Craig, Addison-Wesley Publishing Com- pany, 3rd Edition, 2003
Introduction to Python Programming and developing GUI applications with PyQT.pdf 作者B.M. Harwani © 2012 Course Technology, a part of Cengage Learning. ISBN-13: 978-1-4354-6097-3 ISBN-10: 1-4354-6097...
https://www.amazon.com/Introduction-Computation-Programming-Using-Python/dp/0262529629/ref=sr_1_2?s=books&ie=UTF8&qid=1543410901&sr=1-2&keywords=introduction+to+computing+using+python
All rights are reserved by the Publisher, whether the whole or part of the material is concerned, specifically the rights of ...Springer is part of Springer Science+Business Media (www.springer.com)