现有的普通 C++ class 是否可以在不转换为 COM class 的情况下实现 IDL 接口?

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

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

这是它的样子:

//-------------
// 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 上引入一种方法,returns 静态向量的副本 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_vProviderInfoIProviderInfoRetriever::GetProviderInfo().

的输出 SAFEARRAY

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

我做了一些广泛的研究,答案很简单:是的,但这并不容易。在不破坏依赖于上述 C++ class.

的现有代码的情况下,您极不可能只拥有一个普通的 C++ class inherit/implement 一个在 IDL 中定义的接口

首先,如果你使用ATL的工具将你的C++class转换成COMclass,你会突然发现这个C++class已经变得抽象了由于引入了所有纯虚函数 ATL 宏。因此,至少,您将通过 ATL 宏将 IUnknownAddRef()Release()QueryInterface() 作为纯虚拟添加到您的 C++ class功能,例如

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++ 实例的任何现有代码 class,无论它们是在堆栈上还是在堆上(使用 new 运算符)。所以,最终你有 2 个选择:

  1. 使用 ATL 的工具将您的 C++ class 变成 COM class。

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

  2. Inherit/Implement直接用IDL手动定义的接口

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

    然而,在没有 ATL 的情况下滚动您自己的 COM 接口实现并不总是那么容易,尤其是当您的 C++ class 曾经涉及复杂的场景时,例如互操作。