现有的普通C++类能否在不变成 COM 类的情况下实现 IDL 接口

Can an existing plain C++ class implement IDL interface without turning into a COM class?

本文关键字:COM 实现 接口 IDL 情况下 C++      更新时间:2023-10-16

我有一个COM对象CProvider,使用ATL实现。此类包含另一个类 CProviderInfo,并维护此内部类类型的对象的静态向量。

下面是它的外观:

//-------------
// CProvider.h
//-------------
//
// COM object class
//
class ATL_NO_VTABLE CProvider :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CProvider, &CLSID_Provider>,
    public Interface1,
    public Interface2
{
public:
    BEGIN_COM_MAP(CProvider)
       COM_INTERFACE_ENTRY(Interface1)
       COM_INTERFACE_ENTRY(Interface2)
    END_COM_MAP()
    //
    // The inner class
    //
    class CProviderInfo
    {
    public:
        CProviderInfo();
        CComBSTR m_strName;
        GUID m_guidRegistration;
    };
private:
    //
    // static vector of inner class type
    //
    static vector<CProviderInfo> m_vProviderInfo;
};

我想做的是在CProvider上引入一个返回静态向量m_vProviderInfo副本的方法。为了尝试按照COM规则进行游戏,我为此目的引入了一个新的IDL接口IProviderInfoRetriever

//---------
// IDL file
//---------
//
// IProviderInfoRetriever interface
//
[
    // uuid, version ... etc.
]
interface IProviderInfoRetriever : IUnknown
{
    HRESULT GetProviderInfo(
        [out, retval] SAFEARRAY(IProviderInfo*) *ppProviderInfo);
}
//
// The interface of the class holding the info
//
[
    // uuid, version ... etc.
]
interface IProviderInfo : IUnknown
{
    [propget] 
    HRESULT Name(
        [out, retval] BSTR *pbstrName);
    [propget]
    HRESULT Registration(
        [out, retval] GUID *pguidRegistration);
}

我的计划是让CProvider实现IProviderInfoRetriever有效地将静态向量m_vProviderInfo的内容复制到IProviderInfoRetriever::GetProviderInfo((的输出SAFEARRAY中。

我的问题是:是否有可能让内部类CProviderInfo实现IProviderInfo这会破坏创建CProviderInfo类型的局部变量的现有代码吗?

我做了一些广泛的研究,答案很简单:是的,但这并不容易。您极不可能只让普通的 C++ 类继承/实现 IDL 中定义的接口,而不会破坏依赖于所述C++类的现有代码。

首先,如果您使用 ATL 的功能将C++类转换为 COM 类,您会突然发现由于 ATL 宏引入的所有纯虚函数,这个C++类已经变得抽象。所以,至少,你会有IUnknownAddRef()Release()QueryInterface() ATL宏作为纯虚函数添加到你的C++类中,

例如
class ATL_NO_VTABLE CProviderInfo:
    public CComObjectRootEx<CComMultiThreadModel>,
    public IProviderInfo
{
public:
    BEGIN_COM_MAP(CProviderInfo)
        COM_INTERFACE_ENTRY(IProviderInfo)
    END_COM_MAP() // This line adds IUnknown's AddRef(), Release(),
                  // and QueryInterface() as pure virtual functions.
    // ...        
};

仅此一项就会破坏用于创建 C++ 类实例的任何现有代码,无论它们是在堆栈上还是堆上(使用 new 运算符(。因此,最终您有 2 个选择:

  1. 使用 ATL 的工具将C++类转换为 COM 类。

    这会将您的C++类变成抽象类。您必须修改源代码中的所有位置,以创建C++类的对象以改用ATL的类,即 CComObjectCComObjectStack ...等。

  2. 直接和手动继承/实现 IDL 中定义的接口。

    这将需要根据您的接口提供您自己的 IUnknownIDispatch 或两者的实现,以及接口本身定义的任何方法的实现。此选项的优点是不太可能破坏代码库中C++类的任何现有用法。

    但是,在没有 ATL 的情况下滚动自己的 COM 接口实现并不总是那么容易,尤其是当您的 C++ 类涉及复杂方案(例如互操作(时。