第7章
相信有了前六章的知識積累,學(xué)些以后的章節將會(huì )很順利。本章實(shí)現了一個(gè)真正的COM組件,并通過(guò)client客戶(hù)端來(lái)使用這個(gè)組件。
本章將介紹類(lèi)廠(chǎng),類(lèi)廠(chǎng)是能夠創(chuàng )建其他組件的組件,CoCreateInstance函數也是按照一定方法通過(guò)類(lèi)廠(chǎng)來(lái)創(chuàng )建組件的。
CoCreateInstance
CoCreateInstance函數是COM庫的函數,函數原型如下
- HRESULT __stdcall CoCreateInstance(const CLSID &clsid,
- IUnknown *pIUnkownOuter,
- DWORD dwClsContext,
- const IID &iid,
- void **ppv);
其中前四個(gè)是輸入參數,最后一個(gè)是輸出參數。第一個(gè)參數clsid是所創(chuàng )建組件的CLSID,第二個(gè)參數pIUnkownOuter是聚合組件需要用的,將在第8章介紹,第三個(gè)參數dwClsContext是限定所創(chuàng )建組件的執行上下文,第四個(gè)參數iid是所創(chuàng )建組件的接口的IID,第五個(gè)參數ppv將返回該接口的指針。
dwClsContext
dwClsContext可以控制所創(chuàng )建是與客戶(hù)在相同的進(jìn)程中運行,還是在不同的進(jìn)程中運行,或者是在另外一臺機器上運行。此參數可以是如下值的組合
CLSCTX_INPROC_SERVER 客戶(hù)希望創(chuàng )建在同一進(jìn)程中運行的組件,因此組件必須是在DLL中實(shí)現。
CLSCTX_INPROC_HANDLER 客戶(hù)希望創(chuàng )建進(jìn)程中處理器。
CLSCTX_LOCAL_SERVER 客戶(hù)希望創(chuàng )建一個(gè)在同一機器上的另外一個(gè)進(jìn)程中運行的組件。組件是由EXE實(shí)現的。
CLSCTX_REMOTE_SERVER 客戶(hù)希望創(chuàng )建一個(gè)在遠程機器上運行的組件。分布式COM組件。
在OBJBASE.H中定義了一些上述值的組合。
CoCreateInstance的具有一定的不靈活性,解決問(wèn)題的辦法是使用專(zhuān)門(mén)用于創(chuàng )建所需組件的組件,這個(gè)組件就是類(lèi)廠(chǎng)。
類(lèi)廠(chǎng)
某個(gè)特定的類(lèi)廠(chǎng)可以創(chuàng )建某個(gè)特定CLSID相對應的組件,客戶(hù)可以通過(guò)類(lèi)廠(chǎng)提供的接口來(lái)對組件的創(chuàng )建過(guò)程進(jìn)行控制??蛻?hù)使用CoCreateInstance所創(chuàng )建的組件實(shí)際上是通過(guò)類(lèi)廠(chǎng)的IClassFactory創(chuàng )建的,使用類(lèi)廠(chǎng)創(chuàng )建組件的步驟是首先創(chuàng )建類(lèi)廠(chǎng),然后使用IClassFactory創(chuàng )建所需的組件。
創(chuàng )建類(lèi)廠(chǎng)
COM庫函數CoGetClassObject創(chuàng )建同某個(gè)CLSID相應的類(lèi)廠(chǎng)。函數原型如下
- HRESULT __stdcall CoGetClassObject(const CLSID &clsid,
- DWORD dwClsContext,
- COSERVERINFO *pServerInfo,
- const IID &iid,
- void **ppv);
同CoCreateInstance非常相似。第一個(gè)參數const CLSID&待創(chuàng )建組件的CLSID,第二個(gè)參數DWORD dwClsContext是待創(chuàng )建組件的執行上下文,第三個(gè)參數COSERVERINFO*用于遠程組件的訪(fǎng)問(wèn),將在第10章討論,第四個(gè)參數const IID&是IClassFactory接口的IID,第五個(gè)參數返回IClassFactory接口的指針。
創(chuàng )建組件
IClassFactory
大多數組件是使用IClassFactory來(lái)創(chuàng )建的,原型如下
- interface IClassFactory:IUnknown
- {
- HRESULT __stdcall CreateInstance(IUnkown *pUnknownOuter, const IID &id, void **ppv);
- HRESULT __stdcall LockServer(BOOL bLock);
- };
IClassFactory::CreateInstance函數的第一個(gè)參數IUnknown*是組件聚合使用的,將在第8章介紹,后兩個(gè)參數跟CoCreateInstance后兩個(gè)參數作用相同,將在創(chuàng )建組件的
同時(shí)返回此組件的某個(gè)接口指針??梢钥吹絀ClassFactory::CreateInstance并沒(méi)有接收一個(gè)CLSID參數,這意味著(zhù)此函數將只能創(chuàng )建同某個(gè)CLSID——即傳給CoGetClassObject的參數CLSID相應的組件。
在兩種情況下使用創(chuàng )建類(lèi)廠(chǎng)再創(chuàng )建組件的方法,而不是直接使用CoCreateInstance的方法直接創(chuàng )建組件
(1)想使用IClassFactory2來(lái)創(chuàng )建組件。IClassFactory2是Microsoft定義的另外一個(gè)接口,此接口在IClassFactory的基礎上增加了獲取組件接口的許可權限功能。
(2)需要創(chuàng )建一個(gè)組件的多個(gè)實(shí)例。這樣只需創(chuàng )建相應的類(lèi)廠(chǎng)一次,而CoCreateInstance需要為每一個(gè)組件實(shí)例分別創(chuàng )建并釋放相應的類(lèi)廠(chǎng)。
類(lèi)廠(chǎng)的特性
(1)類(lèi)廠(chǎng)將只能給你創(chuàng )建同某個(gè)CLSID相應的組件。
(2)與某個(gè)特定CLSID相應的類(lèi)廠(chǎng)是由組件開(kāi)發(fā)人員來(lái)實(shí)現的。大多數情況下,類(lèi)廠(chǎng)組件包含在它所創(chuàng )建的組件的相同的DLL中。
類(lèi)廠(chǎng)的創(chuàng )建
客戶(hù)通過(guò)CoGetClassObject來(lái)創(chuàng )建類(lèi)廠(chǎng),這就需要在DLL中實(shí)現一個(gè)特定的函數,此函數名為DllGetClassObject,函數原型如下
- STDAPI DllGetClassObject(const CLSID &clsid, const IID &iid, void **ppv);
函數的三個(gè)參數同CoGetClassObject中參數的意義相同。
通過(guò)類(lèi)廠(chǎng)來(lái)創(chuàng )建組件的示意圖如下,COM庫函數CoGetClassObject將根據傳入參數CLSID查詢(xún)注冊表,裝載組件所在的DLL庫。
CFactory只不過(guò)是另外一個(gè)組件而已,它也跟其他組件一樣實(shí)現了IUnknown接口。
IClassFactory::CreateInstance和DllGetClassObject的實(shí)現是相同的,這兩個(gè)函數都將創(chuàng )建一個(gè)組件然后向它查詢(xún)某個(gè)接口。
組件的注冊
實(shí)現組件的DLL中輸出四個(gè)函數,除了DllGetClassObject外,DllRegisterServer和DllUnregisterServer將用于組件在注冊表中注冊和取消注冊(鏈接的時(shí)候需要鏈接Advapi32.lib),調用regsvr32來(lái)完成注冊和取消。
類(lèi)廠(chǎng)的復用
在設計類(lèi)廠(chǎng)和組件的時(shí)候,可以做到只用一個(gè)類(lèi)廠(chǎng)的實(shí)現來(lái)完成所有組件的創(chuàng )建。將在第9章中實(shí)現。但即使是這樣,類(lèi)廠(chǎng)CFactory的一個(gè)實(shí)例也僅能創(chuàng )建一個(gè)同某個(gè)CLSID相應的組件。
DLL的卸載
COM庫中實(shí)現了一個(gè)CoFreeUnusedLibraries的函數,以釋放那些不再需要使用的DLL庫所占用的內存,由組件的客戶(hù)進(jìn)行調用。
DllCanUnloadNow函數
也是在實(shí)現組件的DLL中的輸出函數,CoFreeUnusedLibraries函數將調用DllCanUnloadNow函數,以詢(xún)問(wèn)Dll是否可以被卸載。代碼中g(shù)_lComponents的作用就是這個(gè),可以看到IClassFactory::CreateInstance也就是說(shuō)在創(chuàng )建組件的時(shí)候,組件的構造函數都可以將g_lComponents增大,組件的析構函數可以將g_lComponents的值減小。若g_lComponets值為0,CoFreeUnusedLibraries可以將組件的DLL卸載掉。
LockServer函數
使用g_lComponents只是對DLL中的組件進(jìn)行了記數,另外一個(gè)組件CFactory并沒(méi)有記數。對類(lèi)廠(chǎng)的記數使用了IFactory::LockServer函數,組件Server內部設置了另外一個(gè)與g_IComponents不同的計數值進(jìn)行計數。(將在第10章進(jìn)行討論,主要原因是因為第10章將會(huì )講到的進(jìn)程外組件(exe實(shí)現的)的類(lèi)廠(chǎng)無(wú)法像進(jìn)程中的組件(dll實(shí)現的)一樣方便的進(jìn)行記數。)
本章代碼
組件端:
cmpnt.cpp
-
-
-
-
-
- #include <objbase.h>
- #include "iface.h" //interface declarations
- #include "Registry.h" //Registry helper function
- #include <iostream>
- #include <string>
- using namespace std;
-
-
- void trace(string msg)
- {
- cout<<msg<<endl;
- }
-
-
-
- static HMODULE g_hModule = NULL;
- static long g_lComponent = 0;
- static long g_lServerLocks = 0;
-
-
- const char g_szFriendlyName[] = "InsideCOM Chapter 7 Example";
-
-
- const char g_szVerIndProgID[] = "InsideCOM.Chap07";
-
-
- const char g_szProgID[] = "InsideCOM.Chap07.1";
-
-
- class CA:public IX, public IY
- {
- public:
-
- virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv);
- virtual ULONG __stdcall AddRef();
- virtual ULONG __stdcall Release();
- virtual void __stdcall Fx() {cout<<"Fx"<<endl;}
- virtual void __stdcall Fy() {cout<<"Fy"<<endl;}
- CA();
- ~CA();
- protected:
- long m_cRef;
- };
-
- CA::CA()
- {
- m_cRef = 1;
-
- InterlockedIncrement(&g_lComponent);
- }
-
- CA::~CA()
- {
- InterlockedDecrement(&g_lComponent);
- trace("Component:destroy self");
- }
-
-
- 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: return pointer to ix");
- }
- else if(iid == IID_IY)
- {
- *ppv = static_cast<IY*>(this);
- trace("component: return 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 CFactory:public IClassFactory
- {
- public:
-
- virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv);
- virtual ULONG __stdcall AddRef();
- virtual ULONG __stdcall Release();
-
- virtual HRESULT __stdcall CreateInstance(IUnknown *pUnknownOuter, const IID &iid, void **ppv);
- virtual HRESULT __stdcall LockServer(BOOL bLock);
-
- CFactory():m_cRef(1){}
-
- ~CFactory()
- {
- trace("class factory :destory 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;
- }
-
-
-
-
- HRESULT __stdcall CFactory::CreateInstance(IUnknown *pUnknownOuter, const IID &iid, void **ppv)
- {
- trace("class factory : create component");
-
- if(pUnknownOuter != NULL)
- {
- return CLASS_E_NOAGGREGATION;
- }
-
- CA *pA = new CA();
- if(pA == NULL)
- {
- return E_OUTOFMEMORY;
- }
-
- HRESULT hr = pA->QueryInterface(iid, ppv);
-
-
- pA->Release();
- return hr;
- }
-
-
- HRESULT __stdcall CFactory::LockServer(BOOL bLock)
- {
- if(bLock)
- {
- InterlockedIncrement(&g_lServerLocks);
- }
- else
- {
- InterlockedDecrement(&g_lServerLocks);
- }
- return S_OK;
- }
-
-
-
-
-
- STDAPI DllGetClassObject(const CLSID &clsid, const IID &iid, void **ppv)
- {
- trace("DllGetClassObeject: create class factory");
- 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;
- }
-
- STDAPI DllCanUnloadNow()
- {
- if((g_lComponent == 0) && (g_lServerLocks == 0))
- {
- return S_OK;
- }
- else
- {
- return S_FALSE;
- }
- }
-
-
- STDAPI DllRegisterServer()
- {
- return RegisterServer(g_hModule,
- CLSID_Component1,
- g_szFriendlyName,
- g_szVerIndProgID,
- g_szProgID);
- }
-
-
- STDAPI DllUnregisterServer()
- {
- return UnregisterServer(CLSID_Component1,
- g_szVerIndProgID,
- g_szProgID);
- }
-
- BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, void *lpReserved)
- {
- if(dwReason == DLL_PROCESS_ATTACH)
- {
- g_hModule = hModule;
- }
- return TRUE;
- }
-
-
cmpnt.def
- LIBRARY Cmpnt.dll
- DESCRIPTION 'Chapter 7 Example COM Component'
-
- EXPORTS DllGetClassObject @2 PRIVATE
- DllRegisterServer @3 PRIVATE
- DllUnregisterServer @4 PRIVATE
- DllCanUnloadNow @5 PRIVATE
registry.h
- #ifndef _REGISTRY_H_
- #define _REGISTRY_H_
-
- HRESULT RegisterServer(HMODULE hModule,
- const CLSID &clsid,
- const char *szFriendlyName,
- const char *szVerIndProgID,
- const char *szProgID);
- HRESULT UnregisterServer(const CLSID &clsid,
- const char *szVerIndProgID,
- const char *szProgID);
- #endif
registry.cpp
-
-
-
- #include <objbase.h>
- #include <cassert>
- #include "registry.h"
-
-
-
-
-
- BOOL SetKeyAndValue(const char *szKey, const char *szSubKey, const char *szValue);
-
-
- void CLSIDtochar(const CLSID &clsid, char *szClsID, int nLength);
-
-
- LONG RecursiveDeleteKey(HKEY hKeyParent, const char *szKeyChild);
-
- const int CLSID_STRING_SIZE = 39 ;
-
-
-
-
- HRESULT RegisterServer(HMODULE hModule,
- const CLSID& clsid,
- const char* szFriendlyName,
- const char* szVerIndProgID,
- const char* szProgID)
- {
- char szModule[512] ;
- DWORD dwResult =::GetModuleFileName(hModule, szModule, sizeof(szModule)/sizeof(char));
-
- assert(dwResult != 0) ;
-
-
- char szCLSID[CLSID_STRING_SIZE] ;
- CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;
-
-
- char szKey[64] ;
- strcpy(szKey, "CLSID\\") ;
- strcat(szKey, szCLSID) ;
-
-
- SetKeyAndValue(szKey, NULL, szFriendlyName) ;
-
-
- SetKeyAndValue(szKey, "InprocServer32", szModule) ;
-
-
- SetKeyAndValue(szKey, "ProgID", szProgID) ;
-
-
- SetKeyAndValue(szKey, "VersionIndependentProgID",
- szVerIndProgID) ;
-
-
- SetKeyAndValue(szVerIndProgID, NULL, szFriendlyName) ;
- SetKeyAndValue(szVerIndProgID, "CLSID", szCLSID) ;
- SetKeyAndValue(szVerIndProgID, "CurVer", szProgID) ;
-
-
- SetKeyAndValue(szProgID, NULL, szFriendlyName) ;
- SetKeyAndValue(szProgID, "CLSID", szCLSID) ;
-
- return S_OK ;
- }
-
-
-
-
- LONG UnregisterServer(const CLSID& clsid,
- const char* szVerIndProgID,
- const char* szProgID)
- {
-
- char szCLSID[CLSID_STRING_SIZE] ;
- CLSIDtochar(clsid, szCLSID, sizeof(szCLSID)) ;
-
-
- char szKey[64] ;
- strcpy(szKey, "CLSID\\") ;
- strcat(szKey, szCLSID) ;
-
-
- LONG lResult = RecursiveDeleteKey(HKEY_CLASSES_ROOT, szKey) ;
- assert( (lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND) ) ;
-
-
- lResult = RecursiveDeleteKey(HKEY_CLASSES_ROOT, szVerIndProgID) ;
- assert((lResult == ERROR_SUCCESS) ||
- (lResult == ERROR_FILE_NOT_FOUND)) ;
-
-
- lResult = RecursiveDeleteKey(HKEY_CLASSES_ROOT, szProgID) ;
- assert((lResult == ERROR_SUCCESS) ||
- (lResult == ERROR_FILE_NOT_FOUND)) ;
-
- return S_OK ;
- }
-
-
- BOOL SetKeyAndValue(const char *szKey, const char *szSubKey, const char *szValue)
- {
- HKEY hKey;
- char szKeyBuf[1024] ;
-
-
- strcpy(szKeyBuf, szKey) ;
-
-
- if (szSubKey != NULL)
- {
- strcat(szKeyBuf, "\\") ;
- strcat(szKeyBuf, szSubKey ) ;
- }
-
-
- 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 ;
- }
-
-
- if (szValue != NULL)
- {
- RegSetValueEx(hKey, NULL, 0, REG_SZ,
- (BYTE *)szValue,
- strlen(szValue)+1) ;
- }
-
- RegCloseKey(hKey) ;
- return TRUE ;
-
- }
-
-
- LONG RecursiveDeleteKey(HKEY hKeyParent, const char *szKeyChild)
- {
-
- HKEY hKeyChild ;
- LONG lRes = RegOpenKeyEx(hKeyParent, szKeyChild, 0,
- KEY_ALL_ACCESS, &hKeyChild) ;
- if (lRes != ERROR_SUCCESS)
- {
- return lRes ;
- }
-
-
- FILETIME time ;
- char szBuffer[256] ;
- DWORD dwSize = 256 ;
- while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL,
- NULL, NULL, &time) == S_OK)
- {
-
- lRes = RecursiveDeleteKey(hKeyChild, szBuffer) ;
- if (lRes != ERROR_SUCCESS)
- {
-
- RegCloseKey(hKeyChild) ;
- return lRes;
- }
- dwSize = 256 ;
- }
-
-
- RegCloseKey(hKeyChild) ;
-
-
- return RegDeleteKey(hKeyParent, szKeyChild) ;
- }
-
- void CLSIDtochar(const CLSID &clsid, char *szClSID, int nLength)
- {
- assert(nLength >= CLSID_STRING_SIZE) ;
-
- LPOLESTR wszCLSID = NULL ;
- HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ;
- assert(SUCCEEDED(hr)) ;
-
-
- wcstombs(szClSID, wszCLSID, nLength);
-
-
- CoTaskMemFree(wszCLSID) ;
- }
iface.h
-
-
- #include <objbase.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 const IID IID_IX;
- extern const IID IID_IY;
- extern const IID IID_IZ;
- extern const CLSID CLSID_Component1;
-
guids.cpp
-
-
-
- #include <objbase.h>
-
-
- extern const IID IID_IX =
- {0x32bb8320, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
-
-
- extern const IID IID_IY =
- {0x32bb8321, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
-
-
- extern const IID IID_IZ =
- {0x32bb8322, 0xb41b, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
-
-
- extern const CLSID CLSID_Component1 =
- {0x0c092c21, 0x882c, 0x11cf,
- {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
-
-
客戶(hù)端
clients.cpp
-
-
-
-
- #include <objbase.h>
- #include "iface.h"
- #include <iostream>
- #include <string>
- using namespace std;
-
- void trace(string msg)
- {
- cout<<msg<<endl;
- }
-
- int main(void)
- {
- CoInitialize(NULL);
- trace("client:call CoCreateInstance to create component and get interface ix");
- IX *pIx = NULL;
- HRESULT hr = ::CoCreateInstance(CLSID_Component1, NULL, CLSCTX_INPROC_SERVER, IID_IX, (void**)&pIx);
- if(SUCCEEDED(hr))
- {
- trace("client:Succeeded getting IX");
- pIx->Fx();
- trace("client:Ask for interface IY");
- IY *pIy = NULL;
- hr = pIx->QueryInterface(IID_IY, (void**)&pIy);
- if(SUCCEEDED(hr))
- {
- trace("client:Succeeded getting IY");
- pIy->Fy();
- pIy->Release();
- trace("client:Release IY interface");
- }
- else
- {
- trace("client:Could not get interface IY");
- }
- trace("client:Ask for interface IZ");
- IZ *pIz = NULL;
- hr = pIx->QueryInterface(IID_IZ, (void**)&pIz);
- if(SUCCEEDED(hr))
- {
- trace("client:Succeeded getting IZ");
- pIz->Fz();
- pIz->Release();
- trace("client:Release IZ interface");
- }
- else
- {
- trace("client:Could not get interface IZ");
- }
- trace("client:Release IX interface");
- pIx->Release();
- }
- else
- {
- cout<<"Client: Could not create component.hr ="<<hex<<hr<<endl;
- }
- CoUninitialize();
- return 0;
- }
運行結果