HRESULT __stdcall CoCreateInstace( |
函数参数clsid是要创建组件的CLSID,pIUnknownOuter用于聚合组件,如果不使用可以设置为NULL。参数dwClsContext则限定了所创建组件的执行上下文。最后两个参数iid和ppv则分别为要使用接口的IID和返回得到的接口指针。在使用时只需将CLSID、IID等作为参数传入即可创建相应的组件并从输出参数ppv得到所请求接口的指针。如果函数是直接创建组件的,那么在函数返回时组件将创建完毕,这样客户将无法对组件的创建过程进行任何干预,灵活性太差。因此,CoCreateInstance()在函数内部实现中通过调用CoGetClassObject()函数先创建一种专门用来创建组件的组件来解决此问题。这种用途的组件被称为类厂(class factory)。
类厂所支持的用以创建组件的接口是IClassFactory,该接口从IUnknown派生,并具有两个自己的接口成员函数CreateInstance()和LockServer()。这两个成员函数分别用于创建COM组件对象和控制组件的生存期。下面先给出CreateInstance()的函数声明:
HRESULT __stdcall CreateInstance(IUnknown* pIUnknownOuter, const IID& iid, void** ppv); |
可以看出,这个用于创建组件对象的CreateInstance()函数并未包含一个用来接受CLSID的参数,显然该函数将只能创建同某个CLSID相应的组件。对于一个类厂,由于只能通过CreateInstance()函数去创建组件,因此只能创建与某个特定CLSID相应的组件。
创建类厂的CoGetClassObject()函数将接收一个CLSID作为参数并返回指向类厂对象IClassFactory接口的指针。客户将可以通过此指针来创建所需要的组件并返回某接口的指针。通过此指针,客户将可以直接调用新创建的COM对象接口的成员函数,从而获得COM对象的所有服务。
在用CoGetClassObject()创建类厂对象时,如果COM对象是进程内组件(组件与客户处于同一进程地址空间,通常多以DLL形式存在),CoGetClassObject()将调用DLL模块的DllGetClassObject()引出函数并把clsid、iid和ppv等参数传递进去以创建类厂,并返回类厂对象的接口指针。
如果COM对象是进程外组件(拥有独立的进程地址空间,通常多以EXE形式存在),则CoGetClassObject()将要首先启动组件进程,并一直等待到组件进程通过CoRegisterClassObject()函数将类厂注册到COM后,才会返回COM中相应的类厂信息。一旦组件进程退出,此注册的类厂对象也就不再有效,需调用CoRevokeClassObject()函数予以通知。
客户程序对COM组件的调用主要分对进程内组件调用和进程外调用两种情况。在具体过程上却并没有什么太大的区别。为了能够使用COM库提供的API函数,首先要用CoInitialize()初始化COM库。
虽然通过CLSID和ProgID都可以标识一个组件,但ProgID显然要比CLSID更易于理解和使用,因此通常很少直接使用CLSID,而是通过使用CLSIDFromProgID(),根据ProgID得到组件的CLSID。进而以此返回的CLSID作为参数去调用CoGetClassObject()以创建类厂对象并返回类厂接口指针。通过该指针调用类厂对象的CreateInstance()接口成员函数,执行结果将创建与CLSID相应的组件对象并返回IUnknown接口指针。通过此接口的QueryInterface()成员函数将能够进一步获过程将是隐含进行的,使用更为简单。
取组件的其他接口指针,从而使用组件提供的各种服务。
最后,通过Release()函数释放接口指针。如果使用的进程内组件,在调用CoUninitialize()函数释放COM库资源之前,应首先调用CoFreeUnusedLibraries()将其从内存卸载。由于在CoCreateInstance()函数内部实现了对CoGetClassObject()的调用并一直完成了类厂对象接口函数对组件的创建和类厂对象的释放.