共用方式為


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

AddRefRelease 成員函式會控制物件的記憶體管理。 COM 使用參考計數配置追蹤物件。 不可像使用 C++ 時直接參考物件。 相反地,COM 物件永遠是透過指標來參考。 若要在擁有者完成使用時釋放物件,呼叫物件的 Release 成員 (而不像傳統 C++ 物件使用 operator delete)。 參考計數機制允許單一物件的多個參考為 Managed。 AddRef版本 的實作會維持物件的參考計數,在參考計數達到零之前,不會刪除該物件。

從實作的角度來看,AddRefRelease 相當簡單明暸。 以下是一般實作:

ULONG CMyObj::AddRef() 
{ 
    return ++m_dwRef; 
}

ULONG CMyObj::Release() 
{ 
    if (--m_dwRef == 0) 
    {
        delete this; 
        return 0;
    }
    return m_dwRef;
}

QueryInterface 成員函式會比較有趣一點。 僅有的成員函式為 AddRefRelease 的物件並不值得注意,最好是告訴物件要執行的不只是 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();
};

AddRefRelease 的實作與上述實作的介面完全相同。 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 介面對應的核心功能如下:

此外,介面對應還支援下列進階功能:

  • 支援建立可彙總的 COM 物件

  • 支援在 COM 物件的實作中使用彙總物件

  • 實作是可攔截且可擴充

如需彙總的詳細資訊,請參閱彙總主題。

MFC 的介面對應支援根源於 CCmdTarget 類別。 CCmdTargethas-a」會參考計數以及所有與 IUnknown 實作相關的成員函式 (例如 CCmdTarget 中的參考計數)。 若要建立支援 OLE COM 的類別,從 CCmdTarget 類別衍生並使用各種巨集以及 CCmdTarget 的成員函式,以實作所需的介面。 MFC 的實作使用巢狀類別來定義每個介面的實作,很像上面的範例。 這是以 IUnknown 的標準實作以及一些消除某些重複程式碼的巨集變得更容易。

若要使用 MFC 的介面對應來實作類別

  1. CCmdTarget 直接或間接衍生類別。

  2. 使用衍生類別定義中的 DECLARE_INTERFACE_MAP 函式。

  3. 針對您要支援的每個介面,在類別定義中使用 BEGIN_INTERFACE_PARTEND_INTERFACE_PART 巨集。

  4. 在實作檔案中,使用 BEGIN_INTERFACE_MAPEND_INTERFACE_MAP 巨集來定義類別的介面對應。

  5. 針對支援的每個 IID,在 BEGIN_INTERFACE_MAPEND_INTERFACE_MAP 巨集之間使用 INTERFACE_PART 巨集將該 IID 對應至類別的特定「部分」。

  6. 實作表示您所支援之介面的每個巢狀類別。

  7. 使用 METHOD_PROLOGUE 巨集存取父代 CCmdTarget 衍生物件。

  8. AddRefReleaseQueryInterface 可以委派至 CCmdTarget (ExternalAddRefExternalReleaseExternalQueryInterface 函式的實作。

上述 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_PARTEND_INTERFACE_PART 巨集還會定義巢狀類別,在這種情況下,名稱為 CEditObj 和 CPrintObj (X 只是用來區別開頭為 "C" 的全域類別和開頭為 "Me" 的介面類別)。 分別建立這些類別的兩個巢狀成員:m_CEditObj 和 m_CPrintObj。 巨集會自動宣告 AddRefReleaseQueryInterface 函式,因此您只需宣告此介面特定的函式: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 巨集會自動宣告 AddRefReleaseQueryInterface 函式,您還是需要實作它們:

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 巨集

  1. 宣告成員變數 (IUnknown*),其中將包含彙總物件的指標。

  2. 在依據名稱參考成員變數的介面對應中加入 INTERFACE_AGGREGATE 巨集。

  3. 在某些情況下 (通常在 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。 通常,您想要彙總支援的所有介面。

讓物件實作可以彙總

為了能夠彙總物件,AddRefReleaseQueryInterface 的實作必須委派至 "controlling unknown"。換句話說,為了成為物件的一部分,它必須將 AddRefReleaseQueryInterface 委派給同樣衍生自 IUnknown 的不同物件。 這個 "controlling unknown" 在物件建立時提供給物件,也就是提供給 COleObjectFactory 的實作。 實作此物件會增加少量的額外負荷,在某些情況下並不適當,因此 MFC 會做此選擇。 若要將物件設為可彙總,您必須從物件的建構函式呼叫 CCmdTarget::EnableAggregation

如果物件也使用彙總,則您同樣必須傳遞正確的 "controlling unknown" 至彙總物件。 通常當彙總建立時,傳遞這個 IUnknown 指標給物件。 例如,pUnkOuter 參數是使用 CoCreateInstance 所建立物件的 "controlling unknown"。 正確的 "controlling unknown" 指標可以透過呼叫 CCmdTarget::GetControllingUnknown來擷取。 然而,從該函式傳回的值在建構函式期間是無效的。 因此,建議您只在 CCmdTarget::OnCreateAggregates 覆寫中建立彙總,其中來自 GetControllingUnknown 的傳回值是可靠的,即使是從 COleObjectFactory 實作所建立。

加入或釋放假造的參考計數時,物件也必須管理正確的參考計數。 若要確保此情況,一定要呼叫 ExternalAddRefExternalRelease,而非 InternalReleaseInternalAddRef。 在支援彙總的類別上呼叫 InternalReleaseInternalAddRef,很少見。

參考資料

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_MAPEND_INTERFACE_MAP 巨集的實作檔 (.CPP) 中定義介面對應。

BEGIN_INTERFACE_PART 和 END_INTERFACE_PART —巨集描述

BEGIN_INTERFACE_PART( 
   localClass,
   iface 
);
END_INTERFACE_PART( 
   localClass 
)

備註

參數

  • localClass
    實作介面的類別名稱

  • iface
    這個類別實作的介面名稱

備註

您的類別將實作的每個介面都必須有 BEGIN_INTERFACE_PARTEND_INTERFACE_PART 配對。 這些巨集定義從您定義的 OLE 介面衍生的區域類別以及該類別的內嵌成員變數。 AddRefReleaseQueryInterface 成員是自動宣告的。 您必須包含其他成員函式 (是實作介面的一部分) 的宣告 (這些宣告是放置在 BEGIN_INTERFACE_PARTEND_INTERFACE_PART 巨集之間)。

iface 引數是您要實作的 OLE 介面,例如 IAdviseSinkIPersistStorage (或您的自訂介面)。

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_MAPEND_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,用戶端可能在下列任何介面上執行 QueryInterfaceIOleUIWindowIOleWindowIUnknown,但除了「最具衍生性」介面 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_PARTEND_INTERFACE_PART 巨集之間。 彙總物件是另一個物件,衍生自 IUnknown。 使用彙總和 INTERFACE_AGGREGATE 巨集,您可以讓彙總支援的所有介面看起來直接由物件支援。 theAggr 引數是衍生自 IUnknown (直接或間接) 之類別的成員變數名稱。 所有 INTERFACE_AGGREGATE 巨集放在介面對應中時必須遵循 INTERFACE_PART 巨集。

請參閱

其他資源

依編號顯示的技術提示

依分類區分的技術提示