在上一篇中,介绍了怎么样用动态链接库去实现COM,但组件对我们来说仍是不透明的,我们需要知道实现组件DLL的位置,必须自己来加载组件的CreateInstance函数来获得组件的指针.在书中第一篇就曾经提到过:COM组件可以透明地在网络上(或本地)被重新分配位置,而不会影响本地客户程序.所以,由客户端来调用DLL并不是什么好主意.必须有一种更好的办法让组件的实现更透明,更灵活!
    于是,就引入了类厂的概念.什么是类厂,类厂也是一个接口,它的职责是帮我们创造组件的对象.并返回给客户程序一个接口的指针.每个组件都必须有一个与之相关的类厂,这个类厂知道怎么样创建组件.当客户请求一个组件对象的实例时,实际上这个请求交给了类厂,由类厂创建组件实例,然后把实例指针交给客户程序。这么说有点难明白.先看一个伪实例.
 1.实现二个接口IX,IY        (上二节中有详细介绍)
 2.实现一个组件CA,实现了IX,IY接口.    (上二节中有详细介绍)
 3.对于这个组件进行注册,把组件的信息加入到注册表中.
     实现DllRegisterServer和DllUnregisterServer函数.函数具体功能就是把本组件的CLSID,ProgID,DLL的位置放入注册表中.这样程序就可以通过查询注册表来获得组件的位置.
 4.创建本组件类厂的实例
class CFactory:public IClassFactory
{
 virtual HRESULT __stdcall QueryInterface(const IID& iid,void** ppv);
 virtual ULONG   __stdcall AddRef();
 virtual ULONG   __stdcall Release();

 virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter,
  const IID& iid,
  void** ppv);
}
 在类厂实例中,主要的功能就是CreateInstance了,这个函数就是创建组件的相应实例.看它的实现:
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,const IID& iid,void** ppv)
{
   //...
 CA* pA = new CA;
 if(pA == NULL)
  return E_OUTOFMEMORY;
 HRESULT hr = pA->QueryInterface(iid,ppv);

 pA->Release();
 return hr;
}

 5.在这个组件的DLL中导出DllGetClassObject函数.这个函数的功能就是创建类厂的实例对象并查询接口.看其实现:
STDAPI DllGetClassObject(const CLSID& clsid,
       const IID& iid,
       void** ppv)
{
 //....
 CFactory* pFactory = new CFactory();

 if(pFactory == NULL)
  return E_OUTOFMEMORY;

 HRESULT hr = pFactory->QueryInterface(iid,ppv);
 pFactory->Release();
 return hr;
}

组件的实现差不多就这么多,下面在客户端怎么调用组件呢?这就需要用到COM函数库了,由COM函数库去查找注册表,调用组件的类厂,创建组件实例,返回接口.如下所示:
IUnknown* pUnk = NULL;
IX* iX = NULL;
CoInitialize(NULL);
CoCreateInstance(CLSID_Component1,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&pUnk);
pUnk->QueryInterface(IID_IX,(void**)&iX);
pUnk->Release();
iX->Fx();
iX->Release();
CoUninitialize();

至于客户是通过CoCreateInstance怎么获得组件的类厂,创建组件实例的.下面摘录的一篇文章很清晰的说明了这一切:
-------------------------------------------------------------------------------------
这部分我们将构造一个创建COM组件的最小框架结构,然后看一看其内部处理流程是怎样的

COM组件的运行机制,即COM是怎么跑起来的。
    IUnknown *pUnk=NULL;
    IObject *pObject=NULL;
    CoInitialize(NULL);
    CoCreateInstance(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IUnknown, (void**)&pUnk);
    pUnk->QueryInterface(IID_IOjbect, (void**)&pObject);
    pUnk->Release();
    pObject->Func();
    pObject->Release();
    CoUninitialize();
  CoCreateInstance身上,让我们来看看它内部做了一些什么事情。以下是它内部实现的一个伪代码:

    CoCreateInstance(....)
    {
      .......
      IClassFactory *pClassFactory=NULL;
      CoGetClassObject(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pClassFactory);
      pClassFactory->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk);
      pClassFactory->Release();
      ........
    }
 


  这段话的意思就是先得到类厂对象,再通过类厂创建组件从而得到IUnknown指针。
  继续深入一步,看看CoGetClassObject的内部伪码:

    CoGetClassObject(.....)
    {
      //通过查注册表CLSID_Object,得知组件DLL的位置、文件名
      //装入DLL库
      //使用函数GetProcAddress(...)得到DLL库中函数DllGetClassObject的函数指针。
      //调用DllGetClassObject
    } 


  DllGetClassObject是干什么的,它是用来获得类厂对象的。只有先得到类厂才能去创建组件.
  下面是DllGetClassObject的伪码:

   DllGetClassObject(...)
   {
      ......
      CFactory* pFactory= new CFactory; //类厂对象
      pFactory->QueryInterface(IID_IClassFactory, (void**)&pClassFactory);
      //查询IClassFactory指针
      pFactory->Release();
      ......
   }
CoGetClassObject的流程已经到此为止,现在返回CoCreateInstance,看看CreateInstance的伪码:
   CFactory::CreateInstance(.....)
   {
      ...........
      CObject *pObject = new CObject; //组件对象
      pObject->QueryInterface(IID_IUnknown, (void**)&pUnk);
      pObject->Release();
      ...........
   } 


  下图是从COM+技术内幕中COPY来的一个例图,从图中可以清楚的看到CoCreateInstance的整个流程。

COM组件的类厂(COM技术内幕笔记之四)

接下来就写下完全的源代码,说明类厂的概念:

