C++ CComPtr和std::共享的ptr互操作性

C++ CComPtr和std::共享的ptr互操作性,c++,com,shared-ptr,C++,Com,Shared Ptr,考虑以下场景。有一个ATL包的C++类( CMyComClass 实现了 IMyComClass >代码>),这个类用于异步操作,因此它(应该派生)派生自 STD::Enable SydDyFasux这个< /Cuff>提供 SydRyFasux()/(而不是通过代码> < 到异步函数)这将确保在调用异步操作时对象仍然存在。另一方面,有一个COM接口,例如可以要求将上述类作为COM对象返回,类似于get(IUnknown**myobject)或addObject(IUnkown*mynewobj

考虑以下场景。有一个ATL包的C++类(<代码> CMyComClass <代码>实现了<代码> IMyComClass >代码>),这个类用于异步操作,因此它(应该派生)派生自<代码> STD::Enable SydDyFasux这个< /Cuff>提供<代码> SydRyFasux()/<代码>(而不是通过代码> < <代码>到异步函数)这将确保在调用异步操作时对象仍然存在。另一方面,有一个COM接口,例如可以要求将上述类作为COM对象返回,类似于
get(IUnknown**myobject)
addObject(IUnkown*mynewobject)
来添加对象。在这种情况下,我处于死锁状态,我不能从COM获取原始指针并分配给
共享的\u ptr
,因为我没有转移所有权,而
共享的\u ptr
的引用计数将是错误的,因为它不计算以前的COM引用,此外,
shared_ptr
计数的增加不会影响
CComPtr
ref计数,这意味着指针可能随时被销毁。此外,还有
CMyComClass
的成员函数,它们可以创建,例如,
std::async
操作,将
传递到其中,同样,包装COM可能被破坏,我将被留下悬空指针。有没有办法克服这个问题?在
IUnknown
上,是否有此中的
共享\u的等价物
是的,我知道,设计有缺陷,不,我现在无法更改

EDIT001: 我想我把问题复杂化了。 让我们从根本问题开始。 考虑以下COM类

class ATL_NO_VTABLE CMyComObject
    : public CComObjectRootEx<CComMultiThreadModel>,
      public CComCoClass<CMyComObject, &CLSID_MyComObject>,
      public IDispatchImpl<IMyComObject, &IID_IMyComObject, &LIBID_ATLProject2Lib, /*wMajor =*/1, /*wMinor =*/0>
{
public:
    CMyComObject() { m_pUnkMarshaler = NULL; }

    DECLARE_REGISTRY_RESOURCEID(IDR_MYCOMOBJECT)

    BEGIN_COM_MAP(CMyComObject)
    COM_INTERFACE_ENTRY(IMyComObject)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.p)
    END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()
    DECLARE_GET_CONTROLLING_UNKNOWN()

    HRESULT FinalConstruct()
    {
        m_asyncTask = std::async(std::launch::async, [self{this}]() { std::cout << typeid(self).name() << std::endl; });
        return CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &m_pUnkMarshaler.p);
    }

    void FinalRelease() { m_pUnkMarshaler.Release(); }

    CComPtr<IUnknown> m_pUnkMarshaler;

private:
    std::future<void> m_asyncTask;
};

这将是一个很好的、优雅的解决方案,效果非常好。但是,类本身持有一个对对象的引用,因此
std::shared\u ptr
的删除程序永远不会生效。唉!做正确的事情,从COM类中获取这些东西,然后将提取的东西保留在COM类中的 SyddYPPT/Cuth>。< P> >,直接的方法是不混合C++和COM引用计数器,并为COM接口创建一个瘦的C++包装对象,这可能是异步操作本身使用的:

class t_MyComClassWrapper: std::enable_shared_from_this< t_MyComClassWrapper >
{
    private: CComPtr< IMyComClass > m_p_my_class;

    public: t_MyComClassWrapper(void)
    :    m_p_my_class(new CMyComClass)
    {}

    // TODO forward IMyComClass methods...
};

auto p_wrapper(::std::make_shared< t_MyComClassWrapper >());
class t\u MyComClassWrapper:std::从\u this启用\u共享\u
{
私人:CComPtrm\u p\u my\u class;
public:t_mycomclasswapper(void)
:m_p_my_类(新CMyComClass)
{}
//TODO转发IMyComClass方法。。。
};
自动p_包装(::std::使_共享());

