一. HRESULT
对于该返回值,只要记住一点,一定要使用SUCCEEDED和FAILED宏来判断,不要直接把它与S_OK,S_FALSE等等来比较。
二. GUID
包含头文件OBJBASE.H。 由于GUID值占用了16个字节,因此一般不用值传递GUID参数。而大量使用的是按引用传递,这就是为什么QueryInterface接受一个常量引用参数的原因。除了使用const IID&,还可以等价使用REFIID,同理,在传递类标识符时,可以使用REFCLSID,在传递GUID时可以使用REFGUID。
三. 注册表
由于Dll知道它所包含的组件,因此Dll可以完成这些信息的注册。但是由于Dll本身不能完成任何事情,因此在Dll中一定要输出如下两个函数:
STDAPI DllRegisterServer(); STDAPI DllUnregisterServer();
四. Com库初始化
在使用Com库中的其它函数之前,进程必须先调用CoInitialize来初始化Com库函数。当进程不再需要使用Com库函数时,必须调用CoUninitialize。对于每一个进程,Com库函数只需要初始化一次,并且由于Com库是用于创建组件的,因此进程中组件无需初始化Com库。
五. 内存管理
如果在组件中分配了一块儿内存,然后通过一个函数的指针传递给了客户,那么这块儿内存该由谁来释放,如何释放?
为此,com提供了两个方便的函数,CoTaskMemAlloc和CoTaskMemFree。
void* CoTaskMemAlloc( ULONG cb); void* CoTaskMemFree(void* pv);
同某个输出参数相关联的内存的释放应由函数的调用者使用CoTaskMemFree完成。
六.类厂
CoCreateInstance 返回组件中某个接口
CoGetClassObject 返回类厂中某个接口
在每次创建组件时,先创建相应的类厂,然后用所获取的IClassFactory指针来创建所需的接口需要完成的工作显然比直接调用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。
v1:若不想使用IClassFactory接口来创建组件,比如想使用IClassFactory2来创建组件。(应该CoCreateInstance默认使用IClassFactory)
v2:若需创建同一组件的多个实例,那么使用CoGetClassObject将可以获得更高的效率,因为这样只需要创建相应的类厂一次。
(1) 类厂的特性
首先类厂的一个实例只能创建同某个CLSID相应的组件。
(2) 类厂的实现
与某个特定的CLISID相应的类厂将是由实现组件的开发人员实现的。大多数情况下,类厂组件包含在与它所创建的组件相同的DLL中。
v1: DllGetClassObject
CoGetClassObject主要是调用组件dll中输出的DllGetClassObject来获得类厂接口指针。该函数如下:
STDAPI DllGetClassObject( const CLSID& clsid, const IID& iid, void** ppv);
组件的创建过程:首先是客户,它将调用CoGetClassObject来启动组件的创建过程。其次是Com库,它实现了CoGetClassObject函数。第三个角色是DLL,其中实现了被CoGetClassObject调用的DllGetClassObject函数。DllGetClassObject的任务就是创建客户所请求的类厂。
同一个DLL可以创建多个组件,这一点的关键之处在于将待创建的组件的CLISID传给DllGetClassObject,对于每一个CLSID,DllGetClassObject可以方便的创建一个不同的类厂。
下面我们来实现一个通过类厂,而且提供注册功能的组件。
(1)新建一个空的Dll项目。
(2)定义接口文件IFace.h如下:
interface IX : IUnknown { virtual void __stdcall Fx() = 0; }; interface IY : IUnknown { virtual void __stdcall Fy() = 0; }; interface IZ : IUnknown { virtual void __stdcall Fz() = 0; }; extern "C" const IID IID_IX; extern "C" const IID IID_IY; extern "C" const IID IID_IZ; extern "C" const CLSID CLSID_Component1 ;
(3)定义guid的定义文件 GUIDS.cpp
#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}} ;
(3) 定义并实现注册Dll的文件
#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
// // 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 ; }
(4)定义实现组件,以及类厂,以及导出函数的文件
#include <iostream> #include <ObjBase.h> #include "IFace.h" #include "REGISTRY.H" using namespace std; void trace(const char* msg) { cout << msg << endl; } //全局变量区 static HMODULE g_hModule = NULL; //dll module handle static long g_cComponents = 0; // 组件的活动数 static long g_cServerLocks = 0; // count of locks ,针对类厂 //用于注册表 const char g_szFriendlyName[] = "Inside Com,chapter 7 exmple wll"; // Version-independent ProgID const char g_szVerIndProgID[] = "InsideCOM.Chap07" ; // ProgID const char g_szProgID[] = "InsideCOM.Chap07.1" ; //组件 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\t CA Destroy self.") ; } HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv) { if (iid == IID_IUnknown) { *ppv = static_cast<IX*>(this) ; //默认返回IX } else if (iid == IID_IX) { *ppv = static_cast<IX*>(this) ; trace("Component:\t\tReturn pointer to IX. WLL") ; } else if (iid == IID_IY) { *ppv = static_cast<IY*>(this) ; trace("Component:\t\tReturn pointer to IY. WLL") ; } 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 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 ; } ; 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 component. 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 ; } //导出函数 //是否可以卸载Dll,提供给CoFreeUnusedLibraries库函数来调用。 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) { 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(); return hr; } // // DLL 注册到注册表 // STDAPI DllRegisterServer() { return RegisterServer(g_hModule, CLSID_Component1, g_szFriendlyName, g_szVerIndProgID, g_szProgID) ; } // // DLL 从注册表中删除 // STDAPI DllUnregisterServer() { return UnregisterServer(CLSID_Component1, g_szVerIndProgID, g_szProgID) ; } /////////////////////////////////////////////////////////// // // DLL module information // BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) { if (dwReason == DLL_PROCESS_ATTACH) { g_hModule = (HMODULE)hModule ; } return TRUE ; }
(6)定义导出函数文件
// CMPNT.def LIBRARY Cmpnt.dll DESCRIPTION 'Chapter 7 Example COM Component (c)1996-1997 Dale E. Rogerson' EXPORTS DllGetClassObject @2 PRIVATE DllCanUnloadNow @3 PRIVATE DllRegisterServer @4 PRIVATE DllUnregisterServer @5 PRIVATE
(7) build工程,并运行cmd后,输入 regsvr32 dll的绝对路径,进行dll注册。
下面我们来实现测试工程,新建一个控制台程序。不需要拷贝dll,lib到工程目录。
我们这里采用两种方式来访问组件,一个是CoCreateInstance,一个是CoGetClassObject
#include <iostream> #include <objbase.h> #include "Iface.h" using namespace std; void trace(const char* msg) { cout << "Client: \t\t" << msg << endl ;} int main() { // Initialize COM Library CoInitialize(NULL) ; trace("Call CoCreateInstance to create") ; trace(" component and get interface IX.") ; IX* pIX = NULL ; //方式一: /* HRESULT hr = ::CoCreateInstance(CLSID_Component1, NULL, CLSCTX_INPROC_SERVER, IID_IX, (void**)&pIX) ; */ //方式二: IClassFactory* pIFactory = NULL; HRESULT hr = ::CoGetClassObject(CLSID_Component1,CLSCTX_INPROC_SERVER,NULL,IID_IClassFactory,(void**)&pIFactory); if( SUCCEEDED(hr) ) { trace("Succeeded getting IFactory.") ; hr = pIFactory->CreateInstance(NULL,IID_IX,(void**)&pIX); } if (SUCCEEDED(hr)) { trace("Succeeded getting IX.") ; pIX->Fx() ; // Use interface IX. trace("Ask for interface IY.") ; IY* pIY = NULL ; hr = pIX->QueryInterface(IID_IY, (void**)&pIY) ; if (SUCCEEDED(hr)) { trace("Succeeded getting IY.") ; pIY->Fy() ; // Use interface IY. pIY->Release() ; trace("Release IY interface.") ; } else { trace("Could not get interface IY.") ; } trace("Ask for interface IZ.") ; IZ* pIZ = NULL ; hr = pIX->QueryInterface(IID_IZ, (void**)&pIZ) ; if (SUCCEEDED(hr)) { trace("Succeeded in getting interface IZ.") ; pIZ->Fz() ; pIZ->Release() ; trace("Release IZ interface.") ; } else { trace("Could not get interface IZ.") ; } trace("Release IX interface.") ; pIX->Release() ; } else { cout << "Client: \t\tCould not create component. hr = " << hex << hr << endl ; } // Uninitialize COM Library CoUninitialize() ; return 0 ; }