聚合
聚合的情况下,外部组件直接把内部组件的接口指针返回给客户,因此无法对接口的实现进行修改
示意图如下
聚合的实现
客户向外部组件请求接口IY,外部组件只是向内部组件查询此接口并将此接口指针返回给客户。
若内部组件直接返回接口指针给客户,客户可以直接得到两个不同的IUnknown接口,即外部组件和内部组件的。因此内部组将应该使用外部组件所实现的IUnknown接口(外部未知接口),为此,内部组件需要一个指向外部未知接口的指针,将调用请求转发给外部未知接口,
代理和非代理未知接口
内部组件要使用外部未知接口,需要实现两个IUnknown接口,一个是非代理未知接口,一个是代理未知接口。非代理外部接口按照之前的方式实现内部组件的IUnknown接口,代理未知接口在内部调用外部IUnknown接口(被聚合的情况下)或者非代理IUnknown接口(没有被聚合的情况下)。
事实上,对内部组件来说,需要保存一个外部组件的IUnknown接口,当组件使用者调用时,用外部组件的IUnknown来响应用户的调用请求,以及一个未被聚合时的自己内部的IUnknown接口,为了区分,起名字叫非代理IUnknown接口和代理IUnknown接口,代理IUnknown接口还有一个作用,可以实现响应的分发,是决定在聚合时使用外部IUnknown接口还是内部的IUnknown接口。
对聚合组件的客户来说,将调用代理未知接口就是外部组件的IUnknown接口。对外部组件来说,需要保存一个内部组件的IUnknown接口就是非代理外部接口来操作内部组件。如下图所示
代码如下
组件CB,被聚合的组件,即内部组件
cb.cpp
//cb.cpp // #include <ObjBase.h> #include "iface.h" #include "registry.h" #include <iostream> using namespace std; 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 const char g_szFriendlyName[] = "Inside COM, Chapter 8 Example 2, Component 2"; // Version-independent ProgID const char g_szVerIndProgID[] = "InsideCOM.Chap08.Ex2.Cmpnt2"; // ProgID const char g_szProgID[] = "InsideCOM.Chap08.Ex2.Cmpnt2.1"; struct INonDelegateUnknown { virtual HRESULT __stdcall NonDelegateQueryInterface(const IID &iid, void **ppv) = 0; virtual ULONG __stdcall NonDelegateAddRef() = 0; virtual ULONG __stdcall NonDelegateRelease() = 0; }; class CB : public IY, public INonDelegateUnknown { public : virtual HRESULT __stdcall NonDelegateQueryInterface(const IID &iid, void **ppv); virtual ULONG __stdcall NonDelegateAddRef(); virtual ULONG __stdcall NonDelegateRelease(); virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); virtual void __stdcall Fy(); CB(IUnknown *pIUnkownOuter); ~CB(); protected: long m_cRef; IUnknown *m_pIUnknownOuter; }; HRESULT __stdcall CB::QueryInterface(const IID &iid, void **ppv) { return m_pIUnknownOuter->QueryInterface(iid, ppv); } ULONG __stdcall CB::AddRef() { return m_pIUnknownOuter->AddRef(); } ULONG __stdcall CB::Release() { return m_pIUnknownOuter->Release(); } ULONG __stdcall CB::NonDelegateAddRef() { return InterlockedIncrement(&m_cRef); } ULONG __stdcall CB::NonDelegateRelease() { if(InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; } return m_cRef; } HRESULT __stdcall CB::NonDelegateQueryInterface(const IID &iid, void **ppv) { if(iid == IID_IUnknown) { *ppv = static_cast<INonDelegateUnknown*>(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; } void __stdcall CB::Fy() { cout<<"CB::Fy()"<<endl; } CB::CB(IUnknown *pIUnkownOuter) : m_cRef(1) { InterlockedIncrement(&g_cComponents); if(pIUnkownOuter == NULL) { m_pIUnknownOuter = reinterpret_cast<IUnknown*>(static_cast<INonDelegateUnknown*>(this)); } else { m_pIUnknownOuter = pIUnkownOuter; } } CB::~CB() { InterlockedDecrement(&g_cComponents); } /////////////////////////////////////////////////////////// class CFactory: public IClassFactory { public: virtual HRESULT __stdcall CreateInstance(IUnknown *pUnknownOuter, const IID &iid, void **ppv); virtual HRESULT __stdcall LockServer(BOOL bLock); virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); CFactory(); ~CFactory(); protected: long m_cRef; }; CFactory::CFactory() : m_cRef(1) { } CFactory::~CFactory() { } HRESULT __stdcall CFactory::CreateInstance(IUnknown *pUnknownOuter, const IID &iid, void **ppv) { if(pUnknownOuter != NULL && iid != IID_IUnknown) { return CLASS_E_NOAGGREGATION; } CB *pB = new CB(pUnknownOuter); HRESULT hr = pB->NonDelegateQueryInterface(iid, ppv); pB->NonDelegateRelease(); return hr; } HRESULT __stdcall CFactory::LockServer(BOOL bLock) { if(bLock) { InterlockedIncrement(&g_cServerLocks); } else { InterlockedDecrement(&g_cServerLocks); } return S_OK; } HRESULT __stdcall CFactory::QueryInterface(const IID &iid, void **ppv) { if(iid == IID_IClassFactory || iid == IID_IUnknown) { *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; } /////////////////////////////////////////////////////////// //export function STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) { if(clsid != CLSID_Component2) { return CLASS_E_CLASSNOTAVAILABLE; } IClassFactory *pFactory = new CFactory(); HRESULT hr = pFactory->QueryInterface(iid, ppv); pFactory->Release(); return hr; } STDAPI DllCanUnloadNow() { if(g_cServerLocks == 0 && g_cComponents == 0) { return S_OK; } return S_FALSE; } STDAPI DllRegisterServer() { return RegisterServer(g_hModule, CLSID_Component2, g_szFriendlyName, g_szVerIndProgID, g_szProgID); } STDAPI DllUnregisterServer() { return UnregisterServer(CLSID_Component2, g_szVerIndProgID, g_szProgID); } BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) { if (dwReason == DLL_PROCESS_ATTACH) { g_hModule = (HMODULE)hModule; } return TRUE; }
//iface.h // #ifndef IFACE_H #define IFACE_H interface IX:IUnknown { virtual void __stdcall Fx() = 0; }; interface IY:IUnknown { virtual void __stdcall Fy() = 0; }; extern "C" const IID IID_IX; extern "C" const IID IID_IY; extern "C" const CLSID CLSID_Component1; extern "C" const CLSID CLSID_Component2; #endif //IFACE_H
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}}; // {0c092c22-882c-11cf-a6bb-0080c7b2d682} extern "C" const CLSID CLSID_Component1 = {0x0c092c22, 0x882c, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}}; // {0c092c23-882c-11cf-a6bb-0080c7b2d682} extern "C" const CLSID CLSID_Component2 = {0x0c092c23, 0x882c, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}};
#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_H_
// //registry.cpp // #include <objbase.h> #include <cassert> #include "registry.h" // //Internal functions //Set the key and its value BOOL SetKeyAndValue(const char *szKey, const char *szSubKey, const char *szValue); //Convert a clsid to a char string void CLSIDtochar(const CLSID &clsid, char *szClsID, int nLength); //Delete szKeyChild and all its child LONG RecursiveDeleteKey(HKEY hKeyParent, const char *szKeyChild); const int CLSID_STRING_SIZE = 39 ; // // 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 { 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 ; } 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 ; } LONG RecursiveDeleteKey(HKEY hKeyParent, const char *szKeyChild) { // Open the child. HKEY hKeyChild ; LONG lRes = RegOpenKeyEx(hKeyParent, szKeyChild, 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, szKeyChild) ; } void CLSIDtochar(const CLSID &clsid, char *szClSID, int nLength) { assert(nLength >= 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, nLength); // Free memory. CoTaskMemFree(wszCLSID) ; }
cb.def
LIBRARY cb.dll EXPORTS DllGetClassObject @2 PRIVATE DllCanUnloadNow @3 PRIVATE DllRegisterServer @4 PRIVATE DllUnregisterServer @5 PRIVATE
ca.cpp
#include <ObjBase.h> #include "iface.h" #include "registry.h" #include <iostream> using namespace std; 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 8 Example 2, Component 1"; // Version-independent ProgID const char g_szVerIndProgID[] = "InsideCOM.Chap08.Ex2.Cmpnt1"; // ProgID const char g_szProgID[] = "InsideCOM.Chap08.Ex2.Cmpnt1.1"; class CA : public IX { public: virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); virtual void __stdcall Fx(); CA(); ~CA(); HRESULT __stdcall Init(); protected: long m_cRef; IY *m_pIy; IUnknown *m_pIUnknownInner; }; CA::CA():m_cRef(1), m_pIUnknownInner(NULL) { InterlockedIncrement(&g_cComponents); } CA::~CA() { InterlockedDecrement(&g_cComponents); if (m_pIUnknownInner != NULL) { m_pIUnknownInner->Release(); } } HRESULT __stdcall CA::Init() { HRESULT hr; IUnknown *pIUnknownOuter = this; hr = CoCreateInstance(CLSID_Component2, pIUnknownOuter, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&m_pIUnknownInner); if (FAILED(hr)) { return E_FAIL; } hr = m_pIUnknownInner->QueryInterface(IID_IY, (void**)&m_pIy); if (FAILED(hr)) { m_pIUnknownInner->Release(); return E_FAIL; } pIUnknownOuter->Release(); return S_OK; } HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv) { if (iid == IID_IUnknown) { *ppv = static_cast<IUnknown*>(this); } else if (iid == IID_IX) { *ppv = static_cast<IX*>(this); } else if (iid == IID_IY) { return m_pIUnknownInner->QueryInterface(iid, ppv); } 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; } void __stdcall CA::Fx() { cout<<"CA::Fx()"<<endl; } //////////////////////////////////////////////////////// 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(); ~CFactory(); private: long m_cRef; }; CFactory::CFactory() : m_cRef(1) { } CFactory::~CFactory() { } HRESULT __stdcall CFactory::QueryInterface(REFIID 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) { if (pUnknownOuter != NULL) { return CLASS_E_NOAGGREGATION; } CA* pA = new CA; if (pA == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pA->Init(); if (FAILED(hr)) { pA->Release(); return hr; } hr = pA->QueryInterface(iid, ppv); pA->Release(); return hr; } HRESULT __stdcall CFactory::LockServer(BOOL bLock) { if (bLock) { ::InterlockedIncrement(&g_cServerLocks); } else { ::InterlockedDecrement(&g_cServerLocks); } return S_OK; } /////////////////////////////////////////////////////////// // Export functions STDAPI DllCanUnloadNow() { if ((g_cComponents == 0) && (g_cServerLocks == 0)) { return S_OK; } else { return S_FALSE; } } STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv) { if (clsid != CLSID_Component1) { return CLASS_E_CLASSNOTAVAILABLE; } CFactory* pFactory = new CFactory; HRESULT hr = pFactory->QueryInterface(iid, ppv); pFactory->Release(); return hr; } 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(HANDLE hModule, DWORD dwReason, void* lpReserved) { if (dwReason == DLL_PROCESS_ATTACH) { g_hModule = (HMODULE)hModule; } return TRUE; }
LIBRARY ca.dll EXPORTS DllGetClassObject @2 PRIVATE DllCanUnloadNow @3 PRIVATE DllRegisterServer @4 PRIVATE DllUnregisterServer @5 PRIVATE
客户端
client.cpp
#include <objbase.h> #include "Iface.h" #include <iostream> using namespace std; int main(void) { CoInitialize(NULL); IX* pIX = NULL; HRESULT hr = CoCreateInstance(CLSID_Component1, NULL, CLSCTX_INPROC_SERVER, IID_IX, (void**)&pIX); if (SUCCEEDED(hr)) { IY* pIY = NULL; hr = pIX->QueryInterface(IID_IY, (void**)&pIY); if (SUCCEEDED(hr)) { pIY->Fy(); pIY->Release(); } pIX->Release(); } CoUninitialize(); return 0; }
聚合组件的一些问题
一.对组件cb来说,有两个未知接口,一个是自身的未知接口,一个是外部组件的未知接口
对组件ca来说,需要在组件创建的时候Init()的时候,创建组件cb,获取一个内部组件的非代理未知接口,方便对组件cb读取控制
二. 组件cb中的
在
HRESULT __stdcall CFactory::CreateInstance(IUnknown *pUnknownOuter, const IID &iid, void **ppv)
创建组件cb的时候
HRESULT hr = pB->NonDelegateQueryInterface(iid, ppv); pB->NonDelegateRelease();
如果组件cb被聚合,返回的是非代理未知接口,由外部组件ca获取。
HRESULT __stdcall CB::NonDelegateQueryInterface(const IID &iid, void **ppv)函数中的
reinterpret_cast<IUnknown*>(*ppv)->AddRef();的作用,当组件cb被聚合的情况下,iid == IID_IY时,*ppv指向的是接口IY,对它AddRef(),实际是上增加外部组件的引用计数