此设计将确保对象不会被破坏,直到C++和COM引用计数器都降至0。

正如其他人所提到的,COM对于跨线程使用接口有非常严格的规则,而只是通过不遵循这些规则来调用bug。这是许多COM开发人员将核心逻辑创建为C++类,然后将这些C++类封装在瘦COM对象中的原因之一。这是我的建议。您的核心对象应该没有COM感知的内容。如果它当前有其他COM接口指针的数据成员,那么做同样的事情——将子对象提取到C++类中,并为父对象的COM包装器所拥有的子对象提供COM包装器。

class CMyObject : std::enable_shared_from_this
{
public:
    void Startup()
    {
        // non-COM stuff
        auto self = shared_from_this();
        m_asyncTask = std::async(std::launch::async, [self]() {
             std::cout << typeid(self).name() << std::endl;
        });
    }
private:
    std::future<void> m_asyncTask;
}

class ATL_NO_VTABLE CMyComObject
    : public CComObjectRootEx<CComMultiThreadModel>,
      public CComCoClass<CMyComObject, &CLSID_MyComObject>,
      public IDispatchImpl<IMyComObject, &IID_IMyComObject, &LIBID_ATLProject2Lib, /*wMajor =*/1, /*wMinor =*/0>
{
    ...
    HRESULT FinalConstruct()
    {
        m_internal = make_shared<CMyObject>();
        m_internal->Startup();
        // COM stuff
        return CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &m_pUnkMarshaler.p);
    }
    ...
private:
    shared_ptr<CMyObject> m_internal;
    ...
}
class-CMyObject:std::从\u中启用\u共享\u
{
公众:
无效启动()
{
//非COM内容
auto self=shared_from_this();
m_asyncTask=std::async(std::launch::async,[self](){

std::您可以自由地为用户定义的类专门化std模板。在这种情况下,IMyComClass是一个用户定义的类,所以只需编写std::shared的适当包装器专门化即可_ptr@RichardHodges,@Niall,我想你们遗漏了一个重要的点,如果我从
enable\u shared\u这个
实例m派生我的类例如,必须由
std::make_shared
创建。但是,由于它是一个COM类,所以它可以(而且很可能会)由
CreateInstance
new ActiveXObject
创建。在这种情况下,
启用\u shared\u将如何工作?想想脚本或ATL代码执行
CreateInstance()
或`新的ActiveXObject(“SomeClassId”)'要创建类的实例,包装器应该如何帮助?@kreuzerkrieg的想法是将com ptr保留在包装器中,而不管实际用于创建com类实例的方法是什么。您可以在外部创建它,然后将
CComPtr
传递到
t\u mycoClassWrapper
constructor.a)它需要在客户端进行更改。b)可以从导入库的script/c#/ATL代码访问IMyComClass。无法访问包装器,您想如何将COM实例传递给包装器?顺便说一句,检查问题edit001,确保它是正确的(而且很明显)要做的事情。但这意味着大量的工作,我的课程比示例中的要复杂一些。我正在寻找一些解决这个问题的巧妙方法,但除了你提到的方法外,我看不到任何解决方法。谢谢!
class CMyObject : std::enable_shared_from_this
{
public:
    void Startup()
    {
        // non-COM stuff
        auto self = shared_from_this();
        m_asyncTask = std::async(std::launch::async, [self]() {
             std::cout << typeid(self).name() << std::endl;
        });
    }
private:
    std::future<void> m_asyncTask;
}

class ATL_NO_VTABLE CMyComObject
    : public CComObjectRootEx<CComMultiThreadModel>,
      public CComCoClass<CMyComObject, &CLSID_MyComObject>,
      public IDispatchImpl<IMyComObject, &IID_IMyComObject, &LIBID_ATLProject2Lib, /*wMajor =*/1, /*wMinor =*/0>
{
    ...
    HRESULT FinalConstruct()
    {
        m_internal = make_shared<CMyObject>();
        m_internal->Startup();
        // COM stuff
        return CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &m_pUnkMarshaler.p);
    }
    ...
private:
    shared_ptr<CMyObject> m_internal;
    ...
}