一. 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 ;
}