TN038: MFC/OLE IUnknown 實作
注意事項 |
---|
由於它第一次線上文件中包含尚未更新下列技術提示。如此一來,某些程序和主題可能已經過期或不正確。如需最新資訊,建議您先搜尋線上文件索引中有興趣的主題。 |
OLE 2 的核心是一個 「 OLE 元件物件模型 」 或 com。COM 會定義一種標準方式且相互合作的物件,彼此通訊。這包括了何種 「 物件 」 外觀,包括如何方法會在物件上,分派的詳細資料。COM 還會定義所有的 COM 相容的類別衍生的基底類別。這個基底類別是 IUnknown。雖然 IUnknown 介面就是 C++ 類別、 COM 並無適用於任何一種語言 — 它才能併入 C、 pascal 命名法或任何其他語言可支援二進位 COM 物件的版面配置。
OLE 是指所有的類別衍生自 IUnknown 為 「 介面 」。這是一項重要的差異,因為 「 介面 」 如 IUnknown 會具有沒有實作。它只是定義所用的物件通訊,不實作所執行的工作的詳細資訊的通訊協定。這是相當合理的一種系統,可讓最大的彈性。它是 MFC 的工作來實作 MFC/C++ 程式的預設行為。
若要了解的 MFC 實作 IUnknown 您必須先了解這個介面是什麼。簡化的版 IUnknown 定義如下:
class IUnknown
{
public:
virtual HRESULT QueryInterface(REFIID iid, void** ppvObj) = 0;
virtual ULONG AddRef() = 0;
virtual ULONG Release() = 0;
};
注意事項 |
---|
某些必要的呼叫慣例詳細說明,例如__stdcall供此說明遺漏了。 |
AddRef和發行成員函式控制物件的記憶體管理。COM 會使用參考計數配置,來追蹤物件。永遠不會參考物件時直接在 C++ 一樣。相反地,COM 物件一定會透過指標參考。若要釋放物件擁有者完成後使用它,該物件的發行的呼叫成員 (相對於使用運算子 delete,如同傳統的 C++ 物件即可完成)。參考計數機制准許您進行多個參考單一物件來管理。實作AddRef和發行會維護物件的參考計數,直到其參考計數到達零時,不會刪除該物件。
AddRef與發行實作的觀點而言相當直接明瞭。下面是一般的實作:
ULONG CMyObj::AddRef()
{
return ++m_dwRef;
}
ULONG CMyObj::Release()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
QueryInterface成員函式會更有趣些。它不是十分有趣,其唯一的成員函式會將物件AddRef和發行 — 還要告訴做事比物件 IUnknown 提供。這是要在哪裡QueryInterface非常有用。它可讓您取得不同的 「 介面 」 上相同的物件。這些介面通常衍生自 IUnknown ,並藉由新增新成員函式中加入其他功能。COM 介面並不會在介面中宣告的成員變數,所有的成員函式宣告為純虛擬的。例如:
class IPrintInterface : public IUnknown
{
public:
virtual void PrintObject() = 0;
};
若要取得 IPrintInterface 如果您只需要 IUnknown,呼叫 IUnknown::QueryInterface 使用 IID 的 IPrintInterface。IID 是一個 128 位元數字可唯一識別此介面。沒有 IID 為每個您或 OLE 所定義的介面。如果 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和發行就是完全相同的實作上面。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 中的程式碼。這會減少程式碼數量,並可避免 bug。這裡的重點在於從 IUnknown 介面可以呼叫QueryInterface擷取任何介面可能支援的物件,並從每個介面仍可執行相同動作。這表示所有QueryInterface從每個介面函式的行為必須完全相同的方式。為了讓這些內嵌的物件來呼叫 「 外部物件 」 中的實作,返回指標已使用過的 (m_pParent)。M_pParent 指標 CEditPrintObj 建構函式期間初始化時發生。您可以實作 CEditPrintObj::CPrintObj::PrintObject 和 CEditPrintObj::CEditObj::EditObject 也。若要新增一項功能加入相當多的程式碼 — 能夠編輯物件。幸運的是,是相當常見的介面,讓只有一個單一的成員函式 (雖然它不會發生),如此一來,EditObject 和 PrintObject 會通常會組合成單一的介面。
這正是許多解釋及大量程式碼,這種簡單的案例。MFC/OLE 類別提供更簡單的另一種方式。MFC 實作會使用訊息對應的 Windows 訊息被包起來的方式類似的技術。這個功能稱為介面對應 ,並在下一節中討論。
MFC 的介面對應
在 MFC/OLE"介面對應"與 MFC 的 「 訊息對應"和"分派對應] 類似的實作中包含概念和執行。MFC 的介面對應的核心功能如下所示:
標準實作, IUnknown、 內建CCmdTarget類別。
維護之參考次數,藉由修改AddRef和發行
資料驅動型的實作QueryInterface
此外,介面對應支援下列進階功能:
建立可彙總的 COM 物件的支援
支援 COM 物件的實作中使用彙總物件
實作是 hookable,並且能夠延伸
如需有關彙總的詳細資訊,請參閱 OLE 程式設計人員參考。
MFC 的介面對應支援起源於CCmdTarget類別。CCmdTarget"有一"參考計數,以及所有的成員函式相關聯 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,使用INTERFACE_PART之間的巨集BEGIN_INTERFACE_MAP和END_INTERFACE_MAP ,將該之 IID 對應至特定類別的 「 組件 」 的巨集。
實作每個巢狀類別,表示您支援的介面。
使用METHOD_PROLOGUE巨集來存取父代, CCmdTarget-衍生物件。
AddRef發行,以及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"和介面的類別,其從"I"開始的通用類別) 的名稱。會建立兩個巢狀的成員,這些類別: m_CEditObj,m_CPrintObj,並分別。巨集自動宣告AddRef, 發行,以及QueryInterface函式。 因此您只宣告函式指定給這個介面: EditObject 和 PrintObject (OLE 巨集 STDMETHOD 是使用例如,讓 _stdcall 和虛擬關鍵字提供來配合目標平台)。
若要實作這個類別的介面對應:
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 分別。CCmdTarget的實作QueryInterface (CCmdTarget::ExternalQueryInterface) 返回指標 m_CPrintObj 與 m_CEditObj 在需要時才會使用此對應。您不需要加入一個項目,如 IID_IUnknown。 架構使用對應 (在此例中為 m_CPrintObj) 中的第一個介面的時 IID_IUnknown 要求時。
即使 BEGIN_INTERFACE_PART 巨集自動宣告AddRef, 發行和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 和發行
在這兩個介面中宣告兩種內建的方法
此外,架構會使用訊息對應於內部。這樣您就可以假設衍生自架構類別, COleServerDoc,已經支援特定的介面,提供替代或加入項目由架構所提供的介面。啟用這項功能很驚訝架構完全支援繼承自基底類別的介面對應 —,原因是為什麼BEGIN_INTERFACE_MAP做為第二個參數,會使用基底類別的名稱。
注意事項 |
---|
它通常是不能重複使用 MFC 的內建的實作,藉由 OLE 介面的實作繼承的 MFC 版本從該介面的內嵌的特製化。問題無法解決由於使用METHOD_PROLOGUE巨集,才能存取包含的CCmdTarget-衍生的物件代表固定位移從內嵌物件的CCmdTarget-衍生物件。這表示,比方說,您無法從其中內嵌的 XMyAdviseSink 中的 MFC 實作 COleClientItem::XAdviseSink,因為要在特定的位移,從頂端依賴 XAdviseSink COleClientItem物件。 |
注意事項 |
---|
您可以,不過,委派至 MFC 實作所有您想要的 MFC 的預設行為的函式。這是在 MFC 實作的 IOleInPlaceFrame (XOleInPlaceFrame) 在 COleFrameHook (委派來提供許多函式的 m_xOleInPlaceUIWindow) 的類別。這種設計選擇用來縮小物件實作許多介面 ; 這些物件的執行階段 (如前一節所使用的方法 m_pParent),它不需要再上一步] 指標。 |
彙總及介面對應
除了支援獨立的 COM 物件,MFC 也支援彙總。彙總本身是一個太複雜 ; 在此討論的主題 請參閱 OLE 程式設計人員參考如需有關彙總。這個註解只需將說明進行彙總的架構和介面對應內建支援。
若要使用彙總的兩種方式: (1) 使用 COM 物件支援彙總,以及 (2) 實作可由另一個彙總物件。這些功能可以被稱為"使用彙總物件"和"使物件可彙總 」。MFC 支援兩者。
使用彙總物件
若要使用的彙總物件沒有必須是某種方式來繫結到 QueryInterface 機制彙總。亦即,彙總物件必須行為就好像它是物件的原生的一部分。那麼要如何這個決勝局至 MFC 的介面對應機制?除了INTERFACE_PART巨集,其中的巢狀的物件會對應到的 IID,您也可以宣告彙總物件的一部分您CCmdTarget衍生的類別。若要執行這項操作, INTERFACE_AGGREGATE 使用巨集時。這可讓您指定的成員變數 (它必須是變數的指標, IUnknown 或衍生類別),這是整合到的介面對應機制。如果指標不是 NULL 時 CCmdTarget::ExternalQueryInterface 是呼叫,架構會自動呼叫彙總物件的QueryInterface成員函式,如果 IID 要求不是原生 IIDs 支援的CCmdTarget物件本身。
若要使用 INTERFACE_AGGREGATE 巨集
宣告成員變數 ( IUnknown 1) 會包含下列資料的彙總物件的指標。
包括 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 的建構函式中初始化。架構會忽略 NULL 成員變數中的預設實作QueryInterface。OnCreateAggregates 》,實際建立彙總的物件。您必須明確地呼叫它,如果您要建立的物件之外的 MFC 實作COleObjectFactory。建立彙總中的原因 CCmdTarget::OnCreateAggregates 的使用方式以及 CCmdTarget::GetControllingUnknown 會浮現時建立可彙總物件會加以討論。
這項技術可讓您的物件而所有的彙總物件再加上其原生介面所支援的介面。如果您只想彙總支援的介面的子集,您可以覆寫 CCmdTarget::GetInterfaceHook。這可讓您非常低層級的 hookability,類似於QueryInterface。通常,您會希望所有彙總支援的介面。
使物件實作可彙總
物件是可彙總,實作AddRef, 發行,以及QueryInterface必須委派給 「 控制未知 」。也就是說,它們是物件的一部份,它必須委派AddRef, 發行,以及QueryInterface到不同的物件,也衍生自 IUnknown。提供物件建立時,亦即,而提供的實作,則此"控制不明" COleObjectFactory。實作這帶來少量額外負荷,而且在某些情況下不令人滿意,使 MFC 可以讓這選擇性。若要讓物件以是可彙總,您呼叫 CCmdTarget::EnableAggregation 從物件的建構函式。
如果物件也可使用的彙總,您也必須確定將正確的彙總物件的 「 控制未知 」。通常這 IUnknown 建立彙總時,指標會傳遞至物件。例如,pUnkOuter 參數是 「 控制未知的 」 以建立物件的CoCreateInstance。可以擷取正確的"控制不明"指標藉由呼叫 CCmdTarget::GetControllingUnknown。從該函式,傳回的值其實不正確建構函式時。基於這個理由,建議您建立您的彙總,只有在覆寫 CCmdTarget::OnCreateAggregates,其中來自的傳回值GetControllingUnknown可靠,雖然仍然是由COleObjectFactory的實作。
它也很重要的物件操作正確的參考次數時新增或釋放人工參考計數。若要確保這是大小寫,請務必呼叫 ExternalAddRef 和 ExternalRelease 而不是InternalRelease和InternalAddRef。我們很難呼叫InternalRelease或InternalAddRef上支援彙總的類別。
參考資料
進階的使用方式的 OLE,例如定義您自己的介面,或覆寫架構的 OLE 介面的實作需要使用的基礎的介面對應機制。
本章節討論每個巨集,它用來實作這些進階的功能的 Api。
CCmdTarget::EnableAggregation — 功能描述
void EnableAggregation();
備註
如果您想要為此型別的物件支援 OLE 的彙總,請在衍生類別的建構函式中呼叫此函式。這樣可以進入特殊的 IUnknown 實作所需的可集合的物件。
CCmdTarget::ExternalQueryInterface — 功能描述
DWORD ExternalQueryInterface(
const void FAR* lpIID,
LPVOID FAR* ppvObj
);
備註
參數
lpIID
(QueryInterface 的第一個引數) 的 IID 遠程指標ppvObj
變數的指標,IUnknown 1 (第二個引數為 QueryInterface)
備註
呼叫此函式的 IUnknown 實作中的每個介面的類別會實作。這個函式會提供標準資料導向的實作 QueryInterface,根據您的物件介面對應。就必須轉型成 HRESULT 傳回值。如果物件彙總,這個函式會呼叫"控制 IUnknown",而非使用本機的介面對應。
CCmdTarget::ExternalAddRef — 功能描述
DWORD ExternalAddRef();
備註
呼叫此函式的 IUnknown::AddRef 實作中的每個介面的類別會實作。傳回值是 CCmdTarget 物件上的新參考次數。如果物件彙總,這個函式會呼叫"控制 IUnknown",而不是受管理的本機參考次數的影響。
CCmdTarget::ExternalRelease — 功能描述
DWORD ExternalRelease();
備註
呼叫此函式的 IUnknown::Release 實作中的每個介面的類別會實作。傳回值會指示在物件上的新參考次數。如果物件彙總,這個函式會呼叫"控制 IUnknown",而不是受管理的本機參考次數的影響。
DECLARE_INTERFACE_MAP--巨集說明
DECLARE_INTERFACE_MAP
備註
在衍生自的任何類別中使用此巨集CCmdTarget是不知道介面對應。很多相同的方式使用於DECLARE_MESSAGE_MAP。此巨集引動過程應置於類別定義中,通常是在標頭 (。H) 檔。具有 DECLARE_INTERFACE_MAP 必須實作檔中定義的介面對應 (。CPP) 與BEGIN_INTERFACE_MAP和END_INTERFACE_MAP巨集。
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, 發行,以及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
因為它永遠都是必要 IUnknown 會負責架構。
INTERFACE_PART--巨集說明
INTERFACE_AGGREGATE(
theClass,
theAggr
)
備註
參數
theClass
包含的介面對應,類別名稱theAggr
要被聚合的成員變數的名稱。
備註
此巨集用來告訴架構類別使用彙總的物件。它必須出現之間 BEGIN_INTERFACE_PART 和 END_INTERFACE_PART 巨集。彙總物件則是另一個物件,衍生自 IUnknown。藉由使用彙總,並 INTERFACE_AGGREGATE 巨集],您可以將彙總支援似乎會直接支援的物件的所有介面。TheAggr 引數是衍生自類別的成員變數的名稱 IUnknown (直接或間接)。所有 INTERFACE_AGGREGATE 巨集必須遵循INTERFACE_PART巨集放置在一個介面對應。