聚合
聚合的情况下,外部组件直接把内部组件的接口指针返回给客户,因此无法对接口的实现进行修改
示意图如下
聚合的实现
客户向外部组件请求接口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
//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}};
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_H_
registry.cpp
//
//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,外部组件
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;
}
ca.def
LIBRARY ca.dll
EXPORTS DllGetClassObject @2 PRIVATE
DllCanUnloadNow @3 PRIVATE
DllRegisterServer @4 PRIVATE
DllUnregisterServer @5 PRIVATE
组件ca的iface.h,guids.cpp,registry.h,registry.cpp同组件cb
客户端
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;
}
客户端的iface.h,guids.cpp同组件cb
聚合组件的一些问题
一.对组件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(),实际是上增加外部组件的引用计数