TN038:MFC/OLE IUnknown 實作
注意事項 |
---|
下列技術提示自其納入線上文件以來,未曾更新。因此,有些程序和主題可能已過期或不正確。如需最新資訊,建議您在線上文件索引中搜尋相關的主題。 |
在 OLE 2 的中心是「OLE 元件物件模型」或 COM。 COM 定義合作物件彼此通訊的標準。 這包括「物件」外觀的詳細資料,包括如何在物件上分派方法。 COM 也定義衍生所有 COM 相容類別的基底類別。 這個基底類別是 IUnknown。 雖然 IUnknown 介面稱為 C ++ 類別,但 COM 並非特定語言專用—在 C、Pascal 或任何可以支援 COM 物件的二進位配置中皆可實作。
OLE 會參考所有衍生自 IUnknown 的類別做為「介面」。這是重要的區別,因為「介面」(例如 IUnknown) 本身不帶有實作。 這簡單定義了物件藉以進行通訊的通訊協定,而非這些實作的作業細節。 這對於允許最大彈性的系統很合理。 實作 MFC/C++ 程式的預設行為是 MFC 的工作。
若要了解 IUnknown 的 MFC 實作,必須先了解這個介面是什麼。 IUnknown 的簡化版本定義如下:
class IUnknown
{
public:
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
virtual ULONG AddRef() = 0;
virtual ULONG Release() = 0;
};
注意事項 |
---|
此範例省略某些必要的呼叫慣例詳細資料,例如 __stdcall 。 |
AddRef 和 Release 成員函式會控制物件的記憶體管理。 COM 使用參考計數配置追蹤物件。 不可像使用 C++ 時直接參考物件。 相反地,COM 物件永遠是透過指標來參考。 若要在擁有者完成使用時釋放物件,呼叫物件的 Release 成員 (而不像傳統 C++ 物件使用 operator delete)。 參考計數機制允許單一物件的多個參考為 Managed。 AddRef 和 版本 的實作會維持物件的參考計數,在參考計數達到零之前,不會刪除該物件。
從實作的角度來看,AddRef 和 Release 相當簡單明暸。 以下是一般實作:
ULONG CMyObj::AddRef()
{
return ++m_dwRef;
}
ULONG CMyObj::Release()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
QueryInterface 成員函式會比較有趣一點。 僅有的成員函式為 AddRef 和 Release 的物件並不值得注意,最好是告訴物件要執行的不只是 IUnknown 提供的動作而已。 這是 QueryInterface 有用的地方。 這可讓您取得相同物件上的不同「介面」。 這些介面通常衍生自 IUnknown,而且可透過加入新成員函式來加入其他功能。 COM 介面不會在介面中宣告成員變數,而會將所有成員函式宣告為純虛擬。 例如:
class IPrintInterface : public IUnknown
{
public:
virtual void PrintObject() = 0;
};
若要在只有 IUnknown 時取得 IPrintInterface,請使用 IPrintInterface 的 IID,呼叫 QueryInterface。 IID 是唯一識別該介面的 128 位元數字。 您或 OLE 定義的每個介面有 IID。 如果 pUnk 是指向 IUnknown 物件的指標,則從其中擷取 IPrintInterface 的程式碼可能是:
IPrintInterface* pPrint = NULL;
if (pUnk->QueryInterface(IID_IPrintInterface,
(void**)&pPrint) == NOERROR)
{
pPrint->PrintObject();
pPrint->Release();
// release pointer obtained via QueryInterface
}
這似乎很容易,但是該如何實作支援 IPrintInterface 和 IUnknown 介面的物件? 在此情況下,這會很簡單,因為 IPrintInterface 直接衍生自 IUnknown,只要實作 IPrintInterface,就會自動支援 IUnknown。 例如:
class CPrintObj : public CPrintInterface
{
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
virtual ULONG AddRef();
virtual ULONG Release();
virtual void PrintObject();
};
AddRef 和 Release 的實作與上述實作的介面完全相同。 CPrintObj::QueryInterface 看起來可能如下:
HRESULT CPrintObj::QueryInterface(REFIID iid, void FAR* FAR* ppvObj)
{
if (iid == IID_IUnknown || iid == IID_IPrintInterface)
{
*ppvObj = this;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
如您所見,如果可以辨識介面識別項 (IID),會將指標傳回至物件;否則會發生錯誤。 同時也請注意成功的 QueryInterface 會產生隱含的 AddRef。 當然,您也必須實作 CEditObj::Print。 這只是因為 IPrintInterface 直接衍生自 IUnknown 介面。 不過,如果您想要支援兩個不同的介面,且兩者都是衍生自 IUnknown,請考慮下列方式:
class IEditInterface : public IUnkown
{
public:
virtual void EditObject() = 0;
};
雖然有許多不同的方式實作支援 IEditInterface 和 IPrintInterface 的類別,包括使用 C++ 多重繼承,但本注意事項所強調的是使用巢狀類別實作這項功能。
class CEditPrintObj
{
public:
CEditPrintObj();
HRESULT QueryInterface(REFIID iid, void**);
ULONG AddRef();
ULONG Release();
DWORD m_dwRef;
class CPrintObj : public IPrintInterface
{
public:
CEditPrintObj* m_pParent;
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj);
virtual ULONG AddRef();
virtual ULONG Release();
} m_printObj;
class CEditObj : public IEditInterface
{
public:
CEditPrintObj* m_pParent;
virtual ULONG QueryInterface(REFIID iid, void** ppvObj);
virtual ULONG AddRef();
virtual ULONG Release();
} m_editObj;
};
以下包含整個實作:
CEditPrintObj::CEditPrintObj()
{
m_editObj.m_pParent = this;
m_printObj.m_pParent = this;
}
ULONG CEditPrintObj::AddRef()
{
return ++m_dwRef;
}
CEditPrintObj::Release()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
HRESULT CEditPrintObj::QueryInterface(REFIID iid, void** ppvObj)
{
if (iid == IID_IUnknown || iid == IID_IPrintInterface)
{
*ppvObj = &m_printObj;
AddRef();
return NOERROR;
}
else if (iid == IID_IEditInterface)
{
*ppvObj = &m_editObj;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
ULONG CEditPrintObj::CEditObj::AddRef()
{
return m_pParent->AddRef();
}
ULONG CEditPrintObj::CEditObj::Release()
{
return m_pParent->Release();
}
HRESULT CEditPrintObj::CEditObj::QueryInterface(
REFIID iid, void** ppvObj)
{
return m_pParent->QueryInterface(iid, ppvObj);
}
ULONG CEditPrintObj::CPrintObj::AddRef()
{
return m_pParent->AddRef();
}
ULONG CEditPrintObj::CPrintObj::Release()
{
return m_pParent->Release();
}
HRESULT CEditPrintObj::CPrintObj::QueryInterface(
REFIID iid, void** ppvObj)
{
return m_pParent->QueryInterface(iid, ppvObj);
}
請注意,大部分 IUnknown 實作是放入 CEditPrintObj 類別,而不是複製 CEditPrintObj::CEditObj 和 CEditPrintObj::CPrintObj 中的程式碼。 這減少程式碼並避免錯誤。 這裡的重點是,從 IUnknown 介面呼叫 QueryInterface 擷取物件可能支援的任何介面是可行的,而從上述每個介面進行呼叫也可以達到同樣的目的。 這表示從每個介面的所有 QueryInterface 函式都必須具有相同的行為。 為了讓這些內嵌物件可以呼叫「外部物件」中的實作,請使用返回指標 (m_pParent)。 m_pParent 指標已在 CEditPrintObj 建構函式期間初始化。 然後您也會實作 CEditPrintObj::CPrintObj::PrintObject 和 CEditPrintObj::CEditObj::EditObject。 加入了不少程式碼來新增一項功能,即編輯物件的功能。 幸好介面只有一個成員函式的情況並不常見 (雖然確實會發生),在此情況下,EditObject 和 PrintObject 通常會結合為單一介面。
要了解這類簡單情節,還需要許多解釋和許多程式碼。 MFC/OLE 類別提供更簡單的替代方式。 MFC 實作會使用類似以訊息對應包裝訊息之方式的技術。 這種機制稱為介面對應,將在下一節討論。
MFC 介面對應
MFC/OLE 是在概念和執行上類似於 MFC 之「訊息對應」和「分派對應」的「介面對應」實作。 MFC 介面對應的核心功能如下:
IUnknown的標準實作,內建於 CCmdTarget 類別。
QueryInterface 的資料驅動型實作
此外,介面對應還支援下列進階功能:
支援建立可彙總的 COM 物件
支援在 COM 物件的實作中使用彙總物件
實作是可攔截且可擴充
如需彙總的詳細資訊,請參閱彙總主題。
MFC 的介面對應支援根源於 CCmdTarget 類別。 CCmdTarget 「has-a」會參考計數以及所有與 IUnknown 實作相關的成員函式 (例如 CCmdTarget 中的參考計數)。 若要建立支援 OLE COM 的類別,從 CCmdTarget 類別衍生並使用各種巨集以及 CCmdTarget 的成員函式,以實作所需的介面。 MFC 的實作使用巢狀類別來定義每個介面的實作,很像上面的範例。 這是以 IUnknown 的標準實作以及一些消除某些重複程式碼的巨集變得更容易。
若要使用 MFC 的介面對應來實作類別
從 CCmdTarget 直接或間接衍生類別。
使用衍生類別定義中的 DECLARE_INTERFACE_MAP 函式。
針對您要支援的每個介面,在類別定義中使用 BEGIN_INTERFACE_PART 和 END_INTERFACE_PART 巨集。
在實作檔案中,使用 BEGIN_INTERFACE_MAP 和 END_INTERFACE_MAP 巨集來定義類別的介面對應。
針對支援的每個 IID,在 BEGIN_INTERFACE_MAP 和 END_INTERFACE_MAP 巨集之間使用 INTERFACE_PART 巨集將該 IID 對應至類別的特定「部分」。
實作表示您所支援之介面的每個巢狀類別。
使用 METHOD_PROLOGUE 巨集存取父代 CCmdTarget 衍生物件。
AddRef、 Release和 QueryInterface 可以委派至 CCmdTarget (ExternalAddRef、 ExternalRelease和 ExternalQueryInterface 函式的實作。
上述 CPrintEditObj 範例可以依照下列方式實作:
class CPrintEditObj : public CCmdTarget
{
public:
// member data and member functions for CPrintEditObj go here
// Interface Maps
protected:
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(EditObj, IEditInterface)
STDMETHOD_(void, EditObject)();
END_INTERFACE_PART(EditObj)
BEGIN_INTERFACE_PART(PrintObj, IPrintInterface)
STDMETHOD_(void, PrintObject)();
END_INTERFACE_PART(PrintObj)
};
上述宣告會建立衍生自 CCmdTarget 的類別。 DECLARE_INTERFACE_MAP 巨集告知架構,這個類別會有自訂介面對應。 此外,BEGIN_INTERFACE_PART 和 END_INTERFACE_PART 巨集還會定義巢狀類別,在這種情況下,名稱為 CEditObj 和 CPrintObj (X 只是用來區別開頭為 "C" 的全域類別和開頭為 "Me" 的介面類別)。 分別建立這些類別的兩個巢狀成員:m_CEditObj 和 m_CPrintObj。 巨集會自動宣告 AddRef、Release 和 QueryInterface 函式,因此您只需宣告此介面特定的函式:EditObject 和 PrintObject (會使用 OLE 巨集 STDMETHOD 以便針對目標平台提供適當的 _stdcall 和 virtual 關鍵字)。
若要實作此類別的介面對應:
BEGIN_INTERFACE_MAP(CPrintEditObj, CCmdTarget)
INTERFACE_PART(CPrintEditObj, IID_IPrintInterface, PrintObj)
INTERFACE_PART(CPrintEditObj, IID_IEditInterface, EditObj)
END_INTERFACE_MAP()
這會分別將 IID_IPrintInterface IID 連接至 m_CPrintObj,而將 IID_IEditInterface 連接至 m_CEditObj。 QueryInterface (CCmdTarget::ExternalQueryInterface) 的 CCmdTarget 實作會在要求時使用此對應傳回 m_CPrintObj 和 m_CEditObj 的指標。 您不需要包含 IID_IUnknown 的項目。要求 IID_IUnknown 時,Framework 將會使用對應中的第一個介面 (在此情況為 m_CPrintObj)。
即使 BEGIN_INTERFACE_PART 巨集會自動宣告 AddRef、Release 和 QueryInterface 函式,您還是需要實作它們:
ULONG FAR EXPORT CEditPrintObj::XEditObj::AddRef()
{
METHOD_PROLOGUE(CEditPrintObj, EditObj)
return pThis->ExternalAddRef();
}
ULONG FAR EXPORT CEditPrintObj::XEditObj::Release()
{
METHOD_PROLOGUE(CEditPrintObj, EditObj)
return pThis->ExternalRelease();
}
HRESULT FAR EXPORT CEditPrintObj::XEditObj::QueryInterface(
REFIID iid, void FAR* FAR* ppvObj)
{
METHOD_PROLOGUE(CEditPrintObj, EditObj)
return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}
void FAR EXPORT CEditPrintObj::XEditObj::EditObject()
{
METHOD_PROLOGUE(CEditPrintObj, EditObj)
// code to "Edit" the object, whatever that means...
}
CEditPrintObj::CPrintObj 的實作,類似於上述的 CEditPrintObj::CEditObj 定義。 雖然可以建立巨集用於自動產生這些函式 (但之前在 MFC/OLE 開發中可以),但當巨集產生一行以上的程式碼時,設定中斷點會變得困難。 因此,這個程式碼為手動展開。
使用訊息對應架構實作時不需執行許多步驟:
實作 QueryInterface
實作 AddRef 和 Release
在您的兩個介面上宣告這些內建方法的其中一種。
此外,這個架構會在內部使用訊息對應。 這可讓您從 Framework 類別衍生,例如 COleServerDoc,這個類別已支援特定介面並取代或補充 Framework 提供的介面。 因為 Framework 完全支援從基底類別繼承介面對應,您可以這麼做。 這就是 BEGIN_INTERFACE_MAP 接受基底類別名稱做為其第二個參數的原因。
注意事項 |
---|
通常無法透過從 MFC 版本繼承該介面的內嵌特製化,重複使用 OLE 介面之 MFC 內建實作的實作。這不是可行的,因為使用 METHOD_PROLOGUE 巨集存取包含 CCmdTarget 衍生物件,表示內嵌物件是從 CCmdTarget 衍生物件固定位移。這表示,例如,您無法從 COleClientItem::XAdviseSink 中的 MFC 實作衍生內嵌 XMyAdviseSink,因為 XAdviseSink 依賴位在 COleClientItem 物件頂端的特定位移。 |
注意事項 |
---|
不過,您可以針對需要 MFC 預設行為的所有函式,委派到 MFC 實作。這是在 COleFrameHook 類別 (它會委派至許多函式的 m_xOleInPlaceUIWindow) 的 IOleInPlaceFrame (XOleInPlaceFrame) MFC 實作中完成。選取這個設計以減少實作許多介面的物件的執行階段大小;它不需要返回指標 (例如上一節使用 m_pParent 的方式)。 |
彙總和介面對應
除了支援獨立 COM 物件之外,MFC 也支援彙總。 彙總本身過於複雜;請參閱彙總 主題中關於彙總的詳細資訊。 這個附註會描述對 Framework 和介面對應內建彙總的支援。
有兩種方式使用彙總:(1) 使用支援彙總的 COM 物件和 (2) 實作可由其他彙總的物件。 這些功能被稱為「使用彙總物件」和「使物件可彙總」。 MFC 支援兩者。
使用彙總的物件
若要使用彙總物件,需要想辦法繫結彙總至 QueryInterface 機制。 換句話說,彙總物件必須表現得就像是您物件的原生部分一般。 因此這是如何連繫於 MFC 的介面對應機制中? 除了將巢狀物件對應至 IID 的 INTERFACE_PART 巨集之外,您也可以宣告彙總物件做為 CCmdTarget 衍生類別的一部分。 若要這麼做,使用 INTERFACE_AGGREGATE 巨集。 這可讓您指定要整合到介面對應機制的成員變數 (必須是 IUnknown 或衍生類別的指標)。 如果呼叫 CCmdTarget::ExternalQueryInterface 時指標不是 NULL,則架構會自動呼叫彙總物件的 QueryInterface 成員函式 (如果要求的 IID 不是 CCmdTarget 物件本身所支援的其中一個原生 IID)。
若要使用 INTERFACE_AGGREGATE 巨集
宣告成員變數 (IUnknown*),其中將包含彙總物件的指標。
在依據名稱參考成員變數的介面對應中加入 INTERFACE_AGGREGATE 巨集。
在某些情況下 (通常在 CCmdTarget::OnCreateAggregates期間),將成員變數初始化為 NULL 以外的值。
例如:
class CAggrExample : public CCmdTarget
{
public:
CAggrExample();
protected:
LPUNKNOWN m_lpAggrInner;
virtual BOOL OnCreateAggregates();
DECLARE_INTERFACE_MAP()
// "native" interface part macros may be used here
};
CAggrExample::CAggrExample()
{
m_lpAggrInner = NULL;
}
BOOL CAggrExample::OnCreateAggregates()
{
// wire up aggregate with correct controlling unknown
m_lpAggrInner = CoCreateInstance(CLSID_Example,
GetControllingUnknown(), CLSCTX_INPROC_SERVER,
IID_IUnknown, (LPVOID*)&m_lpAggrInner);
if (m_lpAggrInner == NULL)
return FALSE;
// optionally, create other aggregate objects here
return TRUE;
}
BEGIN_INTERFACE_MAP(CAggrExample, CCmdTarget)
// native "INTERFACE_PART" entries go here
INTERFACE_AGGREGATE(CAggrExample, m_lpAggrInner)
END_INTERFACE_MAP()
m_lpAggrInner 變數會在建構函式中初始化為 NULL。 Framework 會忽略 QueryInterface 預設實作中的 NULL 成員變數。 OnCreateAggregates 是實際建立彙總物件的好位置。 如果您在 COleObjectFactory 的 MFC 實作之外建立物件,您必須明確地呼叫它。 當討論建立可彙總物件時,之所以在 CCmdTarget::OnCreateAggregates 建立彙總以及使用 CCmdTarget::GetControllingUnknown 的原因都變得顯而易見。
這個技術會將彙總物件支援的所有介面提供給您的物件,加上其原生介面。 如果您只想要彙總支援的介面子集,可以覆寫 CCmdTarget::GetInterfaceHook。 這啟用非常低層級的攔截能力 (hookability),類似於 QueryInterface。 通常,您想要彙總支援的所有介面。
讓物件實作可以彙總
為了能夠彙總物件,AddRef、Release 和 QueryInterface 的實作必須委派至 "controlling unknown"。換句話說,為了成為物件的一部分,它必須將 AddRef、Release 和 QueryInterface 委派給同樣衍生自 IUnknown 的不同物件。 這個 "controlling unknown" 在物件建立時提供給物件,也就是提供給 COleObjectFactory 的實作。 實作此物件會增加少量的額外負荷,在某些情況下並不適當,因此 MFC 會做此選擇。 若要將物件設為可彙總,您必須從物件的建構函式呼叫 CCmdTarget::EnableAggregation。
如果物件也使用彙總,則您同樣必須傳遞正確的 "controlling unknown" 至彙總物件。 通常當彙總建立時,傳遞這個 IUnknown 指標給物件。 例如,pUnkOuter 參數是使用 CoCreateInstance 所建立物件的 "controlling unknown"。 正確的 "controlling unknown" 指標可以透過呼叫 CCmdTarget::GetControllingUnknown來擷取。 然而,從該函式傳回的值在建構函式期間是無效的。 因此,建議您只在 CCmdTarget::OnCreateAggregates 覆寫中建立彙總,其中來自 GetControllingUnknown 的傳回值是可靠的,即使是從 COleObjectFactory 實作所建立。
加入或釋放假造的參考計數時,物件也必須管理正確的參考計數。 若要確保此情況,一定要呼叫 ExternalAddRef 和 ExternalRelease,而非 InternalRelease 和 InternalAddRef。 在支援彙總的類別上呼叫 InternalRelease 或 InternalAddRef,很少見。
參考資料
OLE 進階使用方式,例如定義自己的介面或覆寫 OLE 介面的框架的實作,都需要使用基底介面對應機制。
本節討論用來實作這些進階功能的每一個巨集和應用程式開發介面。
CCmdTarget::EnableAggregation — 函式描述
void EnableAggregation();
備註
如果要支援此類型的物件 OLE 彙總,請在衍生類別的建構函式中呼叫這個函式。 這會準備可彙總物件所需的特殊 IUnknown 實作。
CCmdTarget::ExternalQueryInterface — 函式描述
DWORD ExternalQueryInterface(
const void FAR* lpIID,
LPVOID FAR* ppvObj
);
備註
參數
lpIID
IID 的遠端指標 (QueryInterface 的第一個引數)ppvObj
IUnknown* 的指標 (QueryInterface 的第二個引數)
備註
每次您的類別實作時,在每個介面的 IUnknown 實作呼叫這個函式。 這個函式會根據您物件的介面對應,提供 QueryInterface 的標準資料驅動型實作。 將傳回值轉換為 HRESULT 是必要的。 如果物件已彙總,則這個函式會呼叫 "controlling IUnknown",而不會使用本機介面對應。
CCmdTarget::ExternalAddRef — 函式描述
DWORD ExternalAddRef();
備註
每次您的類別實作時,在每個介面的 IUnknown::AddRef 實作呼叫這個函式。 傳回值是 CCmdTarget 物件上的新參考計數。 如果物件已彙總,則這個函式會呼叫 "controlling IUnknown",而不會管理本機參照計數。
CCmdTarget::ExternalRelease — 函式描述
DWORD ExternalRelease();
備註
每次您的類別實作時,在每個介面的 IUnknown::Release 實作呼叫這個函式。 傳回值表示物件的新參考計數。 如果物件已彙總,則這個函式會呼叫 "controlling IUnknown",而不會管理本機參照計數。
DECLARE_INTERFACE_MAP — 巨集描述
DECLARE_INTERFACE_MAP
備註
在將具有介面對應的 CCmdTarget 衍生的任何類別中,使用這個巨集。 使用方式與 DECLARE_MESSAGE_MAP 大致相同。 應該將這個巨集引動過程放在類別定義中,通常在標頭 (.H) 檔案。 有 DECLARE_INTERFACE_MAP 的類別必須在有 BEGIN_INTERFACE_MAP 和 END_INTERFACE_MAP 巨集的實作檔 (.CPP) 中定義介面對應。
BEGIN_INTERFACE_PART 和 END_INTERFACE_PART —巨集描述
BEGIN_INTERFACE_PART(
localClass,
iface
);
END_INTERFACE_PART(
localClass
)
備註
參數
localClass
實作介面的類別名稱iface
這個類別實作的介面名稱
備註
您的類別將實作的每個介面都必須有 BEGIN_INTERFACE_PART 和 END_INTERFACE_PART 配對。 這些巨集定義從您定義的 OLE 介面衍生的區域類別以及該類別的內嵌成員變數。 AddRef、Release 和 QueryInterface 成員是自動宣告的。 您必須包含其他成員函式 (是實作介面的一部分) 的宣告 (這些宣告是放置在 BEGIN_INTERFACE_PART 和 END_INTERFACE_PART 巨集之間)。
iface 引數是您要實作的 OLE 介面,例如 IAdviseSink 或 IPersistStorage (或您的自訂介面)。
localClass 引數是要定義之區域類別的名稱。 「X」將自動會附加到名稱。 這個命名慣例是用來避免與相同名稱的全域類別發生衝突。 此外,內嵌成員名稱,與 localClass 名稱相同,但不同是,它會加上 "m_x" 前置詞。
例如:
BEGIN_INTERFACE_PART(MyAdviseSink, IAdviseSink)
STDMETHOD_(void,OnDataChange)(LPFORMATETC, LPSTGMEDIUM);
STDMETHOD_(void,OnViewChange)(DWORD, LONG);
STDMETHOD_(void,OnRename)(LPMONIKER);
STDMETHOD_(void,OnSave)();
STDMETHOD_(void,OnClose)();
END_INTERFACE_PART(MyAdviseSink)
會定義衍生自 IAdviseSink、名為 XMyAdviseSink 的區域類別,及它在其中宣告、名為 m_xMyAdviseSink.Note 的類別的成員。注意:
注意事項 |
---|
以 STDMETHOD_ 開頭的行基本上是從 OLE2.H 複製並稍加修改。從 OLE2.H 複製可以減少不易解決的錯誤。 |
BEGIN_INTERFACE_MAP 和 END_INTERFACE_MAP —巨集描述
BEGIN_INTERFACE_MAP(
theClass,
baseClass
)
END_INTERFACE_MAP
備註
參數
theClass
要定義介面對應的類別baseClass
theClass 衍生自的類別。
備註
BEGIN_INTERFACE_MAP 和 END_INTERFACE_MAP 巨集會在實作檔案中用來實際定義介面對應。 每個實作的介面都有一個或多個 INTERFACE_PART 巨集引動過程。 類別所使用的每個彙總都有一個 INTERFACE_AGGREGATE 巨集引動過程。
INTERFACE_PART — 巨集描述
INTERFACE_PART(
theClass,
iid,
localClass
)
備註
參數
theClass
包含介面對應的類別名稱。iid
要對應至內嵌類別的 IID。localClass
本機類別的名稱 (去掉 'X')。
備註
對於您的物件將支援的每個介面,在 BEGIN_INTERFACE_MAP 巨集和 END_INTERFACE_MAP 巨集之間使用這個巨集。 這可讓您將 IID 對應至 theClass 和 localClass 所表示之類別的成員。 'm_x' 將會自動加入至 localClass。 請注意,可以將多個 IID 與單一成員產生關聯。 當您只實作最具衍生性的介面,同時希望提供所有中繼介面時,這是非常有用的。 範例之一是 IOleInPlaceFrameWindow 介面。 其階層架構如下所示:
IUnknown
IOleWindow
IOleUIWindow
IOleInPlaceFrameWindow
如果物件實作 IOleInPlaceFrameWindow,用戶端可能在下列任何介面上執行 QueryInterface:IOleUIWindow、IOleWindow 或 IUnknown,但除了「最具衍生性」介面 IOleInPlaceFrameWindow 以外 (您實際實作的介面)。 若要處理,可以使用多個 INTERFACE_PART 巨集將每個基底介面對應至 IOleInPlaceFrameWindow 介面:
在類別定義檔案:
BEGIN_INTERFACE_PART(CMyFrameWindow, IOleInPlaceFrameWindow)
在類別實作檔案:
BEGIN_INTERFACE_MAP(CMyWnd, CFrameWnd)
INTERFACE_PART(CMyWnd, IID_IOleWindow, MyFrameWindow)
INTERFACE_PART(CMyWnd, IID_IOleUIWindow, MyFrameWindow)
INTERFACE_PART(CMyWnd, IID_IOleInPlaceFrameWindow, MyFrameWindow)
END_INTERFACE_MAP
Framework 會處理 IUnknown,因為其永遠為必要項。
INTERFACE_PART — 巨集描述
INTERFACE_AGGREGATE(
theClass,
theAggr
)
備註
參數
theClass
包含介面對應的類別名稱,theAggr
要彙總之成員變數的名稱。
備註
這個巨集用來告知 Framework,類別正在使用彙總物件。 這必須出現在 BEGIN_INTERFACE_PART 和 END_INTERFACE_PART 巨集之間。 彙總物件是另一個物件,衍生自 IUnknown。 使用彙總和 INTERFACE_AGGREGATE 巨集,您可以讓彙總支援的所有介面看起來直接由物件支援。 theAggr 引數是衍生自 IUnknown (直接或間接) 之類別的成員變數名稱。 所有 INTERFACE_AGGREGATE 巨集放在介面對應中時必須遵循 INTERFACE_PART 巨集。