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_FUNCTION
、 DISP_PROPERTY
和 DISP_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
解構函式預設會執行此動作)。