【问题标题】:Can an existing plain C++ class implement IDL interface without turning into a COM class?现有的普通 C++ 类可以在不转换为 COM 类的情况下实现 IDL 接口吗?
【发布时间】:2016-05-08 20:40:30
【问题描述】:

我有一个使用 ATL 实现的 COM 对象 CProvider。此类包含另一个类 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 的输出 SAFEARRAY 中::GetProviderInfo().

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

【问题讨论】:

  • 如果不是“实现一个或多个 COM 接口的类”,“COM 类”是什么意思?不,没有什么能阻止你在堆栈上创建一个 CProvider 的实例(CProvider 显然是一个“COM 类”,无论你想到什么定义)。
  • 另一种选择,如果触摸CProviderInfo 与您有关,将是编写一个实现IProviderInfo 并将CProviderInfo 作为成员的包装类(或者甚至只是一个指向元素的指针,或原始向量的索引)。这将很好地隔离与 COM 相关的部分,并最大限度地减少对代码库其余部分的占用。
  • @IgorTandetnik 我也是这么想的。经过一番研究,我发现继承 IUnknown 会破坏现有代码,因为它会将 CProviderInfo 变成一个抽象类。不可能实例化 CProviderInfo,除非通过 CComObject、CComObjectStack 和这个类家族,因为它提供了 IUnknown 成员的实现。
  • 是的,抱歉,我忘记了 ATL 的 IUnknown 的两阶段实现。那么,包装器方法看起来更有吸引力。
  • 不用担心。复杂的 COM 之上的 ATL 细节简直是不人道的。

标签: c++ com interop atl midl


【解决方案1】:

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

首先,如果您使用 ATL 的工具将您的 C++ 类转换为 COM 类,您会突然发现,由于 ATL 宏引入的所有纯虚函数,这个 C++ 类已经变成了抽象类。因此,至少,您将通过 ATL 宏将 IUnknownAddRef()Release()QueryInterface() 作为纯虚函数添加到您的 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 运算符)。所以,最终你有两个选择:

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2014-02-11
    • 2011-09-18
    • 2020-11-04
    • 2011-01-21
    • 1970-01-01
    • 1970-01-01
    • 2014-11-14
    • 2021-02-03
    • 1970-01-01
    相关资源
    最近更新 更多