TN038:MFC/OLE IUnknown 实现

备注

以下技术说明在首次包括在联机文档中后未更新。因此,某些过程和主题可能已过时或不正确。要获得最新信息,建议你在联机文档索引中搜索热点话题。

OLE 2 的核心处是“OLE 组件对象模型”或 COM。 COM 定义了合作对象之间如何互相通信的标准。 这包括“对象”外观的详细信息,包括如何在对象上调度方法。 COM 还定义了一个基类,所有 COM 兼容的类都从此基类中派生。 该基类是 IUnknown。 虽然 IUnknown 接口称为 C++ 类,但 COM 对任何一种语言都不是特定的,它可以在 C、PASCAL 或任何其他能支持 COM 对象的二进制布局的语言中实现。

OLE 将 IUnknown 派生的所有类作为“接口”引用。由于“接口”(例如 IUnknown)没有实现,因此这是一个重要的区别。 它简单定义了对象借由通信的协议,而不是某些实现进行的特定协议。 允许系统最大化灵活性是合理的。 MFC 的作用是实现 MFC/C++ 程序的默认行为。

若要了解 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++ 对象所操作的一样,使用运算符删除)。 引用计数机制允许管理单个对象的多个引用。 AddRefRelease 的实现会维持对象上的引用计数,也就是在对象的引用计数到达零时才会删除对象。

从实现的角度来看,AddRefRelease 是非常容易的。 此处为普通实现:

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

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

QueryInterface 成员函数更为有趣。 仅有的成员函数为 AddRef发布 的对象不值得拥有,因为让对象执行比 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 的代码中。 这将减少代码量并避免 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 的接口映射核心功能如下:

此外,接口映射支持以下高级功能:

  • 支持创建可聚集的 COM 对象

  • 支持在实现对象 COM 时使用聚合对象

  • 实现是可设计和延展的

有关聚合的更多信息,请参阅聚合主题。

MFC 的接口映射支持来自于 CCmdTarget 类。 CCmdTarget”引用计数和所有与 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 可以委托这些函数 (ExternalAddRefExternalReleaseExternalQueryInterface)的 CCmdTarget 实现。

以上 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”开头的全局类和以“I”开头的接口类进行区分)。 分别创建这些类的嵌套成员: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 时,框架将使用映射中的第一接口(在这种情况下,m_CPrintObj)。

尽管 BEGIN_INTERFACE_PART 宏会为您自动声明 AddRef发布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 并发布

  • 声明这些在两个接口上的内置方法中的一个

此外,框架在内部使用消息映射。 这可使您从框架类派生,表明 COleServerDoc 已支持某些接口并向由框架提供的接口提供替换项或添加项。 由于框架完全支持从基类继承接口,因此您可进行此操作。 这因为此,BEGIN_INTERFACE_MAP 采用其第二个参数作为基类的名称。

备注

通常不可能仅通过集成嵌入式专用化的专用来重新使用 OLE 接口的 MFC 内置实现的实现。这不可能实现,因为使用 METHOD_PROLOGUE 宏获取包含 CCmdTarget 派生对象的访问权限暗指 CCmdTarget 派生对象的嵌入对象的“固定偏移量”。举例来说,这意味着您不能在 COleClientItem::XAdviseSink 中从 MFC 派生嵌入的 XMyAdviseSink,因为 XAdviseSink 依赖于 COleClientItem 对象顶部的指定偏移量。

备注

但是,您可为您希望 MFC 的默认行为的所有函数委托到 MFC 实现。这可在 COleFrameHook 类(对于许多函数可委托 m_xOleInPlaceUIWindow)中的 IOleInPlaceFrame (XOleInPlaceFrame) 的 MFC 实现中完成。选择这种设计旨在减少实现多种接口的对象的运行时大小;它可以避免使用后向指针(例如在上一节使用 m_pParent 的方式)。

聚合和接口映射

除了支持独立的 COM 对象外,MFC 还支持聚合。 聚合这个主题太复杂,无法在这里讨论;请参阅聚合主题获取有关聚合的详细信息。 此说明将扼要介绍对生成框架和接口映射的聚合的支持。

用聚合有两种方法:(1) 使用支持聚合的 COM 对象和 (2) 实现可由另一个对象聚合的对象。 这些功能可作为“使用聚合对象”和“使对象可聚集”引用。 MFC 两个都支持。

使用聚合对象

