共用方式為


TN039:MFC/OLE Automation 實作

注意

下列技術提示自其納入線上文件以來,未曾更新。 因此,有些程序和主題可能已過期或不正確。 如需最新資訊,建議您在線上文件索引中搜尋相關的主題。

OLE IDispatch 介面概觀

介面 IDispatch 是應用程式公開方法與屬性的方法和屬性,讓其他應用程式,例如 Visual BASIC 或其他語言,可以使用應用程式的功能。 這個介面最重要的部分是 函 IDispatch::Invoke 式。 MFC 使用「分派對應」來實作 IDispatch::Invoke 。 分派對應會提供衍生類別配置或「圖形」的 CCmdTarget MFC 實作資訊,讓它可以直接操作物件的屬性,或呼叫物件內的成員函式來滿足 IDispatch::Invoke 要求。

在大部分情況下,ClassWizard 和 MFC 會合作隱藏應用程式程式設計人員的 OLE 自動化大部分詳細資料。 程式設計人員專注于在應用程式中公開的實際功能,不需要擔心基礎管道。

不過,在某些情況下,必須瞭解 MFC 在幕後執行的動作。 此附注將說明架構如何將 DISPID 指派 給成員函式和屬性。 只有當您需要知道識別碼時,才需要知道 MFC 用於指派 DISPID 的演算法,例如當您為應用程式的物件建立「類型程式庫」時。

MFC DISPID 指派

雖然自動化的使用者(例如 Visual Basic 使用者),但在其程式碼中看到自動化已啟用屬性和方法的實際名稱(例如 obj。ShowWindow),的 IDispatch::Invoke 實作不會接收實際名稱。 基於優化考慮,它會接收 DISPID ,這是 32 位的「魔術 Cookie」,描述要存取的方法或屬性。 這些 DISPID 值會透過另一個稱為 IDispatch::GetIDsOfNames 的方法從 IDispatch 實作傳回。 自動化用戶端應用程式會針對想要存取的每個成員或屬性呼叫 GetIDsOfNames 一次,並快取它們以供稍後呼叫 IDispatch::Invoke 。 如此一來,昂貴的字串查閱只會針對每個物件使用一次,而不是每次呼叫一 IDispatch::Invoke 次。

MFC 會 根據兩件事來判斷每個方法和屬性的 DISPID

  • 距離分派地圖頂端的距離 (1 相對)

  • 從最衍生類別的分派對應距離 (0 相對)

DISPID 分成兩個部分。 DISPID LOWORD 包含第一個元件,距離分派對應頂端的距離。 HIWORD 包含與最衍生類別的距離。 例如:

class CDispPoint : public CCmdTarget
{
public:
    short m_x, m_y;
    // ...
    DECLARE_DISPATCH_MAP()
    // ...
};

class CDisp3DPoint : public CDispPoint
{
public:
    short m_z;
    // ...
    DECLARE_DISPATCH_MAP()
    // ...
};

BEGIN_DISPATCH_MAP(CDispPoint, CCmdTarget)
    DISP_PROPERTY(CDispPoint, "x", m_x, VT_I2)
    DISP_PROPERTY(CDispPoint, "y", m_y, VT_I2)
END_DISPATCH_MAP()

BEGIN_DISPATCH_MAP(CDisp3DPoint, CDispPoint)
    DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
END_DISPATCH_MAP()

如您所見,有兩個類別會公開 OLE 自動化介面。 其中一個類別衍生自另一個類別,因此會利用基類的功能,在此案例中包括 OLE 自動化元件(「x」 和 「y」 屬性)。

MFC 將針對 CDispPoint 類別產生 DISPID s,如下所示:

property X    (DISPID)0x00000001
property Y    (DISPID)0x00000002

由於屬性不在基類中, DISPID HIWORD 一律為零(CDispPoint 的衍生類別距離為零)。

MFC 將針對 CDisp3DPoint 類別產生 DISPID s,如下所示:

property Z    (DISPID)0x00000001
property X    (DISPID)0x00010001
property Y    (DISPID)0x00010002

Z 屬性會以零 HIWORD 指定 DISPID ,因為它定義于公開屬性 CDisp3DPoint 的類別中。 由於 X 和 Y 屬性是在基類中定義, 因此 DISPID 的 HIWORD 是 1,因為定義這些屬性的 類別與衍生自最多衍生類別的一個衍生距離。

注意

LOWORD 一律由地圖中的位置決定,即使地圖中有明確的 DISPID 專案(請參閱下一節以取得 和 DISP_FUNCTION 宏_ID 版本 DISP_PROPERTY 的相關資訊 )。

進階 MFC 分派對應功能

此版本的 Visual C++ 不支援 ClassWizard 的一些其他功能。 ClassWizard 支援 DISP_FUNCTIONDISP_PROPERTYDISP_PROPERTY_EX ,分別定義方法、成員變數屬性和 get/set 成員函式屬性。 這些功能通常是建立大部分自動化伺服器所需的所有功能。

當 ClassWizard 支援的宏不夠時,可以使用下列其他宏: DISP_PROPERTY_NOTIFY 、 和 DISP_PROPERTY_PARAM

DISP_PROPERTY_NOTIFY — 宏描述

DISP_PROPERTY_NOTIFY(
    theClass,
    pszName,
    memberName,
    pfnAfterSet,
    vtPropType)

參數

theClass
類別的名稱。

pszName
屬性的外部名稱。

memberName
屬性儲存所在的成員變數名稱。

pfnAfterSet
屬性變更時要呼叫的成員函式名稱。

vtPropType
值,指定屬性的類型。

備註

