本文意旨帮助初涉COM的学者能对COM组件的创建过程有一个清晰的了解。全文以《COM技术内幕》第7章的示例代码为蓝本,稍做修改之后进行详细介绍。如果你也阅读过此书的相关内容,那么理解起来将会更容易。
《COM技术内幕》这本书的示例代码编写于1996年。时至今日,编译器发生了或多或少的变化,将本书作者编写的代码重新组织到Visual studio 2008中并成功编译,对于当时刚接触COM的我来说都略有困难,当时的我甚至不知道进程中服务器的组件需要注册才能调用(当然,现在我不需要注册也能调用),这些都是学习COM的过程中遇到的痛苦,但不是全部,因为学习的过程也是有乐趣的,那就是有所收获。
在介绍COM的创建过程之前,我想用一定的篇幅来解释一下COM到底是什么!开句玩笑,嘿嘿!好了,要正文了。
先整体,再局部,先看看我从别处扒来的图:
首先,我们来看一下客户是如何使用API函数来启动组件的创建的:
- //in client.cpp
- #include <objbase.h>
- #include <iostream>
- #include "..\Chapter7\Iface.h"//包含了诸如IX的声明
- #include "..\Chapter7\GUIDS.cpp"//包含了诸如IID_IX、CLSID_Component1的定义
- int main()
- {
- CoInitialize(NULL) ;
- IX* pIX = NULL ;
- HRESULT hr = ::CoCreateInstance(CLSID_Component1,
- NULL,
- CLSCTX_INPROC_SERVER,
- IID_IX,
- (void**)&pIX) ;
- if (SUCCEEDED(hr))
- {
- pIX->Fx() ;
- }
- CoUninitialize();
- return 0;
- }
正如你所看到的,客户通过参数CLSID_Component1和 IID_IX调用了CoCreateInstance函数来创建所需要的组件。在此你应该对CLSID_Component1和 IID_IX高度警觉,因为这两个参数是创建组件的依据。下面我开始分析CoCreateInstance的调用过程。
<一>、首先有一点必须要明白,CoCreateInstance实际上是用CoGetClassObject实现的:
- HRESULT CoCreateInstance(const CLSID& clsid,IUnknown * pUnknownOuter,DWORD dwClsContext,const IID& iid,void ** ppv)
- {
- *ppv=NULL;
- IClassFactory* pIFactory=NULL;
- HRESULT hr=CoGetClassObject(clsid,dwClsContext,NULL,IID_IClassFactory,(void**)&pIFactory);
- If(SUCCEEDED(hr))
- {
- hr=pIFactory->CreateInstance(pUnknownOuter,iid,&ppv);
- pIFactory->Release();
- }
- return hr;
- }
由上面的代码可以看到,CoCreateInstance首先调用CoGetClassObject。在此需要强调的一点是,CoGetClassObject从CoCreateInstance接受了CLSID_Component1和 IID_IX中的第一个实参(形参为clsid),即CLSID_Component1。到目前为此,先记住这一点就够了。下面我们来分析CoGetClassObject函数的调用过程。
1、CoGetClassObject首先根据形参clsid在注册表项HKEY_CLASS_ROOT\CLSID下查找与之匹配的值,如果找得到(前提是组件已经注册,注册的作用之一是在注册表中写入实现该组件的DLL保存在磁盘中的哪个位置,例如我注册的组件在注册表中的情况是这样的(如果图片中的文字看不清,可以鼠标右键另存为,再打开看):
),那么CoGetClassObject就将该DLL加载到当前进程地址空间中。
2、并调用该DLL中由程序员实现的DllGetClassObject函数:
- //in component’s DLL
- STDAPI DllGetClassObject(const CLSID& clsid,
- const IID& iid,
- void** ppv)
- {
- if (clsid != CLSID_Component1)
- {
- return CLASS_E_CLASSNOTAVAILABLE ;
- }
- CFactory* pFactory = new CFactory ;
- if (pFactory == NULL)
- {
- return E_OUTOFMEMORY ;
- }
- HRESULT hr = pFactory->QueryInterface(iid, ppv);
- pFactory->Release() ;
- /*if pFactory->QueryInterface failed,pFactory->Release() will delete itself.*/
- return hr ;
- }
由此可看到,DllGetClassObject首先对clsid进行判断,如果clsid的值表明不是组件本身的CLSID,即CLSID_Component1,就返回CLASS_E_CLASSNOTAVAILABLE。否则就创建一个类厂实例(它也由程序员在编写组件时自行实现),并通过此类厂实例指针请求由iid标识的接口,即pFactory->QueryInterface(iid, ppv)这行代码。 先等一等,还记得前面我说,CoGetClassObject从CoCreateInstance接受了CLSID_Component1和 IID_IX中的第一个实参(形参为clsid),即CLSID_Component1吗?我们先不管 IID_IX去哪里了。在此需要关心的是,这里的iid是从哪里传进来的?值是什么?答案是由CoGetClassObject传进来的,值是IID_IClassFactory。回到前面步骤<一>中CoCreateInstance函数的实现,你将会看到,iid的值确实为IID_IClassFactory。这意味着什么?pFactory->QueryInterface(iid, ppv)将只能请求IUnknown或IClassFactory接口指针,正如下面的代码所示:
- HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)
- {
- if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
- {
- *ppv = static_cast<IClassFactory*>(this) ;
- }
- else
- {
- *ppv = NULL ;
- return E_NOINTERFACE ;
- }
- reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
- return S_OK ;
- }
这没有问题,我们就是要这样子做!因为CoGetClassObject在调用DLL中的DllGetClassObject函数时,传递给它的iid值是固定的IID_IClassFactory,因此我们类厂的QueryInterface即上述的CFactory::QueryInterface函数也只需要对IID_IUnknown和IID_IClassFactory处理就行。注意,CFactory::QueryInterface函数返回之后,* ppv就保存了创建所需组件的类厂指针。至此,CoGetClassObject函数调用完成。
<二>、接下来,我们来看看前面强调但未正式提及的参数IID_IX。为了避免你反复滚鼠标滚轮,我又将CoCreateInstance的实现代码再次贴到这里,同时也因为对CoCreateInstance的实现进行分析显示太重要了:
- HRESULT CoCreateInstance(const CLSID& clsid,IUnknown * pUnknownOuter,DWORD dwClsContext,const IID& iid,void ** ppv)
- {
- *ppv=NULL;
- IClassFactory* pIFactory=NULL;
- HRESULT hr=CoGetClassObject(clsid,dwClsContext,NULL,IID_IClassFactory,(void**)&pIFactory);
- If(SUCCEEDED(hr))
- {
- hr=pIFactory->CreateInstance(pUnknownOuter,iid,&ppv);
- pIFactory->Release();
- }
- return hr;
- }
可以看到,CoGetClassObject调用完成之后,pIFactory像前面所说的那样,它指向了创建所需组件的类厂指针。随后通过这个指针调用了CreateInstance函数,即pIFactory->CreateInstance(pUnknownOuter,iid,&ppv)这行代码。这时你会发现,传递给CreateInstance函数的iid正是传给CoCreateInstance的那个iid,也就是客户代码中的实参IID_IX。而这里的CreateInstance函数是通过pIFactory来调用的,pIFactory的类型虽然是IClassFactory指针,但它实际指向的是派生类的实例,即我们的类厂CFactory。常规情况下,如果基类指针指向派生类,那么只能通过此基类指针调用派生类中的基类方法,但这里的CreateInstance是虚拟的,因此pIFactory->CreateInstance(pUnknownOuter,iid,&ppv)这行代码将执行派生类中的实现,而我们在派生类即CFactory中重写了CreateInstance:
- HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,
- const IID& iid,
- void** ppv)
- {
- if (pUnknownOuter != NULL)
- {
- return CLASS_E_NOAGGREGATION ;
- }
- /* Create an actual component even if the requested interface is not supported.
- But subsequent calling Release will delete itselt if QueryInterface failed.*/
- CA* pA = new CA ;
- if (pA == NULL)
- {
- return E_OUTOFMEMORY ;
- }
- // Get the requested interface.
- HRESULT hr = pA->QueryInterface(iid, ppv) ;
- // Release the IUnknown pointer.
- // (If QueryInterface failed, component will delete itself.)
- pA->Release() ;
- return hr ;
- }
可以看到,类厂的CreateInstance函数使用C++的new操作符创建实际的组件,并将客户代码中的接口标识符IID_IX传递给后续的QueryInterface调用,即pA->QueryInterface(iid, ppv) 这行代码。而组件本身的QueryInterface的实现也很简单,即返回它所能支持的接口指针,也就是基类指针:
- HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
- {
- if (iid == IID_IUnknown)
- {
- *ppv = static_cast<IX*>(this) ;
- }
- else if (iid == IID_IX)
- {
- *ppv = static_cast<IX*>(this) ;
- }
- else if (iid == IID_IY)
- {
- *ppv = static_cast<IY*>(this) ;
- }
- else
- {
- *ppv = NULL ;
- return E_NOINTERFACE ;
- }
- reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
- return S_OK ;
- }
而我们的类CA的定义是这样的:
- class CA : public IX,public IY
- {
- public:
- // IUnknown
- virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
- virtual ULONG __stdcall AddRef() ;
- virtual ULONG __stdcall Release() ;
- ……
- }
即CA从IX、IY派生。那么对于前面的QueryInterface,CA肯定支持IX、IY接口,因此对IID_IX调用CA::QueryInterface(const IID& iid, void** ppv)肯定是可以的。换句说,客户代码调用CoCreateInstance(CLSID_Component1,NULL, CLSCTX_INPROC_SERVER, IID_IX, (void**)&pIX) 肯定能够成功,亦即成功创建了组件。CA::QueryInterface(const IID& iid, void** ppv)返回之后,* ppv就保存了指向组件实例的指针,这对于客户代码来说,pIX就取得了所请求的接口指针。至此,CoCreateInstance的调用完毕,那么后面通过接口指针pIX调用相应的方法就水到渠成了。
以上就是组件的创建过程分析。不知是否实现了初衷,但通过本文,加深了我对组件创建过程的理解,如果你也有同感,我会倍感欣慰!
最后贴上本文的示例代码:
- //in Chapter7 vcproj
- // Iface.h -
- // Declarations of interfaces, IIDs, and CLSID
- // shared by the client and the component.
- //
- interface IX : IUnknown
- {
- virtual void pascal Fx() = 0 ;
- };
- interface IY : IUnknown
- {
- virtual void pascal Fy() = 0 ;
- };
- interface IZ : IUnknown
- {
- virtual void pascal Fz() = 0 ;
- };
- //
- // Declaration of GUIDs for interfaces and component.
- // These constants are defined in GUIDs.cpp.
- //
- extern "C" const IID IID_IX ;
- extern "C" const IID IID_IY ;
- extern "C" const IID IID_IZ ;
- extern "C" const CLSID CLSID_Component1 ;
- #ifndef __Registry_H__
- #define __Registry_H__
- //
- // Registry.h
- // - Helper functions registering and unregistering a component.
- //
- // This function will register a component in the Registry.
- // The component calls this function from its DllRegisterServer function.
- HRESULT RegisterServer(HMODULE hModule,
- const CLSID& clsid,
- const char* szFriendlyName,
- const char* szVerIndProgID,
- const char* szProgID) ;
- // This function will unregister a component. Components
- // call this function from their DllUnregisterServer function.
- HRESULT UnregisterServer(const CLSID& clsid,
- const char* szVerIndProgID,
- const char* szProgID) ;
- #endif
- //
- // Cmpnt.cpp
- //
- #include <iostream>
- #include <objbase.h>
- using std::cout;
- using std::endl;
- #include "Iface.h" // Interface declarations
- #include "Registry.h" // Registry helper functions
- void trace(const char* strMsg)
- {
- cout << strMsg << endl;
- }
- ///
- //
- // Global variables
- //
- static HMODULE g_hModule = NULL ; // DLL module handle
- static long g_cComponents = 0 ; // Count of active components
- static long g_cServerLocks = 0 ; // Count of locks
- // Friendly name of component
- const char g_szFriendlyName[] = "Inside COM, Chapter 7 Example" ;
- // Version-independent ProgID
- const char g_szVerIndProgID[] = "InsideCOM.Chap07" ;
- // ProgID
- const char g_szProgID[] = "InsideCOM.Chap07.1" ;
- ///
- //
- // Component
- //
- class CA : public IX,
- public IY
- {
- public:
- // IUnknown
- virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
- virtual ULONG __stdcall AddRef() ;
- virtual ULONG __stdcall Release() ;
- // Interface IX
- virtual void __stdcall Fx() { cout << "Fx" << endl ;}
- // Interface IY
- virtual void __stdcall Fy() { cout << "Fy" << endl ;}
- // Constructor
- CA() ;
- // Destructor
- ~CA() ;
- private:
- // Reference count
- long m_cRef ;
- } ;
- //
- // Constructor
- //
- CA::CA() : m_cRef(1)
- {
- InterlockedIncrement(&g_cComponents) ;
- }
- //
- // Destructor
- //
- CA::~CA()
- {
- InterlockedDecrement(&g_cComponents) ;
- trace("Component:\t\tDestroy self.") ;
- }
- //
- // IUnknown implementation
- //
- HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
- {
- if (iid == IID_IUnknown)
- {
- *ppv = static_cast<IX*>(this) ;
- }
- else if (iid == IID_IX)
- {
- *ppv = static_cast<IX*>(this) ;
- trace("Component:\t\tReturn pointer to IX.") ;
- }
- else if (iid == IID_IY)
- {
- *ppv = static_cast<IY*>(this) ;
- trace("Component:\t\tReturn pointer to IY.") ;
- }
- else
- {
- *ppv = NULL ;
- return E_NOINTERFACE ;
- }
- reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
- return S_OK ;
- }
- ULONG __stdcall CA::AddRef()
- {
- return InterlockedIncrement(&m_cRef) ;
- }
- ULONG __stdcall CA::Release()
- {
- if (InterlockedDecrement(&m_cRef) == 0)
- {
- delete this ;
- return 0 ;
- }
- return m_cRef ;
- }
- ///
- //
- // Class factory
- //
- class CFactory : public IClassFactory
- {
- public:
- // IUnknown
- virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
- virtual ULONG __stdcall AddRef() ;
- virtual ULONG __stdcall Release() ;
- // Interface IClassFactory
- virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter,
- const IID& iid,
- void** ppv) ;
- virtual HRESULT __stdcall LockServer(BOOL bLock) ;
- // Constructor
- CFactory() : m_cRef(1) {}
- // Destructor
- ~CFactory() { trace("Class factory:\t\tDestroy self.") ;}
- private:
- long m_cRef ;
- } ;
- //
- // Class factory IUnknown implementation
- //
- HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)
- {
- if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
- {
- *ppv = static_cast<IClassFactory*>(this) ;
- }
- else
- {
- *ppv = NULL ;
- return E_NOINTERFACE ;
- }
- reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
- return S_OK ;
- }
- ULONG __stdcall CFactory::AddRef()
- {
- return InterlockedIncrement(&m_cRef) ;
- }
- ULONG __stdcall CFactory::Release()
- {
- if (InterlockedDecrement(&m_cRef) == 0)
- {
- delete this ;
- return 0 ;
- }
- return m_cRef ;
- }
- //
- // IClassFactory implementation
- //
- HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,
- const IID& iid,
- void** ppv)
- {
- trace("Class factory:\t\tCreate component.") ;
- // Cannot aggregate.
- if (pUnknownOuter != NULL)
- {
- return CLASS_E_NOAGGREGATION ;
- }
- /* Create an actual component even if the requested interface is not supported.
- But subsequent calling Release will delete itselt if QueryInterface failed.*/
- CA* pA = new CA ;
- if (pA == NULL)
- {
- return E_OUTOFMEMORY ;
- }
- // Get the requested interface.
- HRESULT hr = pA->QueryInterface(iid, ppv) ;
- // Release the IUnknown pointer.
- // (If QueryInterface failed, component will delete itself.)
- pA->Release() ;
- return hr ;
- }
- // LockServer
- HRESULT __stdcall CFactory::LockServer(BOOL bLock)
- {
- if (bLock)
- {
- InterlockedIncrement(&g_cServerLocks) ;
- }
- else
- {
- InterlockedDecrement(&g_cServerLocks) ;
- }
- return S_OK ;
- }
- ///
- //
- // Exported functions
- //
- //
- // Can DLL unload now?
- //
- STDAPI DllCanUnloadNow()
- {
- if ((g_cComponents == 0) && (g_cServerLocks == 0))
- {
- return S_OK ;
- }
- else
- {
- return S_FALSE ;
- }
- }
- //
- // Get class factory
- //
- STDAPI DllGetClassObject(const CLSID& clsid,
- const IID& iid,
- void** ppv)
- {
- trace("DllGetClassObject:\tCreate class factory.") ;
- // Can we create this component?
- if (clsid != CLSID_Component1)
- {
- return CLASS_E_CLASSNOTAVAILABLE ;
- }
- // Create class factory.
- /* set CFactory::m_cRef's value to 1 in constructor.*/
- CFactory* pFactory = new CFactory ;
- if (pFactory == NULL)
- {
- return E_OUTOFMEMORY ;
- }
- // Get requested interface.
- HRESULT hr = pFactory->QueryInterface(iid, ppv);
- /*call CFactory::AddRef() if requested interface supported,or ppv's value will be set to null.*/
- pFactory->Release() ;
- /*if pFactory->QueryInterface failed,pFactory->Release() will delete itself.*/
- return hr ;
- }
- //
- // Server registration
- //
- STDAPI DllRegisterServer()
- {
- return RegisterServer(g_hModule,
- CLSID_Component1,
- g_szFriendlyName,
- g_szVerIndProgID,
- g_szProgID) ;
- }
- //
- // Server unregistration
- //
- STDAPI DllUnregisterServer()
- {
- return UnregisterServer(CLSID_Component1,
- g_szVerIndProgID,
- g_szProgID) ;
- }
- ///
- //
- // DLL module information
- //
- BOOL APIENTRY DllMain(HMODULE hModule,
- DWORD dwReason,
- void* lpReserved)
- {
- if (dwReason == DLL_PROCESS_ATTACH)
- {
- g_hModule = hModule ;
- }
- return TRUE ;
- }
- // in CMPNT.def
- LIBRARY "Chapter7"
- EXPORTS
- DllGetClassObject PRIVATE
- DllCanUnloadNow PRIVATE
- DllRegisterServer PRIVATE
- DllUnregisterServer PRIVATE
- //
- // GUIDs.cpp
- // - Defines all IIDs and CLSIDs for the client and the component.
- // The declaration of these GUIDs is in Iface.h
- //
- #include <objbase.h>
- // {32bb8320-b41b-11cf-a6bb-0080c7b2d682}
- extern "C" const IID IID_IX =
- {0x32bb8320, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
- // {32bb8321-b41b-11cf-a6bb-0080c7b2d682}
- extern "C" const IID IID_IY =
- {0x32bb8321, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
- // {32bb8322-b41b-11cf-a6bb-0080c7b2d682}
- extern "C" const IID IID_IZ =
- {0x32bb8322, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
- // {0c092c21-882c-11cf-a6bb-0080c7b2d682}
- extern "C" const CLSID CLSID_Component1 =
- {0x0c092c21, 0x882c, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
- //
- // Registry.cpp
- //
- #include <objbase.h>
- #include <assert.h>
- #include "Registry.h"
- //
- // Internal helper functions prototypes
- //
- // Set the given key and its value.
- BOOL setKeyAndValue(const char* pszPath,
- const char* szSubkey,
- const char* szValue) ;
- // Convert a CLSID into a char string.
- void CLSIDtochar(const CLSID& clsid,
- char* szCLSID,
- int length) ;
- // Delete szKeyChild and all of its descendents.
- LONG recursiveDeleteKey(HKEY hKeyParent, const char* szKeyChild) ;
- //
- // Constants
- //
- // Size of a CLSID as a string
- const int CLSID_STRING_SIZE = 39 ;
- /
- //
- // Public function implementation
- //
- //
- // Register the component in the registry.
- //
- HRESULT RegisterServer(HMODULE hModule, // DLL module handle
- const CLSID& clsid, // Class ID
- const char* szFriendlyName, // Friendly Name
- const char* szVerIndProgID, // Programmatic
- const char* szProgID) // IDs
- {
- // Get server location.
- char szModule[512] ;
- DWORD dwResult =
- ::GetModuleFileName(hModule,
- szModule,
- sizeof(szModule)/sizeof(char)) ;
- assert(dwResult != 0) ;
- // Convert the CLSID into a char.
- char szCLSID[CLSID_STRING_SIZE] ;
- CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;
- // Build the key CLSID\\{...}
- char szKey[64] ;
- strcpy(szKey, "CLSID\\") ;
- strcat(szKey, szCLSID) ;
- // Add the CLSID to the registry.
- setKeyAndValue(szKey, NULL, szFriendlyName) ;
- // Add the server filename subkey under the CLSID key.
- setKeyAndValue(szKey, "InprocServer32", szModule) ;
- // Add the ProgID subkey under the CLSID key.
- setKeyAndValue(szKey, "ProgID", szProgID) ;
- // Add the version-independent ProgID subkey under CLSID key.
- setKeyAndValue(szKey, "VersionIndependentProgID",
- szVerIndProgID) ;
- // Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT.
- setKeyAndValue(szVerIndProgID, NULL, szFriendlyName) ;
- setKeyAndValue(szVerIndProgID, "CLSID", szCLSID) ;
- setKeyAndValue(szVerIndProgID, "CurVer", szProgID) ;
- // Add the versioned ProgID subkey under HKEY_CLASSES_ROOT.
- setKeyAndValue(szProgID, NULL, szFriendlyName) ;
- setKeyAndValue(szProgID, "CLSID", szCLSID) ;
- return S_OK ;
- }
- //
- // Remove the component from the registry.
- //
- LONG UnregisterServer(const CLSID& clsid, // Class ID
- const char* szVerIndProgID, // Programmatic
- const char* szProgID) // IDs
- {
- // Convert the CLSID into a char.
- char szCLSID[CLSID_STRING_SIZE] ;
- CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;
- // Build the key CLSID\\{...}
- char szKey[64] ;
- strcpy(szKey, "CLSID\\") ;
- strcat(szKey, szCLSID) ;
- // Delete the CLSID Key - CLSID\{...}
- LONG lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szKey) ;
- assert((lResult == ERROR_SUCCESS) ||
- (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.
- // Delete the version-independent ProgID Key.
- lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szVerIndProgID) ;
- assert((lResult == ERROR_SUCCESS) ||
- (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.
- // Delete the ProgID key.
- lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szProgID) ;
- assert((lResult == ERROR_SUCCESS) ||
- (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.
- return S_OK ;
- }
- ///
- //
- // Internal helper functions
- //
- // Convert a CLSID to a char string.
- void CLSIDtochar(const CLSID& clsid,
- char* szCLSID,
- int length)
- {
- assert(length >= CLSID_STRING_SIZE) ;
- // Get CLSID
- LPOLESTR wszCLSID = NULL ;
- HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ;
- assert(SUCCEEDED(hr)) ;
- // Covert from wide characters to non-wide.
- wcstombs(szCLSID, wszCLSID, length) ;
- // Free memory.
- CoTaskMemFree(wszCLSID) ;
- }
- //
- // Delete a key and all of its descendents.
- //
- LONG recursiveDeleteKey(HKEY hKeyParent, // Parent of key to delete
- const char* lpszKeyChild) // Key to delete
- {
- // Open the child.
- HKEY hKeyChild ;
- LONG lRes = RegOpenKeyEx(hKeyParent, lpszKeyChild, 0,
- KEY_ALL_ACCESS, &hKeyChild) ;
- if (lRes != ERROR_SUCCESS)
- {
- return lRes ;
- }
- // Enumerate all of the decendents of this child.
- FILETIME time ;
- char szBuffer[256] ;
- DWORD dwSize = 256 ;
- while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL,
- NULL, NULL, &time) == S_OK)
- {
- // Delete the decendents of this child.
- lRes = recursiveDeleteKey(hKeyChild, szBuffer) ;
- if (lRes != ERROR_SUCCESS)
- {
- // Cleanup before exiting.
- RegCloseKey(hKeyChild) ;
- return lRes;
- }
- dwSize = 256 ;
- }
- // Close the child.
- RegCloseKey(hKeyChild) ;
- // Delete this child.
- return RegDeleteKey(hKeyParent, lpszKeyChild) ;
- }
- //
- // Create a key and set its value.
- // - This helper function was borrowed and modifed from
- // Kraig Brockschmidt's book Inside OLE.
- //
- BOOL setKeyAndValue(const char* szKey,
- const char* szSubkey,
- const char* szValue)
- {
- HKEY hKey;
- char szKeyBuf[1024] ;
- // Copy keyname into buffer.
- strcpy(szKeyBuf, szKey) ;
- // Add subkey name to buffer.
- if (szSubkey != NULL)
- {
- strcat(szKeyBuf, "\\") ;
- strcat(szKeyBuf, szSubkey ) ;
- }
- // Create and open key and subkey.
- long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT ,
- szKeyBuf,
- 0, NULL, REG_OPTION_NON_VOLATILE,
- KEY_ALL_ACCESS, NULL,
- &hKey, NULL) ;
- if (lResult != ERROR_SUCCESS)
- {
- return FALSE ;
- }
- // Set the Value.
- if (szValue != NULL)
- {
- RegSetValueEx(hKey, NULL, 0, REG_SZ,
- (BYTE *)szValue,
- strlen(szValue)+1) ;
- }
- RegCloseKey(hKey) ;
- return TRUE ;
- }
- //in UseCmpnt vcproj
- //client.cpp
- #include <iostream>
- using std::cout;
- using std::endl;
- #include <objbase.h>
- #include "..\Chapter7\Iface.h"//包含了诸如IX、IY的声明
- #include "..\Chapter7\GUIDS.cpp"//包含了诸如IID_IX、CLSID_Component1的定义
- void OutPut(const char* msg);
- {
- cout << "Client: \t\t" << msg << endl ;
- }
- int main()
- {
- CoInitialize(NULL) ;
- OutPut("Call CoCreateInstance to create") ;
- OutPut(" component and get interface IX.") ;
- IX* pIX = NULL ;
- HRESULT hr = ::CoCreateInstance(CLSID_Component1,
- NULL,
- CLSCTX_INPROC_SERVER,
- IID_IX,
- (void**)&pIX) ;
- if (SUCCEEDED(hr))
- {
- OutPut("Succeeded getting IX.") ;
- pIX->Fx() ; // Use interface IX.
- OutPut("Ask for interface IY.") ;
- IY* pIY = NULL ;
- hr = pIX->QueryInterface(IID_IY, (void**)&pIY) ;
- if (SUCCEEDED(hr))
- {
- OutPut("Succeeded getting IY.") ;
- pIY->Fy() ; // Use interface IY.
- pIY->Release() ;
- OutPut("Release IY interface.") ;
- }
- else
- {
- OutPut("Could not get interface IY.") ;
- }
- OutPut("Ask for interface IZ.") ;
- IZ* pIZ = NULL ;
- hr = pIX->QueryInterface(IID_IZ, (void**)&pIZ) ;
- if (SUCCEEDED(hr))
- {
- OutPut("Succeeded in getting interface IZ.") ;
- pIZ->Fz() ;
- pIZ->Release() ;
- OutPut("Release IZ interface.") ;
- }
- else
- {
- OutPut("Could not get interface IZ.") ;
- }
- OutPut("Release IX interface.") ;
- pIX->Release() ;
- }
- else
- {
- cout << "Client: \t\tCould not create component. hr = "
- << hex << hr << endl ;
- }
- // Uninitialize COM Library
- CoUninitialize();
- return 0;
- }
版权所有,翻版不究,欢迎山寨!
本文意旨帮助初涉COM的学者能对COM组件的创建过程有一个清晰的了解。全文以《COM技术内幕》第7章的示例代码为蓝本,稍做修改之后进行详细介绍。如果你也阅读过此书的相关内容,那么理解起来将会更容易。
《COM技术内幕》这本书的示例代码编写于1996年。时至今日,编译器发生了或多或少的变化,将本书作者编写的代码重新组织到Visual studio 2008中并成功编译,对于当时刚接触COM的我来说都略有困难,当时的我甚至不知道进程中服务器的组件需要注册才能调用(当然,现在我不需要注册也能调用),这些都是学习COM的过程中遇到的痛苦,但不是全部,因为学习的过程也是有乐趣的,那就是有所收获。
在介绍COM的创建过程之前,我想用一定的篇幅来解释一下COM到底是什么!开句玩笑,嘿嘿!好了,要正文了。
先整体,再局部,先看看我从别处扒来的图:
首先,我们来看一下客户是如何使用API函数来启动组件的创建的:
- //in client.cpp
- #include <objbase.h>
- #include <iostream>
- #include "..\Chapter7\Iface.h"//包含了诸如IX的声明
- #include "..\Chapter7\GUIDS.cpp"//包含了诸如IID_IX、CLSID_Component1的定义
- int main()
- {
- CoInitialize(NULL) ;
- IX* pIX = NULL ;
- HRESULT hr = ::CoCreateInstance(CLSID_Component1,
- NULL,
- CLSCTX_INPROC_SERVER,
- IID_IX,
- (void**)&pIX) ;
- if (SUCCEEDED(hr))
- {
- pIX->Fx() ;
- }
- CoUninitialize();
- return 0;
- }
正如你所看到的,客户通过参数CLSID_Component1和 IID_IX调用了CoCreateInstance函数来创建所需要的组件。在此你应该对CLSID_Component1和 IID_IX高度警觉,因为这两个参数是创建组件的依据。下面我开始分析CoCreateInstance的调用过程。
<一>、首先有一点必须要明白,CoCreateInstance实际上是用CoGetClassObject实现的:
- HRESULT CoCreateInstance(const CLSID& clsid,IUnknown * pUnknownOuter,DWORD dwClsContext,const IID& iid,void ** ppv)
- {
- *ppv=NULL;
- IClassFactory* pIFactory=NULL;
- HRESULT hr=CoGetClassObject(clsid,dwClsContext,NULL,IID_IClassFactory,(void**)&pIFactory);
- If(SUCCEEDED(hr))
- {
- hr=pIFactory->CreateInstance(pUnknownOuter,iid,&ppv);
- pIFactory->Release();
- }
- return hr;
- }
由上面的代码可以看到,CoCreateInstance首先调用CoGetClassObject。在此需要强调的一点是,CoGetClassObject从CoCreateInstance接受了CLSID_Component1和 IID_IX中的第一个实参(形参为clsid),即CLSID_Component1。到目前为此,先记住这一点就够了。下面我们来分析CoGetClassObject函数的调用过程。
1、CoGetClassObject首先根据形参clsid在注册表项HKEY_CLASS_ROOT\CLSID下查找与之匹配的值,如果找得到(前提是组件已经注册,注册的作用之一是在注册表中写入实现该组件的DLL保存在磁盘中的哪个位置,例如我注册的组件在注册表中的情况是这样的(如果图片中的文字看不清,可以鼠标右键另存为,再打开看):
),那么CoGetClassObject就将该DLL加载到当前进程地址空间中。
2、并调用该DLL中由程序员实现的DllGetClassObject函数:
- //in component’s DLL
- STDAPI DllGetClassObject(const CLSID& clsid,
- const IID& iid,
- void** ppv)
- {
- if (clsid != CLSID_Component1)
- {
- return CLASS_E_CLASSNOTAVAILABLE ;
- }
- CFactory* pFactory = new CFactory ;
- if (pFactory == NULL)
- {
- return E_OUTOFMEMORY ;
- }
- HRESULT hr = pFactory->QueryInterface(iid, ppv);
- pFactory->Release() ;
- /*if pFactory->QueryInterface failed,pFactory->Release() will delete itself.*/
- return hr ;
- }
由此可看到,DllGetClassObject首先对clsid进行判断,如果clsid的值表明不是组件本身的CLSID,即CLSID_Component1,就返回CLASS_E_CLASSNOTAVAILABLE。否则就创建一个类厂实例(它也由程序员在编写组件时自行实现),并通过此类厂实例指针请求由iid标识的接口,即pFactory->QueryInterface(iid, ppv)这行代码。 先等一等,还记得前面我说,CoGetClassObject从CoCreateInstance接受了CLSID_Component1和 IID_IX中的第一个实参(形参为clsid),即CLSID_Component1吗?我们先不管 IID_IX去哪里了。在此需要关心的是,这里的iid是从哪里传进来的?值是什么?答案是由CoGetClassObject传进来的,值是IID_IClassFactory。回到前面步骤<一>中CoCreateInstance函数的实现,你将会看到,iid的值确实为IID_IClassFactory。这意味着什么?pFactory->QueryInterface(iid, ppv)将只能请求IUnknown或IClassFactory接口指针,正如下面的代码所示:
- HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)
- {
- if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
- {
- *ppv = static_cast<IClassFactory*>(this) ;
- }
- else
- {
- *ppv = NULL ;
- return E_NOINTERFACE ;
- }
- reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
- return S_OK ;
- }
这没有问题,我们就是要这样子做!因为CoGetClassObject在调用DLL中的DllGetClassObject函数时,传递给它的iid值是固定的IID_IClassFactory,因此我们类厂的QueryInterface即上述的CFactory::QueryInterface函数也只需要对IID_IUnknown和IID_IClassFactory处理就行。注意,CFactory::QueryInterface函数返回之后,* ppv就保存了创建所需组件的类厂指针。至此,CoGetClassObject函数调用完成。
<二>、接下来,我们来看看前面强调但未正式提及的参数IID_IX。为了避免你反复滚鼠标滚轮,我又将CoCreateInstance的实现代码再次贴到这里,同时也因为对CoCreateInstance的实现进行分析显示太重要了:
- HRESULT CoCreateInstance(const CLSID& clsid,IUnknown * pUnknownOuter,DWORD dwClsContext,const IID& iid,void ** ppv)
- {
- *ppv=NULL;
- IClassFactory* pIFactory=NULL;
- HRESULT hr=CoGetClassObject(clsid,dwClsContext,NULL,IID_IClassFactory,(void**)&pIFactory);
- If(SUCCEEDED(hr))
- {
- hr=pIFactory->CreateInstance(pUnknownOuter,iid,&ppv);
- pIFactory->Release();
- }
- return hr;
- }
可以看到,CoGetClassObject调用完成之后,pIFactory像前面所说的那样,它指向了创建所需组件的类厂指针。随后通过这个指针调用了CreateInstance函数,即pIFactory->CreateInstance(pUnknownOuter,iid,&ppv)这行代码。这时你会发现,传递给CreateInstance函数的iid正是传给CoCreateInstance的那个iid,也就是客户代码中的实参IID_IX。而这里的CreateInstance函数是通过pIFactory来调用的,pIFactory的类型虽然是IClassFactory指针,但它实际指向的是派生类的实例,即我们的类厂CFactory。常规情况下,如果基类指针指向派生类,那么只能通过此基类指针调用派生类中的基类方法,但这里的CreateInstance是虚拟的,因此pIFactory->CreateInstance(pUnknownOuter,iid,&ppv)这行代码将执行派生类中的实现,而我们在派生类即CFactory中重写了CreateInstance:
- HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,
- const IID& iid,
- void** ppv)
- {
- if (pUnknownOuter != NULL)
- {
- return CLASS_E_NOAGGREGATION ;
- }
- /* Create an actual component even if the requested interface is not supported.
- But subsequent calling Release will delete itselt if QueryInterface failed.*/
- CA* pA = new CA ;
- if (pA == NULL)
- {
- return E_OUTOFMEMORY ;
- }
- // Get the requested interface.
- HRESULT hr = pA->QueryInterface(iid, ppv) ;
- // Release the IUnknown pointer.
- // (If QueryInterface failed, component will delete itself.)
- pA->Release() ;
- return hr ;
- }
可以看到,类厂的CreateInstance函数使用C++的new操作符创建实际的组件,并将客户代码中的接口标识符IID_IX传递给后续的QueryInterface调用,即pA->QueryInterface(iid, ppv) 这行代码。而组件本身的QueryInterface的实现也很简单,即返回它所能支持的接口指针,也就是基类指针:
- HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
- {
- if (iid == IID_IUnknown)
- {
- *ppv = static_cast<IX*>(this) ;
- }
- else if (iid == IID_IX)
- {
- *ppv = static_cast<IX*>(this) ;
- }
- else if (iid == IID_IY)
- {
- *ppv = static_cast<IY*>(this) ;
- }
- else
- {
- *ppv = NULL ;
- return E_NOINTERFACE ;
- }
- reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
- return S_OK ;
- }
而我们的类CA的定义是这样的:
- class CA : public IX,public IY
- {
- public:
- // IUnknown
- virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
- virtual ULONG __stdcall AddRef() ;
- virtual ULONG __stdcall Release() ;
- ……
- }
即CA从IX、IY派生。那么对于前面的QueryInterface,CA肯定支持IX、IY接口,因此对IID_IX调用CA::QueryInterface(const IID& iid, void** ppv)肯定是可以的。换句说,客户代码调用CoCreateInstance(CLSID_Component1,NULL, CLSCTX_INPROC_SERVER, IID_IX, (void**)&pIX) 肯定能够成功,亦即成功创建了组件。CA::QueryInterface(const IID& iid, void** ppv)返回之后,* ppv就保存了指向组件实例的指针,这对于客户代码来说,pIX就取得了所请求的接口指针。至此,CoCreateInstance的调用完毕,那么后面通过接口指针pIX调用相应的方法就水到渠成了。
以上就是组件的创建过程分析。不知是否实现了初衷,但通过本文,加深了我对组件创建过程的理解,如果你也有同感,我会倍感欣慰!
最后贴上本文的示例代码:
- //in Chapter7 vcproj
- // Iface.h -
- // Declarations of interfaces, IIDs, and CLSID
- // shared by the client and the component.
- //
- interface IX : IUnknown
- {
- virtual void pascal Fx() = 0 ;
- };
- interface IY : IUnknown
- {
- virtual void pascal Fy() = 0 ;
- };
- interface IZ : IUnknown
- {
- virtual void pascal Fz() = 0 ;
- };
- //
- // Declaration of GUIDs for interfaces and component.
- // These constants are defined in GUIDs.cpp.
- //
- extern "C" const IID IID_IX ;
- extern "C" const IID IID_IY ;
- extern "C" const IID IID_IZ ;
- extern "C" const CLSID CLSID_Component1 ;
- #ifndef __Registry_H__
- #define __Registry_H__
- //
- // Registry.h
- // - Helper functions registering and unregistering a component.
- //
- // This function will register a component in the Registry.
- // The component calls this function from its DllRegisterServer function.
- HRESULT RegisterServer(HMODULE hModule,
- const CLSID& clsid,
- const char* szFriendlyName,
- const char* szVerIndProgID,
- const char* szProgID) ;
- // This function will unregister a component. Components
- // call this function from their DllUnregisterServer function.
- HRESULT UnregisterServer(const CLSID& clsid,
- const char* szVerIndProgID,
- const char* szProgID) ;
- #endif
- //
- // Cmpnt.cpp
- //
- #include <iostream>
- #include <objbase.h>
- using std::cout;
- using std::endl;
- #include "Iface.h" // Interface declarations
- #include "Registry.h" // Registry helper functions
- void trace(const char* strMsg)
- {
- cout << strMsg << endl;
- }
- ///
- //
- // Global variables
- //
- static HMODULE g_hModule = NULL ; // DLL module handle
- static long g_cComponents = 0 ; // Count of active components
- static long g_cServerLocks = 0 ; // Count of locks
- // Friendly name of component
- const char g_szFriendlyName[] = "Inside COM, Chapter 7 Example" ;
- // Version-independent ProgID
- const char g_szVerIndProgID[] = "InsideCOM.Chap07" ;
- // ProgID
- const char g_szProgID[] = "InsideCOM.Chap07.1" ;
- ///
- //
- // Component
- //
- class CA : public IX,
- public IY
- {
- public:
- // IUnknown
- virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
- virtual ULONG __stdcall AddRef() ;
- virtual ULONG __stdcall Release() ;
- // Interface IX
- virtual void __stdcall Fx() { cout << "Fx" << endl ;}
- // Interface IY
- virtual void __stdcall Fy() { cout << "Fy" << endl ;}
- // Constructor
- CA() ;
- // Destructor
- ~CA() ;
- private:
- // Reference count
- long m_cRef ;
- } ;
- //
- // Constructor
- //
- CA::CA() : m_cRef(1)
- {
- InterlockedIncrement(&g_cComponents) ;
- }
- //
- // Destructor
- //
- CA::~CA()
- {
- InterlockedDecrement(&g_cComponents) ;
- trace("Component:\t\tDestroy self.") ;
- }
- //
- // IUnknown implementation
- //
- HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
- {
- if (iid == IID_IUnknown)
- {
- *ppv = static_cast<IX*>(this) ;
- }
- else if (iid == IID_IX)
- {
- *ppv = static_cast<IX*>(this) ;
- trace("Component:\t\tReturn pointer to IX.") ;
- }
- else if (iid == IID_IY)
- {
- *ppv = static_cast<IY*>(this) ;
- trace("Component:\t\tReturn pointer to IY.") ;
- }
- else
- {
- *ppv = NULL ;
- return E_NOINTERFACE ;
- }
- reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
- return S_OK ;
- }
- ULONG __stdcall CA::AddRef()
- {
- return InterlockedIncrement(&m_cRef) ;
- }
- ULONG __stdcall CA::Release()
- {
- if (InterlockedDecrement(&m_cRef) == 0)
- {
- delete this ;
- return 0 ;
- }
- return m_cRef ;
- }
- ///
- //
- // Class factory
- //
- class CFactory : public IClassFactory
- {
- public:
- // IUnknown
- virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;
- virtual ULONG __stdcall AddRef() ;
- virtual ULONG __stdcall Release() ;
- // Interface IClassFactory
- virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter,
- const IID& iid,
- void** ppv) ;
- virtual HRESULT __stdcall LockServer(BOOL bLock) ;
- // Constructor
- CFactory() : m_cRef(1) {}
- // Destructor
- ~CFactory() { trace("Class factory:\t\tDestroy self.") ;}
- private:
- long m_cRef ;
- } ;
- //
- // Class factory IUnknown implementation
- //
- HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)
- {
- if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
- {
- *ppv = static_cast<IClassFactory*>(this) ;
- }
- else
- {
- *ppv = NULL ;
- return E_NOINTERFACE ;
- }
- reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
- return S_OK ;
- }
- ULONG __stdcall CFactory::AddRef()
- {
- return InterlockedIncrement(&m_cRef) ;
- }
- ULONG __stdcall CFactory::Release()
- {
- if (InterlockedDecrement(&m_cRef) == 0)
- {
- delete this ;
- return 0 ;
- }
- return m_cRef ;
- }
- //
- // IClassFactory implementation
- //
- HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,
- const IID& iid,
- void** ppv)
- {
- trace("Class factory:\t\tCreate component.") ;
- // Cannot aggregate.
- if (pUnknownOuter != NULL)
- {
- return CLASS_E_NOAGGREGATION ;
- }
- /* Create an actual component even if the requested interface is not supported.
- But subsequent calling Release will delete itselt if QueryInterface failed.*/
- CA* pA = new CA ;
- if (pA == NULL)
- {
- return E_OUTOFMEMORY ;
- }
- // Get the requested interface.
- HRESULT hr = pA->QueryInterface(iid, ppv) ;
- // Release the IUnknown pointer.
- // (If QueryInterface failed, component will delete itself.)
- pA->Release() ;
- return hr ;
- }
- // LockServer
- HRESULT __stdcall CFactory::LockServer(BOOL bLock)
- {
- if (bLock)
- {
- InterlockedIncrement(&g_cServerLocks) ;
- }
- else
- {
- InterlockedDecrement(&g_cServerLocks) ;
- }
- return S_OK ;
- }
- ///
- //
- // Exported functions
- //
- //
- // Can DLL unload now?
- //
- STDAPI DllCanUnloadNow()
- {
- if ((g_cComponents == 0) && (g_cServerLocks == 0))
- {
- return S_OK ;
- }
- else
- {
- return S_FALSE ;
- }
- }
- //
- // Get class factory
- //
- STDAPI DllGetClassObject(const CLSID& clsid,
- const IID& iid,
- void** ppv)
- {
- trace("DllGetClassObject:\tCreate class factory.") ;
- // Can we create this component?
- if (clsid != CLSID_Component1)
- {
- return CLASS_E_CLASSNOTAVAILABLE ;
- }
- // Create class factory.
- /* set CFactory::m_cRef's value to 1 in constructor.*/
- CFactory* pFactory = new CFactory ;
- if (pFactory == NULL)
- {
- return E_OUTOFMEMORY ;
- }
- // Get requested interface.
- HRESULT hr = pFactory->QueryInterface(iid, ppv);
- /*call CFactory::AddRef() if requested interface supported,or ppv's value will be set to null.*/
- pFactory->Release() ;
- /*if pFactory->QueryInterface failed,pFactory->Release() will delete itself.*/
- return hr ;
- }
- //
- // Server registration
- //
- STDAPI DllRegisterServer()
- {
- return RegisterServer(g_hModule,
- CLSID_Component1,
- g_szFriendlyName,
- g_szVerIndProgID,
- g_szProgID) ;
- }
- //
- // Server unregistration
- //
- STDAPI DllUnregisterServer()
- {
- return UnregisterServer(CLSID_Component1,
- g_szVerIndProgID,
- g_szProgID) ;
- }
- ///
- //
- // DLL module information
- //
- BOOL APIENTRY DllMain(HMODULE hModule,
- DWORD dwReason,
- void* lpReserved)
- {
- if (dwReason == DLL_PROCESS_ATTACH)
- {
- g_hModule = hModule ;
- }
- return TRUE ;
- }
- // in CMPNT.def
- LIBRARY "Chapter7"
- EXPORTS
- DllGetClassObject PRIVATE
- DllCanUnloadNow PRIVATE
- DllRegisterServer PRIVATE
- DllUnregisterServer PRIVATE
- //
- // GUIDs.cpp
- // - Defines all IIDs and CLSIDs for the client and the component.
- // The declaration of these GUIDs is in Iface.h
- //
- #include <objbase.h>
- // {32bb8320-b41b-11cf-a6bb-0080c7b2d682}
- extern "C" const IID IID_IX =
- {0x32bb8320, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
- // {32bb8321-b41b-11cf-a6bb-0080c7b2d682}
- extern "C" const IID IID_IY =
- {0x32bb8321, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
- // {32bb8322-b41b-11cf-a6bb-0080c7b2d682}
- extern "C" const IID IID_IZ =
- {0x32bb8322, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
- // {0c092c21-882c-11cf-a6bb-0080c7b2d682}
- extern "C" const CLSID CLSID_Component1 =
- {0x0c092c21, 0x882c, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
- //
- // Registry.cpp
- //
- #include <objbase.h>
- #include <assert.h>
- #include "Registry.h"
- //
- // Internal helper functions prototypes
- //
- // Set the given key and its value.
- BOOL setKeyAndValue(const char* pszPath,
- const char* szSubkey,
- const char* szValue) ;
- // Convert a CLSID into a char string.
- void CLSIDtochar(const CLSID& clsid,
- char* szCLSID,
- int length) ;
- // Delete szKeyChild and all of its descendents.
- LONG recursiveDeleteKey(HKEY hKeyParent, const char* szKeyChild) ;
- //
- // Constants
- //
- // Size of a CLSID as a string
- const int CLSID_STRING_SIZE = 39 ;
- /
- //
- // Public function implementation
- //
- //
- // Register the component in the registry.
- //
- HRESULT RegisterServer(HMODULE hModule, // DLL module handle
- const CLSID& clsid, // Class ID
- const char* szFriendlyName, // Friendly Name
- const char* szVerIndProgID, // Programmatic
- const char* szProgID) // IDs
- {
- // Get server location.
- char szModule[512] ;
- DWORD dwResult =
- ::GetModuleFileName(hModule,
- szModule,
- sizeof(szModule)/sizeof(char)) ;
- assert(dwResult != 0) ;
- // Convert the CLSID into a char.
- char szCLSID[CLSID_STRING_SIZE] ;
- CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;
- // Build the key CLSID\\{...}
- char szKey[64] ;
- strcpy(szKey, "CLSID\\") ;
- strcat(szKey, szCLSID) ;
- // Add the CLSID to the registry.
- setKeyAndValue(szKey, NULL, szFriendlyName) ;
- // Add the server filename subkey under the CLSID key.
- setKeyAndValue(szKey, "InprocServer32", szModule) ;
- // Add the ProgID subkey under the CLSID key.
- setKeyAndValue(szKey, "ProgID", szProgID) ;
- // Add the version-independent ProgID subkey under CLSID key.
- setKeyAndValue(szKey, "VersionIndependentProgID",
- szVerIndProgID) ;
- // Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT.
- setKeyAndValue(szVerIndProgID, NULL, szFriendlyName) ;
- setKeyAndValue(szVerIndProgID, "CLSID", szCLSID) ;
- setKeyAndValue(szVerIndProgID, "CurVer", szProgID) ;
- // Add the versioned ProgID subkey under HKEY_CLASSES_ROOT.
- setKeyAndValue(szProgID, NULL, szFriendlyName) ;
- setKeyAndValue(szProgID, "CLSID", szCLSID) ;
- return S_OK ;
- }
- //
- // Remove the component from the registry.
- //
- LONG UnregisterServer(const CLSID& clsid, // Class ID
- const char* szVerIndProgID, // Programmatic
- const char* szProgID) // IDs
- {
- // Convert the CLSID into a char.
- char szCLSID[CLSID_STRING_SIZE] ;
- CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;
- // Build the key CLSID\\{...}
- char szKey[64] ;
- strcpy(szKey, "CLSID\\") ;
- strcat(szKey, szCLSID) ;
- // Delete the CLSID Key - CLSID\{...}
- LONG lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szKey) ;
- assert((lResult == ERROR_SUCCESS) ||
- (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.
- // Delete the version-independent ProgID Key.
- lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szVerIndProgID) ;
- assert((lResult == ERROR_SUCCESS) ||
- (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.
- // Delete the ProgID key.
- lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szProgID) ;
- assert((lResult == ERROR_SUCCESS) ||
- (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist.
- return S_OK ;
- }
- ///
- //
- // Internal helper functions
- //
- // Convert a CLSID to a char string.
- void CLSIDtochar(const CLSID& clsid,
- char* szCLSID,
- int length)
- {
- assert(length >= CLSID_STRING_SIZE) ;
- // Get CLSID
- LPOLESTR wszCLSID = NULL ;
- HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ;
- assert(SUCCEEDED(hr)) ;
- // Covert from wide characters to non-wide.
- wcstombs(szCLSID, wszCLSID, length) ;
- // Free memory.
- CoTaskMemFree(wszCLSID) ;
- }
- //
- // Delete a key and all of its descendents.
- //
- LONG recursiveDeleteKey(HKEY hKeyParent, // Parent of key to delete
- const char* lpszKeyChild) // Key to delete
- {
- // Open the child.
- HKEY hKeyChild ;
- LONG lRes = RegOpenKeyEx(hKeyParent, lpszKeyChild, 0,
- KEY_ALL_ACCESS, &hKeyChild) ;
- if (lRes != ERROR_SUCCESS)
- {
- return lRes ;
- }
- // Enumerate all of the decendents of this child.
- FILETIME time ;
- char szBuffer[256] ;
- DWORD dwSize = 256 ;
- while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL,
- NULL, NULL, &time) == S_OK)
- {
- // Delete the decendents of this child.
- lRes = recursiveDeleteKey(hKeyChild, szBuffer) ;
- if (lRes != ERROR_SUCCESS)
- {
- // Cleanup before exiting.
- RegCloseKey(hKeyChild) ;
- return lRes;
- }
- dwSize = 256 ;
- }
- // Close the child.
- RegCloseKey(hKeyChild) ;
- // Delete this child.
- return RegDeleteKey(hKeyParent, lpszKeyChild) ;
- }
- //
- // Create a key and set its value.
- // - This helper function was borrowed and modifed from
- // Kraig Brockschmidt's book Inside OLE.
- //
- BOOL setKeyAndValue(const char* szKey,
- const char* szSubkey,
- const char* szValue)
- {
- HKEY hKey;
- char szKeyBuf[1024] ;
- // Copy keyname into buffer.
- strcpy(szKeyBuf, szKey) ;
- // Add subkey name to buffer.
- if (szSubkey != NULL)
- {
- strcat(szKeyBuf, "\\") ;
- strcat(szKeyBuf, szSubkey ) ;
- }
- // Create and open key and subkey.
- long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT ,
- szKeyBuf,
- 0, NULL, REG_OPTION_NON_VOLATILE,
- KEY_ALL_ACCESS, NULL,
- &hKey, NULL) ;
- if (lResult != ERROR_SUCCESS)
- {
- return FALSE ;
- }
- // Set the Value.
- if (szValue != NULL)
- {
- RegSetValueEx(hKey, NULL, 0, REG_SZ,
- (BYTE *)szValue,
- strlen(szValue)+1) ;
- }
- RegCloseKey(hKey) ;
- return TRUE ;
- }
- //in UseCmpnt vcproj
- //client.cpp
- #include <iostream>
- using std::cout;
- using std::endl;
- #include <objbase.h>
- #include "..\Chapter7\Iface.h"//包含了诸如IX、IY的声明
- #include "..\Chapter7\GUIDS.cpp"//包含了诸如IID_IX、CLSID_Component1的定义
- void OutPut(const char* msg);
- {
- cout << "Client: \t\t" << msg << endl ;
- }
- int main()
- {
- CoInitialize(NULL) ;
- OutPut("Call CoCreateInstance to create") ;
- OutPut(" component and get interface IX.") ;
- IX* pIX = NULL ;
- HRESULT hr = ::CoCreateInstance(CLSID_Component1,
- NULL,
- CLSCTX_INPROC_SERVER,
- IID_IX,
- (void**)&pIX) ;
- if (SUCCEEDED(hr))
- {
- OutPut("Succeeded getting IX.") ;
- pIX->Fx() ; // Use interface IX.
- OutPut("Ask for interface IY.") ;
- IY* pIY = NULL ;
- hr = pIX->QueryInterface(IID_IY, (void**)&pIY) ;
- if (SUCCEEDED(hr))
- {
- OutPut("Succeeded getting IY.") ;
- pIY->Fy() ; // Use interface IY.
- pIY->Release() ;
- OutPut("Release IY interface.") ;
- }
- else
- {
- OutPut("Could not get interface IY.") ;
- }
- OutPut("Ask for interface IZ.") ;
- IZ* pIZ = NULL ;
- hr = pIX->QueryInterface(IID_IZ, (void**)&pIZ) ;
- if (SUCCEEDED(hr))
- {
- OutPut("Succeeded in getting interface IZ.") ;
- pIZ->Fz() ;
- pIZ->Release() ;
- OutPut("Release IZ interface.") ;
- }
- else
- {
- OutPut("Could not get interface IZ.") ;
- }
- OutPut("Release IX interface.") ;
- pIX->Release() ;
- }
- else
- {
- cout << "Client: \t\tCould not create component. hr = "
- << hex << hr << endl ;
- }
- // Uninitialize COM Library
- CoUninitialize();
- return 0;
- }
版权所有,翻版不究,欢迎山寨!