var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-333696-1']); _gaq.push(['_trackPageview']); _gaq.push(['_trackPageLoadTime']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
  • 2008年09月19日

    COM编程(2)

    分类:

    COM中所有的接口都派生于IUnknown接口,IUnknown是基础接口,它定义在UnKnwn.h头文件中。IUnknown 定义了三个方法: QueryInterface, AddRefRelease;纯虚成员函数QueryInterface,客户可以通过此函数来查询组件是否支持某个特定的接口,返回一个指向组件支持的接口的指针,定义在头文件WinError.h中的HRESULT值最重要1位表示函数调用是否成功,低16位包含函数的返回码,其余的15位包含此类型及返回值起源的更详细信息。如果组件支持该接口则返回S_OK,否则返回E_NOINTERFACE并将相应指针的返回值置成NULL,应分别使用SUCCEEDED宏或FAILED宏来比较返回值。COM接口没有虚析构函数,但有一个非常严格的协议来删除对象。每一个COM类都有一个数据成员,比如MFC中是m_dwRef,它被用作记录对象的“用户”数目。每当组件程序返回一个新的接口指针时,它都会调用AddRef,在函数AddRef中将m_dwRef1;当客户程序使用完该指针时,它会调用Release,在函数Release中将m_dwRef1直到为0,当m_dwRef值为0时对象会将子集销毁掉,全部过程见MFC/OLE IUnknown ImplementationMFC中,对象构造会将m_dwRef初始化为1,于是对象构造后不必调用AddRef,但客户对象对接口指针做拷贝需要。

     

    HKCR注册表下CLSID关键字下是当前系统安装所有组件的CLSID,每项下子关键字InprocServer32指明该组件所在的DLL文件名。HKCR注册表下大多是ProgID,它是指程序员给某个CLSID指定一个程序员易记的名字,表示程序员定义的标识符。ProgID下名为CLSID的关键字,其缺省值为组件的CLSID;其下名为CurlVer的关键字,其缺省值为组件当前版本的ProgIDCLSIDProgID可用函数ProgIDFromCLSID和函数CLSIDFromProgID来相互转换。128位的全局唯一标识符(GUID)是组件和接口的标识号,微软Visual C++提供了GUID Generator(guidgen.exe)UUID Generator(uuidgen.exe)来生成具有唯一性的GUID

     

    函数CoCreateInstance用传入的CLSID参数创建本地系统上相应组件的一个实例,并返回此组件实例的某个接口。函数CoGetClassObject接收一个CLSID作为参数并返回指向所创建组件即类场中某个接口的指针。函数CoCreateInstance实际上是通过函数CoGetClassObject实现的。因为函数CoCreateInstance无法控制将组件装载到内存中何处或检查客户是否有权限来创建该组件,于是它不够灵活,需要另外一个专门用于创建所需组件的组件,这就是类厂。类厂能创建只同某个特定的CLSID相应的组件,客户可以通过类厂所支持的接口来对类厂创建组件的过程加以控制。

     

    COM中的一个类对象代表一个特定COM类的全局静态域,等价于MFC中的CRuntimeClassCOM中类对象(class factory)之所以被称为类厂(class factory,精确讲应为对象厂(object factory)),是因为它实现了IClassFactory接口。IClassFactory包含两个方法:CreateInstanceLockServer。前者用作创建一个组件并返回一个IUnknown指针,后者用作锁定内存中打开的对象。

     

    大多数情况下,创建组件均使用CoCreateInstance而不是CoGetClassObject,但有两种例外:一是若想用不同于IClassFactory的某个创建接口来创建组件,则必须使CoGetClassObject,因此用IClassFactory2来创建组件时必须这样。另一种是若需创建同一组件的多个实例,那么使用CoGetClassObject将获取更高的侠侣,因为这样只需创建相应的类场一次,而CoCreateInstance则需为每个实例分别创建并释放相应的类场,再者CoGetClassObject可使客户对组件的创建过程进行更多的控制。

     

    组件创建过程:客户通过调用CoGetClassObject来启动组件的创建;接着COM库实现了CoGetClassObject函数,此函数查找指定组件,找着后装载实现此组件的DLL;接着DLL实现了被CoGetClassObject调用的DllGetClassObject函数(若类上下文是DLL),此外函数DllGetClassObject将查询IClassFactory接口,并将其返回给CoCreateInstance;这时创建好了类厂,客户将使用IClassFactory接口创建相应的组件,如调用IClassFactory::CreateInstance函数。

     

    函数DLLRegisterServerDllUnregisterServer的作用是在Windows注册表中注册某个组件或者取消某个组件的注册。Regsvr32.exe将调用这两个函数。一个DLL可以支持多个不同组件,于是它相当于是一个组件服务器。当使用完该DLL后,需要用函数CoFreeUnusedLibraries将其中内存中释放。在函数CoFreeUnusedLibraries内部,它调用DllCanUnloadNow函数以询问该DLL是否可以被卸载。IDL叫作接口定义,借助它用MIDL编译器(midl.exe)可生成代理和存根DLL。代理和存根DLL除了完成LPC(本地过程调用)外,它还需要对参数和返回值进行翻译和传递,客户程序调用的参数,先经代理DLL处理,它把参数及其它的一些调用信息组装成一个数据包传递给组件进程,这个过程称为参数列集(marshaling);组件进程即存根DLL端接收到数据报后,进行解包报参数信息提取出来,这个过程称为散集(unmarshalling);然后再进行实际接口功能调用。

     

    自动化(Automation)使得用解释性语言和宏语言访问COM组件更加容易,同时用这些语言编写组件也更为容易,自动化多是其它ActiveX技术的基础。自动化关注运行时的类型检查,它建立在COM基础上。一个自动化服务器实际上是一个实现了IDispatch接口的COM组件,而一个自动化控制器则是通过IDispatch接口同自动化服务器进行通信的COM客户。IDispatch接口的IDL描述在OAIdl.dil文件中。COM中的类型库等价于C++中的头文件,类型库将提供有关组件、接口、方法、属性、参数和结构的类型信息。类型库实际上是IDL文件的一个编译版本(.tlb),可以用编程访问,是一个二进制文件。类型库注册在HKCR\TypeLib关键字下,可以看到一个LibID列表。微软扩充IDL形成了ODL,可以描述组件对象的类型信息,它包括每个接口的类型信息和对象的类型信息,接口类型的描述完全兼容IDL,对象类型则用coclass关键字来描述。

     

    MFC用嵌套类实现COM接口(BEGIN_INTERFACE_PART),用接口映射表(BEGIN_INTERFACE_MAP)提供多接口支持;ATL使用多重继承实现COM接口,采用COM映射表(BEGIN_COM_MAP)。宏STDMETHOD_STDMETHODIMP_STDAPI_使用__stdcall参数传递约定来声明和实现函数。

     

    资源

    VC进行COM编程所必须掌握的理论知识

    COM技术内幕——微软组件对象模型》

    COM原理与应用》

    COM本质论》

    C++ Reference

    分享到:

    历史上的今天:

    美国主要电视网简介 2006年09月19日