1. 分布式組件對(duì)象模型
多少年來軟件的開發(fā)過程并沒有很大的改變,軟件開發(fā)過程中需要面對(duì)的主要問題如:開發(fā)周期長,難于確保程序的正確性,難于維護(hù)等還沒有得到非常好的解決,盡管出現(xiàn)了如面向?qū)ο?,框架設(shè)計(jì)等等的概念和工具。組件對(duì)象模型是傳統(tǒng)面向?qū)ο竽P偷臄U(kuò)充,傳統(tǒng)面向?qū)ο竽P偷闹攸c(diǎn)是源程序,以及系統(tǒng)分析和設(shè)計(jì)過程。組件的概念則強(qiáng)調(diào)大的軟件系統(tǒng)如何由不同開發(fā)商的小型可執(zhí)行組件構(gòu)成。以下首先從面向?qū)ο竽P烷_始對(duì)各種概念作一番梳理。
· 面向?qū)ο缶幊?br /> 面向?qū)ο笫且粋€(gè)被廣大編程人員和工業(yè)界認(rèn)同已久的概念。面向?qū)ο蟪绦蛟O(shè)計(jì)語言讓開發(fā)者按照現(xiàn)實(shí)世界里人們思考問題的模式來編寫程序,它讓開發(fā)者更好地用代碼直接表達(dá)現(xiàn)實(shí)中存在的對(duì)象,這樣開發(fā)代碼簡單并且易于維護(hù)。面向?qū)ο笳Z言有以下三個(gè)最重要的概念:
封裝(Encapsulation)- 強(qiáng)調(diào)隱藏對(duì)象的實(shí)現(xiàn)細(xì)節(jié),對(duì)象的使用者僅僅通過定義好的接口使用對(duì)象。
繼承(Inheritance)- 新的對(duì)象可以利用舊的對(duì)象的功能。
多態(tài)(Polymorphism)- 根據(jù)所使用的對(duì)象展現(xiàn)多種不同行為的能力。
而從程序的整體來看,面向?qū)ο缶幊烫峁┙o用戶的最重要的概念則是代碼的共享與重用,它對(duì)于提高編寫程序的效率極為重要。但是代碼的共享與重用一旦應(yīng)用于實(shí)踐中仍然存在種種問題,如版本的升級(jí)、接口的變化、在不同程序設(shè)計(jì)語言之間共享代碼等等。對(duì)于這些困難原有的面向?qū)ο蟪绦蛟O(shè)計(jì)方法并沒有相應(yīng)的答案,這就是組件對(duì)象模型提出的背景。
· 組件對(duì)象模型
將工程分解為邏輯組件是面向組件分析和設(shè)計(jì)的基礎(chǔ),這也是組件軟件的基礎(chǔ)。組件軟件系統(tǒng)由可重用的二進(jìn)制形式的軟件組件模塊組成,只需要相當(dāng)小的改動(dòng)就可以將這些來自不同開發(fā)商的組件模塊組合在一起。特別重要的是這樣的組合并不需要源代碼,也不需要重新編譯,組件之間通過基于二進(jìn)制的規(guī)范進(jìn)行通訊,這被稱為二進(jìn)制重用。組件模塊是獨(dú)立于編程語言的,使用組件的客戶程序和組件之間除了通過標(biāo)準(zhǔn)的方法進(jìn)行通訊以外,彼此不做任何限定。
組件可以劃分為不同的類型,包括可視化組件如按鈕或者列表框;功能組件如打印或者拼寫檢查。例如一個(gè)基于組件的架構(gòu)可以提供將多個(gè)開發(fā)商的拼寫檢查組件插入到另一個(gè)開發(fā)商的字處理應(yīng)用程序中的機(jī)制,這樣用戶可以根據(jù)自己的喜好方便地選擇和替換字處理軟件。
組件結(jié)構(gòu)中最重要的概念是接口。接口是集合在同一個(gè)名稱(通常是一個(gè)系統(tǒng)唯一的ID值)下的相關(guān)方法的的集合。組件之間的通訊是基于接口的,接口是組件和其客戶之間嚴(yán)格類型化的契約。實(shí)現(xiàn)相同接口的兩個(gè)對(duì)象就被認(rèn)為是多態(tài)的,這里的多態(tài)不包含諸如基類指針指向派生類對(duì)象的意義,這里是指同一個(gè)接口可以由多個(gè)對(duì)象以不同方法實(shí)現(xiàn)。
2. COM/DCOM的基本概念
· 概述:
以下將通過程序?qū)嵗忉孋OM/DCOM的基本概念。基于微軟的一貫作風(fēng),雖然COM/DCOM自稱為是一個(gè)可跨平臺(tái)支持異構(gòu)的模型(也確實(shí)從根本上說是可以跨平臺(tái)的),但它也是和MicrosoftWindows系統(tǒng)中的其它概念緊密結(jié)合在一起的,而且除了MicrosoftWindows系統(tǒng)以外很少有什么系統(tǒng)支持COM/DCOM,所以在以下概念的介紹中將基于MicrosoftWindows系統(tǒng)。COM/DCOM模型主要包括三方面的內(nèi)容:(A)程序編寫的模式。(B)程序交互時(shí)遵循的二進(jìn)制規(guī)范。(C)程序運(yùn)行的輔助環(huán)境。首先通過圖1描述COM/DCOM基本機(jī)制。
由圖可見COM/DCOM是基于客戶機(jī)和服務(wù)器模型的,客戶程序和組件程序是相對(duì)的,進(jìn)行功能請(qǐng)求調(diào)用的是客戶程序而響應(yīng)該請(qǐng)求的是組件程序。組件程序也可以作為客戶程序去調(diào)用其它的組件程序,正是這種角色的轉(zhuǎn)換和相互調(diào)用關(guān)系使組件程序最終構(gòu)成一個(gè)軟件系統(tǒng)。根據(jù)COM/DCOM中客戶程序和組件程序的交互關(guān)系可以將組件分為進(jìn)程內(nèi)組件和進(jìn)程外組件兩種。所謂進(jìn)程內(nèi)組件是指客戶程序和組件程序在同一個(gè)進(jìn)程地址空間內(nèi);進(jìn)程外組件指客戶程序和組件程序分別處在不同的進(jìn)程空間地址中。進(jìn)程內(nèi)組件是通過將組件作為動(dòng)態(tài)連接庫(DLL)來實(shí)現(xiàn)的,客戶程序?qū)⒔M件程序加載到自己的進(jìn)程地址空間后再調(diào)用組件程序的函數(shù)。對(duì)于這兩種不同的組件,客戶程序和組件程序交互的內(nèi)在方式是完全不同的。但是對(duì)于功能相同的進(jìn)程內(nèi)和進(jìn)程外組件,從程序編寫的角度看,客戶程序是以同樣的方法來使用組件程序的,客戶程序不需要做任何的修改。因此以下先通過進(jìn)程內(nèi)組件的實(shí)現(xiàn)來理解COM/DCOM的編程模式。
· 進(jìn)程內(nèi)組件:
例子程序:
以下是一個(gè)用C++語言編寫的COM程序?qū)嵗闹饕獌?nèi)容:
頭文件:component.h
interfaceDECLSPEC_UUID("10000001-0000-0000-0000-000000000001")
ISum : public IUnknown
{public:
virtual HRESULT STDMETHODCALLTYPE Sum( int x, int y, int__RPC_FAR *retval) = 0;
};
客戶程序:
#include "component.h"
const CLSID CLSID_InsideCOM = {0x10000002,0x0000,0x0000,
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}};
void main()
{
IUnknown* pUnknown;
ISum* pSum;
HRESULT hr = CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);
hr = CoCreateInstance(CLSID_InsideCOM, NULL,
CLSCTX_INPROC_SERVER, IID_IUnknown,(void**)&pUnknown);
hr = pUnknown->QueryInterface(IID_ISum,(void**)&pSum);
if(FAILED(hr))
cout << "IID_ISum not supported. " << endl;
pUnknown->Release();
int sum;
hr = pSum->Sum(2, 3, &sum);
if(SUCCEEDED(hr))cout << "Client: Calling Sum(2, 3) = "<< sum << endl;
pSum->Release();
CoUninitialize();
}
組件程序:
#include "component.h"
const CLSID CLSID_InsideCOM = {0x10000002,0x0000,0x0000,
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}};
class CInsideCOM : public ISum{
public:
// IUnknown
ULONG __stdcall AddRef();
ULONG __stdcall Release();
HRESULT __stdcall QueryInterface(REFIID riid, void**ppv);
// ISum
HRESULT __stdcall Sum(int x, int y, int* retval);
CInsideCOM() : m_cRef(1) {}
private:
ULONG m_cRef;
};
ULONG CInsideCOM::AddRef()
{ return ++m_cRef; }
ULONG CInsideCOM::Release()
{ if(--m_cRef != 0) return m_cRef;
delete this;
return 0;
}
HRESULT CInsideCOM::QueryInterface(REFIID riid, void** ppv)
{
if(riid == IID_IUnknown)
{ *ppv = (IUnknown*)this; }
else if(riid == IID_ISum){
*ppv = (ISum*)this; }
else {
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
HRESULT CInsideCOM::Sum(int x, int y, int* retval)
{ *retval = x + y;
return S_OK;
}
class CFactory : public IClassFactory
{
public:
// IUnknown
ULONG __stdcall AddRef();
ULONG __stdcall Release();
HRESULT __stdcall QueryInterface(REFIID riid, void**ppv);
// IClassFactory
HRESULT __stdcall CreateInstance(IUnknown *pUnknownOuter,REFIID riid, void** ppv);
CFactory() : m_cRef(1) { }
private:
ULONG m_cRef;
};
ULONG CFactory::AddRef()
{ return ++m_cRef; }
ULONG CFactory::Release()
{
if(--m_cRef != 0) return m_cRef;
delete this;
return 0;
}
HRESULT CFactory::QueryInterface(REFIID riid, void** ppv)
{
if(riid == IID_IUnknown)
{ *ppv = (IUnknown*)this; }
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
}
else{
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
HRESULT CFactory::CreateInstance(IUnknown *pUnknownOuter, REFIIDriid, void** ppv){
CInsideCOM *pInsideCOM = new CInsideCOM;
HRESULT hr = pInsideCOM->QueryInterface(riid, ppv);
return hr;
}
HRESULT __stdcall DllGetClassObject(REFCLSID clsid, REFIID riid,void** ppv){
if(clsid != CLSID_InsideCOM)
return CLASS_E_CLASSNOTAVAILABLE;
CFactory* pFactory = new CFactory;
if(pFactory == NULL)
return E_OUTOFMEMORY;
HRESULT hr = pFactory->QueryInterface(riid, ppv);
return hr;
}Top
由于COM/DCOM系統(tǒng)組件之間通訊是和位置無關(guān)的,也即一個(gè)使用組件功能的客戶程序在編寫時(shí)不需要考慮組件的位置,組件的定位和通訊由系統(tǒng)完成。因此不妨將客戶程序和組件程序分別加以分析。
客戶端程序:
?。?) 調(diào)用CoInitializeEx初始化。
因?yàn)槌绦虻暮芏噍o助功能是由庫函數(shù)和操作系統(tǒng)中的各種服務(wù)自動(dòng)完成的,如組件的定位和加載,并且這些工作很復(fù)雜,所以程序需要首先作一些初始化。
?。?)調(diào)用CoCreateInstance創(chuàng)建對(duì)象。
第1個(gè)參數(shù)CLSID_InsideCOM是一個(gè)128位的標(biāo)識(shí)-類標(biāo)識(shí)符(CLSID),在程序中定義為{0x10000002,0x0000, 0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}},今后這樣128位的標(biāo)識(shí)在表示時(shí)將省略0x并用"-"代替 ",";第4個(gè)參數(shù)IID_IUnknown也是一個(gè)128位的標(biāo)識(shí)-接口標(biāo)識(shí)符(IID);第5個(gè)參數(shù)(void**)&pUnknown是一個(gè)指針,在返回時(shí)它指向一個(gè)接口實(shí)例的指針。
CoCreateInstance()是一個(gè)庫函數(shù),從語義上說它創(chuàng)建對(duì)應(yīng)類標(biāo)識(shí)(CLSID)的一個(gè)COM/DCOM對(duì)象實(shí)例,并獲得該對(duì)象的一個(gè)接口實(shí)例指針。對(duì)于進(jìn)程內(nèi)組件一個(gè)COM/DCOM對(duì)象是在一個(gè)DLL中實(shí)現(xiàn)的,在Windows系統(tǒng)注冊(cè)表中維護(hù)著CLSID和DLL文件路徑的對(duì)應(yīng)關(guān)系,CoCreateInstance首先查找注冊(cè)表,然后加載對(duì)應(yīng)的DLL程序,調(diào)用該DLL的DllGetClaseObject引出函數(shù)(任何作為組件的DLL都必須提供該函數(shù))以及其他的一些操作創(chuàng)建一個(gè)對(duì)象實(shí)例。
一個(gè)COM/DCOM對(duì)象對(duì)于客戶程序唯一可見的是它所包含的一組接口,每一個(gè)接口都由128位的IID標(biāo)識(shí),在整個(gè)COM/DCOM系統(tǒng)中都是唯一的(包括分布在不同機(jī)器上的COM/DCOM系統(tǒng))。任何類型的COM/DCOM對(duì)象都必須支持IID_IUknown(標(biāo)識(shí)為00000000-0000-0000-C000-000000000046)接口。不同語言編寫的客戶程序中基本都有某種機(jī)制來標(biāo)識(shí)接口指針,COM/DCOM用對(duì)象的IID_IUnknown接口指針的值來區(qū)分對(duì)象而沒有獨(dú)立的對(duì)象引用,任何對(duì)象在生命期內(nèi)返回給客戶程序的IID_IUnknown接口指針值必須是相同的。注意COM/DCOM對(duì)其他的接口指針值沒有如上的要求。
COM/DCOM規(guī)定IID_IUnknown接口由以下三個(gè)函數(shù)組成:
QueryInterface(const IID iid, void **ppv);
AddRef( );
Release( );
而且其他任何接口也必須包含這三個(gè)函數(shù)。其中AddRef和Release是用來控制對(duì)象生存周期的。 QueryInter-face則達(dá)到通過接口標(biāo)識(shí)查詢對(duì)象實(shí)現(xiàn)的接口,COM/DCOM規(guī)定通過對(duì)象的任何接口的QueryInterface函數(shù)可以獲得同一個(gè)對(duì)象的其余接口指針。
?。?)pUnknown->QueryInterface(IID_ISum,(void**)&pSum);獲得接口標(biāo)識(shí)符為IID_Isum的另一個(gè)接口實(shí)例指針pSum。
?。?)hr = pSum->Sum(2, 3,&sum);通過接口實(shí)例指針pSum,調(diào)用接口的成員函數(shù)Sum。從編程模式的角度來看客戶程序向組件程序發(fā)送功能請(qǐng)求在源程序中最終體現(xiàn)為調(diào)用接口的一個(gè)成員函數(shù),并且實(shí)際上不論對(duì)于進(jìn)程內(nèi)組件還是對(duì)于進(jìn)程外組件都是同樣的。對(duì)于進(jìn)程內(nèi)組件這一調(diào)用就是通過調(diào)用同一個(gè)進(jìn)程中的函數(shù)實(shí)現(xiàn)的,但是必須強(qiáng)調(diào)即使是進(jìn)程內(nèi)的調(diào)用遵循的仍然是二進(jìn)制的規(guī)范,也即客戶程序中的pSum指向的內(nèi)存格式必須滿足COM/DCOM的規(guī)范,至于這一規(guī)范是怎樣的將在后文中講述。對(duì)于一個(gè)確定的接口(確定值的IID),它的進(jìn)程內(nèi)組件不論用什么程序設(shè)計(jì)語言實(shí)現(xiàn),生成的目標(biāo)DLL返回給客戶程序的接口指針?biāo)赶虻膬?nèi)存格式都是一樣的。
?。?)CoUninitialize();調(diào)用清理函數(shù)。
通過以上對(duì)客戶程序的分析,可見客戶程序的編程模式為(a)創(chuàng)建對(duì)應(yīng)CLSID的對(duì)象實(shí)例;(b)獲得對(duì)象的初始接口指針;(c)通過接口指針的QueryInterfase函數(shù)查詢其它接口指針;(d)通過接口指針調(diào)用接口的函數(shù)。(e)通過接口的AddRef()和Release()控制對(duì)象的生命期??蛻舫绦蚝瓦M(jìn)程內(nèi)組件程序遵循的二進(jìn)制規(guī)范則體現(xiàn)在(a)128位的類表識(shí)CLSID和接口表識(shí)IID;(b)組件程序必須是一個(gè)合法的DLL,并且引出若干標(biāo)準(zhǔn)的函數(shù)如DellGetClassObject。(c)組件程序返回給客戶程序的接口指針?biāo)赶虻膬?nèi)存必須滿足COM/DCOM規(guī)范。
組件程序:
現(xiàn)在分析一下組件程序的編寫,了解對(duì)象是如何實(shí)現(xiàn)的。首先察看DllGetClassObject(const CLSID clsid,const IID id, (void**)ppv)函數(shù),當(dāng)客戶程序加載該DLL后將首先調(diào)用該引出函數(shù)。該函數(shù)是一個(gè)進(jìn)程內(nèi)組件提供其服務(wù)的最基本的入口,也是進(jìn)程內(nèi)組件所遵循的二進(jìn)制規(guī)范的一部分。DllGetClassObject的功能是根據(jù)CLSID判斷本組件是否支持該類型的對(duì)象,一個(gè)組件可以支持多種類型的對(duì)象。DllGetClassObject根據(jù)CLSID生成對(duì)應(yīng)的類廠對(duì)象,并根據(jù)輸入?yún)?shù)constIIDid將類廠對(duì)象的對(duì)應(yīng)接口指針通過ppv返回給客戶程序。這里的引入了類廠這個(gè)在客戶程序中未提及的新概念。根據(jù)COM/DCOM規(guī)范,組件程序必須為自己支持的每個(gè)CLSID提供類廠對(duì)象,由類廠對(duì)象負(fù)責(zé)創(chuàng)建對(duì)應(yīng)類型的COM/DCOM對(duì)象實(shí)例。類廠對(duì)象提供通常稱為IID_IClassFactory的接口(其值為000001-0000-0000-C000-000000000046),客戶程序通過調(diào)用該接口的CreateInstance(Iunknown*pUnknown-Outer, const IID iid, void**ppv)函數(shù)真正創(chuàng)建對(duì)象實(shí)例并獲得對(duì)象的第一個(gè)接口指針。由此可見客戶程序調(diào)用CoCreateInstance庫函數(shù)實(shí)際完成了兩個(gè)步驟的工作,它首先請(qǐng)求組件創(chuàng)建類廠對(duì)象,然后又通過類廠對(duì)象創(chuàng)建對(duì)應(yīng)CLSID的對(duì)象實(shí)例。
上面組件程序的實(shí)例中,在DllGetClassObject函數(shù)中通過newCfactory創(chuàng)建了類廠對(duì)象,在CFactory:: CreateInstance 中通過newCInsideCOM創(chuàng)建類對(duì)象。COM/DCOM對(duì)象是以C++對(duì)象的形式實(shí)現(xiàn)的,接口指針是以C++中的對(duì)象指針的形式返回的。對(duì)于進(jìn)程內(nèi)組件,組件和客戶程序在編寫時(shí)是分別進(jìn)行的,而在運(yùn)行時(shí)處于同一個(gè)地址空間內(nèi)又以指針的方式進(jìn)行交互,那么交互的二進(jìn)制兼容自然是基于內(nèi)存格式的。
在了解進(jìn)程內(nèi)組件的編寫后,讀者最大的疑惑必然是如何確保客戶程序和組件程序在彼此獨(dú)立的編寫的過程中(甚至使用不同的語言)如何確保二進(jìn)制兼容的。如前所述對(duì)于進(jìn)程內(nèi)組件二進(jìn)制兼容包含三個(gè)方面的內(nèi)容,128位標(biāo)識(shí)符的識(shí)別以及確保組件DLL程序的合法性是很容易做到的,而如何確保在基于內(nèi)存的交互時(shí)接口指針?biāo)赶虻膬?nèi)存格式符合規(guī)范則顯得有些復(fù)雜。下一節(jié)將介紹IDL語言,它是解決以上問題的重要手段。
3. IDL語言
在上面的例子程序中,不論是客戶程序還是組件程序都沒有使用任何的輔助手段就達(dá)到了COM/DCOM所要求的二進(jìn)制的規(guī)范。不難想象:符合一定結(jié)構(gòu)的一般C++程序經(jīng)過編譯后生成的二進(jìn)制代碼是滿足COM/DCOM二進(jìn)制規(guī)范的。同樣不難想象:為了達(dá)到符合COM/DCOM的二進(jìn)制規(guī)范,一種簡單的方法就是對(duì)于生成目標(biāo)代碼的源程序的格式作一定的限制,對(duì)于例子中的C++程序顯然只要對(duì)實(shí)現(xiàn)對(duì)象的C++類定義作限制就可以了。同時(shí)考慮到COM/DCOM是和編程語言無關(guān)的,使用C++的頭文件顯然是行不通的,因此必須使用一種獨(dú)立的語言來描述接口,微軟選用的語言就是IDL。
IDL語言是開放軟件基金會(huì)(OSF)為分布式計(jì)算環(huán)境RPC軟件包開發(fā)的,IDL幫助RPC程序員保證工程的客戶機(jī)和服務(wù)器都遵守同一接口。為了將IDL語言應(yīng)用于COM/DCOM系統(tǒng)中,微軟對(duì)IDL語言的語法進(jìn)行了擴(kuò)充。IDL本身不是一種編程語言,它是用來定義接口的一種工具,至于對(duì)IDL語言的解釋由使用它的系統(tǒng)決定。COM/DCOM對(duì)IDL語言的解釋和COM/DCOM的二進(jìn)制規(guī)范密切相關(guān),而這樣的解釋和其它利用IDL的系統(tǒng)毫無關(guān)系。
COM/DCOM通常并不直接將IDL語言定義的接口翻譯成二進(jìn)制代碼。C++語言的用戶使用微軟提供的MIDL.EXE程序可將IDL語言翻譯成對(duì)應(yīng)的C++頭文件,上面例子程序中的頭文件就是由以下的IDL文件生成的,接口ISum繼承了接口IUnknown。接口定義文件精確地描述了接口所包含的函數(shù),函數(shù)的參數(shù)及參數(shù)的類型。
import "unknwn.idl";
[ object, uuid(10000001-0000-0000-0000-000000000001) ]
interface ISum : IUnknown{
HRESULT Sum([in] int x, [in] int y, [out, retval] int*retval);

};
其中unknwn.idl是系統(tǒng)預(yù)定義的,其內(nèi)容如下:
[local,
object,
uuid(00000000-0000-0000-C000-000000000046),
]
interface IUnknown{
HRESULT QueryInterface([in] REFIID riid, [out,iid_is(riid)] void **ppvObject);
ULONG AddRef();
ULONG Release();
}
由IDL生成的C++頭文件在客戶程序和組件程序中分別通過#include被包含。使用由MIDL.EXE翻譯而成的C++頭文件一方面確??蛻舫绦蚝徒M件程序中接口指針?biāo)傅膬?nèi)存結(jié)構(gòu)一致,解決了同一編程語言實(shí)現(xiàn)的組件間的互操作性;另一方面也確保了接口指針?biāo)傅膬?nèi)存結(jié)構(gòu)符合COM/DCOM的二進(jìn)制規(guī)范,解決不同編程語言實(shí)現(xiàn)的組件間的互操作性。不過由于微軟的MIDL.EXE沒有通過IDL文件直接生成其它語言(如VB,JAVA)相應(yīng)頭文件的功能,這些語言的用戶需要其它的工具才能利用IDL,這里不作敘述。
4.組件對(duì)象的繼承
COM/DCOM模型作為組件對(duì)象模型具有對(duì)象模型的基本特性,其中對(duì)象的封裝性,多態(tài)性前已做過論述。但對(duì)象模型的另一個(gè)重要特性---繼承性---還沒有涉及。COM/DCOM通過包容和聚合提供類似的特性。
包容和聚合有一個(gè)共同的特點(diǎn)就是對(duì)象包容和聚合后必須使客戶相信那是一個(gè)對(duì)象。如前所述,客戶程序區(qū)別對(duì)象的唯一標(biāo)志是對(duì)象的IID_IUnknown接口指針的值,而且通過同一對(duì)象的接口的QueryInterface函數(shù)必須能夠查詢到本對(duì)象的其它接口。圖2表示包容和聚合的實(shí)現(xiàn),對(duì)象B實(shí)現(xiàn)接口IID_IB和IID_ISum,其中IID_Isum的功能是通過創(chuàng)建另一個(gè)CLSID_InsideCOM類型的對(duì)象完成的。
如圖所示當(dāng)采用包容模式時(shí),對(duì)象B簡單地創(chuàng)建一個(gè)CLSID_InsideCom對(duì)象,客戶程序所有對(duì)對(duì)象B接口IID_ISum的調(diào)用都可以利用CLSID_InsideCOM對(duì)象的IID_ISum接口完成。當(dāng)采用聚合模式時(shí),對(duì)象B仍然創(chuàng)建一個(gè)CLSID_InsideCom對(duì)象,但對(duì)象B自己并不實(shí)現(xiàn)Top
愛華網(wǎng)



