Com总结二

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

猜你喜欢

转载自qiqi-love.iteye.com/blog/2197469