注意
自联机文档中首次包含此说明以来,尚未更新以下技术说明。 因此,某些过程和主题可能过期或不正确。 有关最新信息,建议在在线文档索引中搜索您感兴趣的主题。
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 和 Release 成员函数控制对象的内存管理。 COM 使用引用计数方案跟踪对象。 从来不会像在 C++ 中一样直接引用对象。 相反,始终通过指针引用 COM 对象。 若要在所有者使用完对象时释放对象,将调用对象的 Release 成员(而不是使用运算符删除),就像对传统C++对象所做的那样)。 引用计数机制允许对单个对象的多个引用进行管理。 AddRef 和 Release 的实现维护对象的引用计数 — 对象在引用计数达到零之前不会删除该对象。
从实现的角度来看,AddRef 和 Release 非常简单。 下面是一个简单实现:
ULONG CMyObj::AddRef()
{
return ++m_dwRef;
}
ULONG CMyObj::Release()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
QueryInterface 成员函数更有意思。 拥有成员函数仅为 AddRef 和 Release 的对象没什么意思,除 IUnknown 提供的功能外,让对象完成更多事项会是很好的体验。 在这里,QueryInterface 会很有用。 它允许在同一对象上获取不同的“接口”。 这些接口通常派生自 IUnknown,并通过添加新成员函数来添加其他功能。 COM 接口从未在接口中声明成员变量,所有成员函数都声明为纯虚拟。 例如
class IPrintInterface : public IUnknown
{
public:
virtual void PrintObject() = 0;
};
如果你只有 IUnknown 并且要获取 IPrintInterface,那么可以使用 的 IID
调用 IPrintInterface
。
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();
};
AddRef 和 Release 的实现与上面实现的实现完全相同。
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)。 在 CEditPrintObj 构造函数期间初始化m_pParent指针。 然后还会实现 CEditPrintObj::CPrintObj::PrintObject 和 CEditPrintObj::CEditObj::EditObject。 添加了相当多的代码来添加一项功能 - 编辑对象的功能。 幸运的是,接口很少只有单个成员函数(尽管这种情况确实存在),因此在这种情况下,EditObject 和 PrintObject 通常会合并为单个接口。
对于如此简单的方案而言,解释和代码都相当多。 MFC/OLE 类提供更简单的替代方法。 MFC 实现使用一种技术,这种技术类似于通过消息映射包装 Windows 消息的方式。 此设施称为 接口地图,在下一部分中进行了讨论。
MFC 接口映射
MFC/OLE 在概念和执行中包括类似于 MFC 的“消息映射”和“调度地图”的“接口映射”实现。 MFC 接口映射的核心功能如下:
IUnknown的标准实现,内置于
CCmdTarget
类中。QueryInterface 的数据驱动实现
此外,接口映射还支持以下高级功能:
支持创建可聚合 COM 对象
支持在 COM 对象的实现中使用聚合对象
实现可钩进且可扩展
有关聚合的详细信息,请参阅 聚合 主题。
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,请在 BEGIN_INTERFACE_MAP 和 END_INTERFACE_MAP 宏之间使用 INTERFACE_PART 宏,将该 IID 映射到类中的特定部分。
实现表示支持的接口的每个嵌套类。
使用 METHOD_PROLOGUE 宏来访问派生自
CCmdTarget
的父对象。AddRef、Release和 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、Release和 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
的 实现 (CCmdTarget::ExternalQueryInterface
) 在收到请求时会使用此映射返回指向 m_CPrintObj 和 m_CEditObj 的指针。 不需要包括 IID_IUnknown
条目;请求 IID_IUnknown
时,框架将使用地图中的第一个接口(在本例中为m_CPrintObj)。
即便BEGIN_INTERFACE_PART宏会自动为您声明AddRef、Release和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 和 Release
在两个接口上声明这两个内置方法之一
此外,框架在内部使用消息映射。 这样,就可以从框架类(例如 COleServerDoc
)派生,该类已经支持某些接口,并提供框架提供的接口的替换或添加。 框架完全支持从基类继承接口映射,您就可以做到这一点。 这就是为什么BEGIN_INTERFACE_MAP将基类的名称作为第二个参数的原因。
注意
通常不可能仅通过从 MFC 版本继承 OLE 接口的嵌入专用化来重用 MFC 的 OLE 接口内置实现的实现。 这不可能实现的原因在于,使用 METHOD_PROLOGUE 宏获取所含 CCmdTarget
派生对象的访问权限表示嵌入对象对 派生对象的固定偏移CCmdTarget
。 举例来说,这意味着你无法在 COleClientItem::XAdviseSink
中从 MFC 的实现派生嵌入 XMyAdviseSink,因为 XAdviseSink 依赖于对 COleClientItem
对象顶部进行特定偏移。
注意
但是,你可以为所有你想要其具有 MFC 默认行为的函数委托到 MFC 实现。 这在 IOleInPlaceFrame
类(它为许多函数委托到 m_xOleInPlaceUIWindow)中的 COleFrameHook
(XOleInPlaceFrame) 的 MFC 实现中完成。 选择此设计以减少实现多个接口的对象运行时大小;它消除了对后指针的需求(例如上一部分使用m_pParent的方式)。
聚合和接口映射
除了支持独立 COM 对象外,MFC 还支持聚合。 聚合本身太复杂,无法在此处讨论;有关聚合的详细信息,请参阅 聚合 主题。 此说明将仅描述对框架和接口映射中内置的聚合的支持。
有两种方法可以使用聚合:(1)使用支持聚合的 COM 对象,以及实现可由另一个对象聚合的对象(2)。 这些功能可以称为“使用聚合对象”和“使对象可聚合”。 MFC 支持这两者。
使用聚合对象
若要使用聚合对象,需要通过某种方式将聚合绑定到 QueryInterface 机制。 换句话说,聚合对象的行为必须好像它本来就是对象的一部分。 如何将其与 MFC 的接口映射机制联系起来呢?除了可以将嵌套对象通过 INTERFACE_PART 宏映射到 IID,你还可以将聚合对象声明为 CCmdTarget
派生类的一部分。 为此,将使用INTERFACE_AGGREGATE宏。 这样,可以指定成员变量(必须是指向 IUnknown 或派生类的指针),该变量要集成到接口映射机制中。 如果调用 CCmdTarget::ExternalQueryInterface
时指针不为 NULL,且如果请求的 不是 IID
对象本身支持的本机 IID
之一,则该框架将自动调用聚合对象的 CCmdTarget
成员函数。
使用 INTERFACE_AGGREGATE 宏
声明成员变量(
IUnknown*
),其中包含指向聚合对象的指针。在接口映射中包含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。 框架忽略 QueryInterface的默认实现中的 NULL 成员变量。
OnCreateAggregates
是实际创建聚合对象的好位置。 如果要在 COleObjectFactory
的 MFC 实现之外创建对象,则必须显式调用它。 讨论创建可聚合对象时,在 CCmdTarget::OnCreateAggregates
中创建聚合的原因以及 CCmdTarget::GetControllingUnknown
的使用将变得明显。
此方法将为对象提供聚合对象支持的所有接口及其本机接口。 如果只需要聚合支持的一部分接口,可以替代 CCmdTarget::GetInterfaceHook
。 这让你可以拥有极低可钩进性,与 QueryInterface 类似。 通常,需要聚合支持的所有接口。
使对象实现可聚合
若要使对象可聚合,AddRef、Release和 QueryInterface 的实现必须委托给“控制未知”。换句话说,要成为对象的一部分,它必须委托 AddRef、Release,并将 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,
LPVOIDFAR* ppvObj
);
参数
lpIID
指向 IID 的较远指针(QueryInterface 的第一个自变量)
ppvObj
指向 IUnknown* 的指针(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、Release 和 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 的成员。注意:
注意
以 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宏,你可以使聚合支持的所有接口似乎都由对象直接支持。 Aggr 参数只是类的成员变量的名称,该变量派生自 IUnknown(直接或间接)。 在接口映射中放置时,所有INTERFACE_AGGREGATE宏都必须遵循INTERFACE_PART宏。