注释
自联机文档中首次包含此说明以来,尚未更新以下技术说明。 因此,某些过程和主题可能过期或不正确。 有关最新信息,建议在在线文档索引中搜索您感兴趣的主题。
OLE IDispatch 接口概述
接口 IDispatch
是应用程序公开方法和属性的方法和属性,以便其他应用程序(如 Visual BASIC 或其他语言)可以使用应用程序的功能。 此接口最重要的部分是 IDispatch::Invoke
函数。 MFC 使用“调度映射”来实现 IDispatch::Invoke
。 调度映射提供有关派生类的布局或“形状”的 CCmdTarget
MFC 实现信息,以便它可以直接作对象的属性,或调用对象中的成员函数来满足 IDispatch::Invoke
请求。
在大多数情况下,ClassWizard 和 MFC 合作将 OLE 自动化的大部分详细信息隐藏在应用程序程序员中。 程序员专注于在应用程序中公开的实际功能,无需担心基础管道。
但是,在某些情况下,有必要了解 MFC 在幕后执行的作。 此说明将说明框架如何将 DISPID分配给成员函数和属性。 仅当需要知道 ID 时(例如,为应用程序的对象创建“类型库”时),才需要了解用于分配 DISPID的算法 MFC。
MFC DISPID 分配
尽管自动化的最终用户(例如 Visual Basic 用户)在其代码中看到已启用自动化的属性和方法的实际名称(如 obj)。ShowWindow),实现 IDispatch::Invoke
不会接收实际名称。 出于优化原因,它会收到 DISPID,这是一个 32 位“magic 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,如下所示:
property X (DISPID)0x00000001
property Y (DISPID)0x00000002
由于属性不在基类中,DISPID 的 HIWORD 始终为零(CDispPoint 最派生类的距离为零)。
MFC 将为类 CDisp3DPoint 生成 DISPIDs,如下所示:
property Z (DISPID)0x00000001
property X (DISPID)0x00010001
property Y (DISPID)0x00010002
Z 属性的 DISPID 具有零 HIWORD ,因为它是在公开属性 CDisp3DPoint 的类中定义的。 由于 X 和 Y 属性是在基类中定义的,因此 DISPID 的 HIWORD 为 1,因为定义这些属性的类与大多数派生类的一个派生相距。
注释
LOWORD 始终由地图中的位置确定,即使地图中存在具有显式 DISPID 的条目(有关_ID版本DISP_PROPERTY
和DISP_FUNCTION
宏的信息,请参阅下一部分)。
高级 MFC 调度映射功能
ClassWizard 在此版本的 Visual C++ 中不支持其他许多功能。 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 自动分配一个。 这些高级宏具有相同的名称,但 ID 追加到宏名称(例如 DISP_PROPERTY_ID),ID 由 pszName 参数之后指定的参数确定。 请参阅 AFXDISP。有关这些宏的详细信息,请参阅 H。 _ID条目必须放置在调度映射的末尾。 它们将像非_ID版本的宏一样影响自动 DISPID 生成(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
析构函数默认执行此作)。