一、什么是COM
COM,中文名称是组件对象模型。是微软公司开发的一种对象结构和规范,用以建立软件模块间的通讯。
二、COM与DLL的优劣分析
从结构化编程,到面向对象编程,再到COM编程,目标只有一个,就是希望软件能像积木一样是累起来的,是组装起来的,而不是一点点编出来的。结构化编程是函数块的形式,通过把一个软件划分成许多模块,每个模块完成各自不同的功能,尽量做到高内聚低耦合,这是像组装的概念。
由于DLL并不是一种积木方式,虽然它是Windows的基础,但有下面几个缺点:
(1)、各编译器对C++函数的名称修饰不兼容问题。
对于C++函数,编译器要根据函数的参数信息为它生成修饰名,DLL库里存的就是这个修饰名,但是不同的编译器产生修饰的方法不一样,所以在VC里编写的DLL在其他环境里就可能用不了。不过也可以用extern “C”来强调使用标准的C函数特性,关闭修饰功能,但这样也丧失了C++的重载多态性功能。
(2)、路径问题
放在自己的目录下面,别人的程序就找不到,放在系统目录下就可能有重名的问题。而真正的组件应该可以放在任何地方甚至可以不在本机,用户根本不需考虑这个问题。
(3)、DLL与EXE的依赖问题。
一般都是用隐匿连接的方式,就是编程的时候指明用什么DLL,这种方式很简单,它在编译时就把EXE与DLL绑在一起。如果DLL发行了一个新版本,则很有必要重新链接一次,因为DLL里面函数的地址可能已经发生了改变。
事实上DLL的缺点就是COM的优点。首先要把握信一点,COM和DLL一样都是基于二进制的代码重用,所以它不存在类库征用时的问题。另一个关键点是,COM本身也是DLL,既使是ActiveX控件.ocx实际上也是DLL,所以说DLL在重用上有很大的优势,只不过通过制订复杂的COM协议,通过COM本身的机制改变了重用的方法。COM没有重名问题,因为根本不是通过函数名来调用函数,而是通过虚函数表,自然也不会有函数名修饰的问题。路径问题也不复存在,因为是通过查注册来找组件的,放在什么地方都可以,即使在别的机器上也可以。也不用考虑和EXE的依赖关系,因为它们两者之间是松散的结合在一起,可以轻松的换上组件的一个新版本,而应用程序混然不觉。
COM组件实际上是一个C++类,而接口都是纯虚类,而且它没有成员变量。组件从接口派生而来,它实现了虚函数,仅此而已。COM中所有的函数都是虚函数,都必须通过虚函数表VTable来调用。
COM组件有3个最基本的接口类,分别是IUnknown,IClassFactory和IDispatch。COM规范规定任何组件、任何接口都必须从IUnknown继承、IUnknown包含三个函数,分别是QueryInterface、AddRef和Release。QueryInterface用于查询组件实现的其他接口,就是查看这个组件的父类中还有哪些接口类;AddRef用于增加引用计数;Release用于减少引用计数。
IClassFactory的作用是创建COM组件。COM组件实际上就是一个类,实例化一个类对象用new命令。创建组件的工作主浊由类厂负责。每个组件都必须有一个与之相关的类厂,由类厂创建组件实例。IClassFactory最重要的一个函数是CreateInstance,顾名思义就是创建组件实例,一般情况下不会直接调用它,API函数都已经将它封装好了,只有某些特殊情况下才会由自己调用。
IDispatch叫做调试接口。脚本语言典型的如VBScript,JavaScript它们并不支持指针,没有指针所以就没法用多态性,也没法调用虚函数。而分布式应用DCOM也是COM组件的一个主要领域,它不得不被这些脚本语言所调用,既然虚函数表的方式行不通,IDispatch应运而生。调度接口把每一个函数每一个属性都编上号,客户程序要调用这些函数属性的时候就把这些编号传给IDispatch接口即可,IDispatch再根据这些编号调用相应的函数,仅此而已。IDispatch接口的主要函数是Invoke,客户程序都调用它,然后Invoke再调用相应的函数。
下面展示COM编程的基本流程:
IUnknown *pUnk = NULL;
iObject *pObject = NULL;
CoInitialize(NULL);
CoCreateInstance(CLSID_Object,CLSCTX_INPROC_SERVER,NULL,IID_IDnknown,(void**)&pUnk);
pUnk->QueryInterface(IID_IOject,(void**)&pObject);
pUnk->Release();
pObject->Func();
PObject->Release();
CoUninitialize();
COM对象就像是一个黑匣子。通常是由DLL来实现的,就像传统的DLL,COM对象提供了接口,在程序中可以调用这些接口来完成编程任务。使用COM对象和使用C++差不多,区别在于以下几个方面:
(1)COM对象比C++对象包装更严密。
COM对象的公用方法组合到接口中,如果想使用方法,就必须创建对象来获取适当的接口。接口包含相关的能够由用户来控制的物体特性一系列方法。
(2)COM对象的创建方法与C++对象的创建方法不一样。
DirectX API简化了创建DirectX对象的过程。
(3)、必须使用COM专有技术来控制其生命周期。
(4)、COM对象不需要显式加载。
COM对象通常包含在DLL中。可以在使用COM对象时,不需要显式加载DLL和静态库。每一个COM对象都有唯一用于创建该对象的标识码。COM会自动装载正确的DLL。
(5)、COM是二进制的。COM对象可以由多种语言来编制或使用。而不需要知道任何的对象源代码。