若要使用聚合对象,需要某种方法将此聚合联系到 QueryInterface 机制。 换言之,聚合对象必须像您对象的本机部分一样运行。 那这如何联系到 MFC 的接口映射机制? 除了在其中将嵌套对象映射到 IID 的 INTERFACE_PART 宏外,还可以将聚合对象声明为 CCmdTarget 派生类的一部分。 为此,将使用 INTERFACE_AGGREGATE 宏。 这可使您指定成员变量(此变量必须是指向 IUnknown 或派生类的指针),这将被集成到接口映射机制。 如果调用 CCmdTarget::ExternalQueryInterface 时指针为 NULL,且如果请求的 IID 不是 CCmdTarget 对象本身支持的本地 IID 之一,则该框架将自动调用聚合对象的 QueryInterface 成员函数。

使用 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。 框架在 QueryInterface 的默认实现中忽略了 NULL 成员变量。 OnCreateAggregates 实际上是创建您聚合对象的适合位置。 如果您要在 COleObjectFactory 的 MFC 实现的外部创建对象,则必须显式调用它。 当讨论聚合对象的创建时,在 CCmdTarget::OnCreateAggregates 中创建聚合以及使用 CCmdTarget::GetControllingUnknown 的原因就会变得很明显。

该项技术可为您呈现所有接口对象,这些接口的聚合对象可支持附加其本机接口。 如果您只需要聚合支持的接口的子集,可以重写 CCmdTarget::GetInterfaceHook。 这可使您降低 hookability,类似于 QueryInterface。 通常,您需要聚合支持的所有接口。

使对象实现变成可聚集的

对于待聚集的对象,必须将 AddRef发布QueryInterface 的实现委托给“未知的控制。”换言之,因为它是对象的一部分,它必须把 AddRef发布QueryInterface 委托到其他对象,也从 IUnknown 中派生。 此“未知控制”在其被创建时就会提供给对象,即提供给 COleObjectFactory 的实现。 将其实现需花费一小笔的开销,并且在某些情况下并不必要,因此 MFC 使其成为可选。 若要启用可聚合的对象,请从该对象的构造函数调用 CCmdTarget::EnableAggregation

如果对象还使用聚合,则必须确定将正确的“未知的控制”传递给聚合对象。 通常,聚合形成后,此 IUnknown 指针传递给对象。 例如,pUnkOuter 参数对于借助 CoCreateInstance 创建的对象而言是“未知的控制”。 正确的“未知的控制”指针可通过调用 CCmdTarget::GetControllingUnknown 进行检索。 但是,从该函数返回的值在构造函数中无效。 因此,建议您只在 CCmdTarget::OnCreateAggregates 重写中创建自己的聚合,该重写中从 GetControllingUnknown 返回的值是可靠的(即使从 COleObjectFactory 实现中创建)。

添加或释放人工引用计数时,务必使对象操作正确的引用计数。 为确保这一点,请始终调用 ExternalAddRefExternalRelease 而不是 InternalReleaseInternalAddRef。 很少在支持聚合的类上调用 InternalReleaseInternalAddRef

参考资料

要使用OLE 的高级用法(例如定义自己的接口或替代框架对 OLE 接口的实现),需要使用基础接口映射机制。

本节讨论了用于实现这些高级功能的每个宏和 API。

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。 如果该对象进行了聚合,此函数将调用“控制 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 的类必须使用 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 的类的成员。

备注

以 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( 
   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

该结构负责 IUnknown,因为它始终是必需的。

界面部分 - 宏声明

INTERFACE_AGGREGATE( 
   theClass,
   theAggr 
)

备注

参数

  • theClass
    包含接口映射的类的名称,

  • theAggr
    要聚合的成员变量的名称。

备注

此宏可用于告知框架,类正在使用聚合对象。 它必须出现在 BEGIN_INTERFACE_PARTEND_INTERFACE_PART 宏之间。 复合对象是单独的对象,它是从 IUnknown 派生的。 通过使用聚合和 INTERFACE_AGGREGATE 宏,您可以让聚合支持的所有接口看起来像是受对象直接支持的。 theAggr 参数只是您的类的成员变量的名称,是从 IUnknown 中派生的(直接或间接)。 所有 INTERFACE_AGGREGATE 宏在放置到接口映射中时必须遵循 INTERFACE_PART

请参见

其他资源

按编号列出的技术说明

按类别列出的技术说明