這個宏非常類似DISP_PROPERTY,不同之處在于它接受額外的引數。 其他引數 pfnAfterSet 應該是一個成員函式, 會傳回任何專案,而且不採用任何參數 'void OnPropertyNotify()'。 在修改成員變數之後 ,將會呼叫 它。

DISP_PROPERTY_PARAM — 宏描述

DISP_PROPERTY_PARAM(
    theClass,
    pszName,
    pfnGet,
    pfnSet,
    vtPropType,
    vtsParams)

參數

theClass
類別的名稱。

pszName
屬性的外部名稱。

memberGet
用來取得屬性的成員函式名稱。

memberSet
用來設定屬性的成員函式名稱。

vtPropType
值,指定屬性的類型。

vtsParams
每個參數VTS_分隔的空間字串。

備註

與DISP_PROPERTY_EX宏類似,這個宏會定義使用個別 Get 和 Set 成員函式存取的屬性。 不過,這個宏可讓您指定 屬性的參數清單。 這適用于實作以其他方式編制索引或參數化的屬性。 參數一律會放在第一位,後面接著 屬性的新值。 例如:

DISP_PROPERTY_PARAM(CMyObject, "item", GetItem, SetItem, VT_DISPATCH, VTS_I2 VTS_I2)

會對應至取得和設定成員函式:

LPDISPATCH CMyObject::GetItem(short row, short col)
void CMyObject::SetItem(short row, short col, LPDISPATCH newValue)

DISP_XXXX_ID - 宏描述

DISP_FUNCTION_ID(
    theClass,
    pszName,
    dispid,
    pfnMember,
    vtRetVal,
    vtsParams)
DISP_PROPERTY_ID(
    theClass,
    pszName,
    dispid,
    memberName,
    vtPropType)
DISP_PROPERTY_NOTIFY_ID(
    theClass,
    pszName,
    dispid,
    memberName,
    pfnAfterSet,
    vtPropType)
DISP_PROPERTY_EX_ID(
    theClass,
    pszName,
    dispid,
    pfnGet,
    pfnSet,
    vtPropType)
DISP_PROPERTY_PARAM_ID(
    theClass,
    pszName,
    dispid,
    pfnGet,
    pfnSet,
    vtPropType,
    vtsParams)

參數

theClass
類別的名稱。

pszName
屬性的外部名稱。

dispid
屬性或方法的固定 DISPID。

pfnGet
用來取得屬性的成員函式名稱。

pfnSet
用來設定屬性的成員函式名稱。

memberName
要對應至 屬性的成員變數名稱

vtPropType
值,指定屬性的類型。

vtsParams
每個參數VTS_分隔的空間字串。

備註

這些宏可讓您指定 DISPID ,而不是讓 MFC 自動指派一個。 這些進階宏的名稱相同,不同之處在于識別碼會附加至宏名稱(例如 DISP_PROPERTY_ID ),而且識別碼是由 pszName 參數之後 指定的參數所決定。 請參閱 AFXDISP。如需這些宏的詳細資訊,H。 _ID 專案必須放在分派地圖的結尾。 它們會影響自動 DISPID 產生的方式,與非 _ID 版本的宏相同( DISPID 是由位置決定的)。 例如:

BEGIN_DISPATCH_MAP(CDisp3DPoint, CCmdTarget)
    DISP_PROPERTY(CDisp3DPoint, "y", m_y, VT_I2)
    DISP_PROPERTY(CDisp3DPoint, "z", m_z, VT_I2)
    DISP_PROPERTY_ID(CDisp3DPoint, "x", 0x00020003, m_x, VT_I2)
END_DISPATCH_MAP()

MFC 會產生 CDisp3DPoint 類別的 DISPID,如下所示:

property X    (DISPID)0x00020003
property Y    (DISPID)0x00000002
property Z    (DISPID)0x00000001

指定固定 DISPID 有助於維持與先前現有分派介面的回溯相容性,或實作某些系統定義的方法或屬性(通常以負 DISPID 表示,例如 DISPID_NEWENUM 集合)。

擷取 COleClientItem 的 IDispatch 介面

許多伺服器都會在其檔物件內支援自動化,以及 OLE 伺服器功能。 若要取得此自動化介面的存取權,必須直接存取 COleClientItem::m_lpObject 成員變數。 下列程式碼會擷取 IDispatch 衍生自 COleClientItem 之物件的介面。 如果您發現需要這項功能,您可以在應用程式中包含下列程式碼:

LPDISPATCH CMyClientItem::GetIDispatch()
{
    ASSERT_VALID(this);
    ASSERT(m_lpObject != NULL);

    LPUNKNOWN lpUnk = m_lpObject;

    Run();      // must be running

    LPOLELINK lpOleLink = NULL;
    if (m_lpObject->QueryInterface(IID_IOleLink,
        (LPVOID FAR*)&lpOleLink) == NOERROR)
    {
        ASSERT(lpOleLink != NULL);
        lpUnk = NULL;
        if (lpOleLink->GetBoundSource(&lpUnk) != NOERROR)
        {
            TRACE0("Warning: Link is not connected!\n");
            lpOleLink->Release();
            return NULL;
        }
        ASSERT(lpUnk != NULL);
    }

    LPDISPATCH lpDispatch = NULL;
    if (lpUnk->QueryInterface(IID_IDispatch, &lpDispatch) != NOERROR)
    {
        TRACE0("Warning: does not support IDispatch!\n");
        return NULL;
    }

    ASSERT(lpDispatch != NULL);
    return lpDispatch;
}

然後,可以從這個函式傳回的分派介面直接使用或附加至 COleDispatchDriver ,以取得型別安全存取。 如果您直接使用它,請務必在透過 指標時呼叫其 Release 成員( COleDispatchDriver 解構函式預設會執行此動作)。

另請參閱

依編號顯示的技術提示
依分類區分的技術提示