Component实现:(FacInterFace.dll)

COM组件的类厂(COM技术内幕笔记之四)//In FACE.H
COM组件的类厂(COM技术内幕笔记之四)#ifndef _IFACE_H
COM组件的类厂(COM技术内幕笔记之四)
#define _IFACE_H
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)//
interfaces
COM组件的类厂(COM技术内幕笔记之四)
interface IX:IUnknown
#endif


组件的注册:

COM组件的类厂(COM技术内幕笔记之四)//In Register.h
COM组件的类厂(COM技术内幕笔记之四)
HRESULT RegisterServer(HMODULE hModule,               const CLSID& clsid,               const char* szFriendlyName,           const char* szVerIndProgID,           const char* szProgID);
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)HRESULT UnRegisterServer(
const CLSID& clsid,         const char* szVerIndProgID,         const char* szProgID);
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
//In Register.cpp
COM组件的类厂(COM技术内幕笔记之四)
//此文件是如何注册组件的代码实现,是把CLSID,ProgID,Version,Dll位置添加到
COM组件的类厂(COM技术内幕笔记之四)
//HKEY_CLASSES_ROOT/CLSID,HKEY_CLASSES_ROOT的子键中去.
COM组件的类厂(COM技术内幕笔记之四)
#include <objbase.h>
COM组件的类厂(COM技术内幕笔记之四)#include 
<assert.h>
COM组件的类厂(COM技术内幕笔记之四)#include 
"Register.h"
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
//set the given key and its value;
COM组件的类厂(COM技术内幕笔记之四)
BOOL setKeyAndValue(const char* pszPath,
COM组件的类厂(COM技术内幕笔记之四)                    
const char* szSubkey,
COM组件的类厂(COM技术内幕笔记之四)                    
const char* szValue);
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
//Convert a CLSID into a char string
COM组件的类厂(COM技术内幕笔记之四)
void CLSIDtochar(const CLSID& clsid,
COM组件的类厂(COM技术内幕笔记之四)                 
char* szCLSID,
COM组件的类厂(COM技术内幕笔记之四)                 
int length);
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
//Delete szKeyChild and all of its descendents
COM组件的类厂(COM技术内幕笔记之四)
LONG recursiveDeleteKey(HKEY hKeyParent,const char* szKeyChild);
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
//size of a CLSID as a string
COM组件的类厂(COM技术内幕笔记之四)
const int CLSID_STRING_SIZE = 39;
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
//Register the component in the registry
COM组件的类厂(COM技术内幕笔记之四)
HRESULT RegisterServer(HMODULE hModule,
COM组件的类厂(COM技术内幕笔记之四)                       
const CLSID& clsid,
COM组件的类厂(COM技术内幕笔记之四)                       
const char* szFriendlyName,
COM组件的类厂(COM技术内幕笔记之四)                       
const char* szVerIndProgID,
COM组件的类厂(COM技术内幕笔记之四)                       
const char* szProgID)
}

 

组件的实现:

COM组件的类厂(COM技术内幕笔记之四)//CMPNT.cpp
COM组件的类厂(COM技术内幕笔记之四)
//此文件是组件CA,组件类厂CFactory的实现,CA的实现与前面讲述的是一样的,关键在于多引入了
COM组件的类厂(COM技术内幕笔记之四)
//一个CFactory,还有一个是全局函数DllGetClassObject,另外,除了要导出DllGetClassObject之
COM组件的类厂(COM技术内幕笔记之四)
//外,还要导出三个函数,分别是DllCanUnloadNow / DllRegisterServer / DllUnregisterServer.
COM组件的类厂(COM技术内幕笔记之四)
//还有一项工作就是在DllMain中保存模块的信息.
COM组件的类厂(COM技术内幕笔记之四)
#include <iostream.h>
COM组件的类厂(COM技术内幕笔记之四)#include 
<objbase.h>
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)#include 
"..\MYIF2\IFACE.h"
COM组件的类厂(COM技术内幕笔记之四)#include 
"Register.h"
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
COM组件的类厂(COM技术内幕笔记之四)
//#ifndef EXPORTAPI 
COM组件的类厂(COM技术内幕笔记之四)
//#define EXPORTAPI extern "C" __declspec(dllexport)
COM组件的类厂(COM技术内幕笔记之四)
//#endif
COM组件的类厂(COM技术内幕笔记之四)

}



以上是组件的实现。下面是客户端的代码实现:
COM组件的类厂(COM技术内幕笔记之四)//In Client.cpp
COM组件的类厂(COM技术内幕笔记之四)
int main()
}


再在最后详述一篇,客户端调用CoCreateInstance,导致调用CoGetClassObject,CoGetClassObject通过查找注册表,得知DLL位置,文件名,然后调用DLL中DllGetClassObject,
DllGetClassObject的功能是返回CFactory的实例.
返回后,回到CoCreateInstance,通过CFactory的指针,调用
pClassFactory->CreaetInstance()创建组件实例.
这样就返回了组件实例的指针.
CoCreateInstace  -->  CoGetClassObject  --> DllGetClassObject --> Get CFactory*
                      <-------------------------------------------------------
                 -->  CFactory->CreateInstance(); --> Get IX* 
IX->Fx();

相关文章:

  • 2022-02-15
  • 2021-08-10
  • 2021-11-18
  • 2022-12-23
  • 2021-12-20
  • 2021-07-29
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-12-15
  • 2021-11-13
  • 2021-12-19
  • 2021-09-23
  • 2022-12-23
  • 2022-12-23
  • 2021-06-07
相关资源
相似解决方案