gpt4 book ai didi

c++ - CComPtr 和 std::shared_ptr 互操作性

转载 作者:搜寻专家 更新时间:2023-10-31 00:54:29 30 4
gpt4 key购买 nike

考虑以下场景。有一个 ATL 包装的 C++ 类( CMyComClass 实现了 IMyComClass ),此类用于异步操作,因此它(应该派生)派生自 std::enable_shared_from_this提供shared_from_this() (而不是将 this 传递给异步函数)这将确保在调用异步操作时对象仍然存在。另一方面,有一个 COM 接口(interface),例如可以要求将上述类作为 com 对象返回,类似于 get(IUnknown** myobject)。或 addObject(IUnkown* mynewobject)添加对象。在这种情况下,我陷入了僵局,我不能只从 COM 获取原始指针并分配给 shared_ptr因为我不转让所有权和 shared_ptr 的引用计数将是错误的,因为它不计算以前的 COM 引用,此外还增加了 shared_ptr count 不会影响 CComPtr引用计数,意味着指针可以随时被销毁。另外还有CMyComClass的成员函数例如,它可以创建 std::async操作,传递 this再次进入其中,包装 COM 可能会被破坏,我将留下悬空指针。有没有办法克服这个问题?有没有等价于shared_from_thisIUnknown .
是的,我知道,设计有缺陷,不,我现在不能改变它

编辑001:我想我把问题复杂化了。让我们从根本问题开始。考虑以下 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::asyncFinalConstruct (不是最合适的地方,但假装它是一个常规的 COM 方法),async被调用,任务被调度,然后实例(COM 对象的实例)被销毁,例如因为实例的引用计数降为零。显然计划任务将失败,比方说访问冲突。如何防止它发生?

EDIT002:只为 lulz
瞧,解决方案!

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(nullptr), m_self(this, [](CMyComObject* p) {
// check if still have COM references
if(p->m_dwRef == 0)
delete p;
})
{
}

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{SharedFromThis()}]() { std::cout << typeid(self).name() << std::endl; });
return CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &m_pUnkMarshaler.p);
}

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

ULONG InternalRelease()
{
if(m_dwRef > 0)
{
_ThreadModel::Decrement(&m_dwRef);
}
// Dont let COM delete the instance if there is shared ptrs in the wild
return m_dwRef + m_self.use_count();
}
std::shared_ptr<CMyComObject> SharedFromThis() { return m_self; }

CComPtr<IUnknown> m_pUnkMarshaler;

private:
std::future<void> m_asyncTask;
std::shared_ptr<CMyComObject> m_self;
};

这将是一个很好的优雅解决方案,效果很好。但是,该类本身持有对该对象的一个​​引用,因此 std::shared_ptr 的删除器永远不会踢。唉!做正确的事,从 COM 类中取出内容,然后将提取的内容保留为 shared_ptr在 COM 类中。

最佳答案

正如其他人所提到的,COM 对于跨线程使用接口(interface)有非常严格的规则,如果您不遵守这些规则,就会招来错误。这是许多 COM 开发人员将其核心逻辑创建为 C++ 类,然后将这些 C++ 类包装在一个瘦 COM 对象中的众多原因之一。这是我的建议。你的核心对象应该没有任何 COM 感知的东西。如果它当前有其他 COM 接口(interface)指针的数据成员,做同样的事情——将该子对象提取到 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;
...
}

关于c++ - CComPtr 和 std::shared_ptr 互操作性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45144724/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com