两年前大三下学期,听完崔神的GIS中的现代软件技术,和很多同学一样把《COM技术内幕》《Visual C++开发COM程序》奉为经典。但是苦于当初知识水平有限,又没有写过任何的COM组件甚至没有用过COM组件,这些书都是翻了两页就看不下去了,对于我来说实在是太晦涩。
前几天用C++做了些ArcGIS的二次开发,对组件有了点使用体会,反过来在看《COM技术内幕》有种豁然开朗的感觉。争取今天把这周看书的体会都写下来,结合AE的二次开发做份总结。
1、什么是COM
COM组件是以Win32动态链接库(DLLs)或可执行文件(EXEs)的形式发布的可执行代码组成的。遵循COM规范编写的组件将能够满足对组件建构的所有需求(动态链接、信息封装)。
这里比较困惑的应该是DLL和COM的区别,依据我的理解,这两者的目的性不一致。DLL是为了1、动态加载,节省程序运行中的内存占用。2、函数库的重用;COM是为了1、跨平台2、接口级别上的重用。
实际上COM是使用DLL来给组件提供动态链接的能力的。
2、接口
在COM中接口就是一切,对于客户来说,一个组件就是一个接口集。C++中是使用纯虚函数来实现的接口功能。在ObjBase.h文件中我们能发现Interface关键字的定义:
#define__STRUCT__ struct
#defineinterface __STRUCT__
可见,C++中一个Inferface就是一个结构体。我们下面看一个简单的例子:
void trace(const char* pMsg){
cout<<pMsg<<endl;
}
interface IX{
virtual void _stdcall Fx1()=0;
virtual void _stdcall Fx2()=0;
};
interface IY{
virtual void _stdcall Fy1()=0;
virtual void _stdcall Fy2()=0;
};
//接口的实现
class CA:public IX,public IY
{
public:
virtual void _stdcall Fx1(){cout<<"CA::Fx1"<<endl;}
virtual void _stdcall Fx2(){cout<<"CA::Fx2"<<endl;}
virtual void _stdcall Fy1(){cout<<"CA::Fy1"<<endl;}
virtual void _stdcall Fy2(){cout<<"CA::Fy2"<<endl;}
};
在这个例子中,我们定义了两个接口 IX 、 IY ,接口的实现是通过他们的派生类 CA 来实现的。
int main()
{
trace("创建组件实例");
CA *pA=new CA();
//定义IX接口指针
IX* pIX=pA;
trace("使用IX接口");
pIX->Fx1();
pIX->Fx2();
//定义IY接口指针
IY* pIY=pA;
trace("使用IY接口");
pIY->Fy1();
pIY->Fy2();
trace("删除组件");
delete pA;
return 0;
}
由C++的继承机制我们知道,可以将一个派生类的实例初始化基类的指针,那么此基类指针实际上则指向了派生类的对象地址。pIX->Fx1(),则会调用派生类CA中的方法。
简单的来理解接口就是这样子的,但是“客户和组件之间只是通过接口进行通信”该简例中,客户与组件的通信是通过一个指向类CA的指针而不是通过指向接口的指针完成的。使用指向CA的指针要求客户知道类CA的定义(通常是一个头文件)。在类CA的声明中有许多有关实现的细节。对这些细节的修改将使得客户必须被重新编译。但是,增加或减少组件的接口不应打断已有的客户。这就是我们为什么一再坚持客户和组件质检只应通过接口进行通信。记住,接口是由没有实现细节的纯虚基类实现的。