作为事件接收器使用的CCmdTarget的正确生命周期管理是什么?

What is the proper lifetime management for a CCmdTarget used as an event sink?

本文关键字:生命 周期 管理 是什么 CCmdTarget 事件 接收器      更新时间:2023-10-16

我遵循给出的"如何在基于mfc的COM客户端中创建接收器接口"的微软示例代码,以便在c++ (VC6)中创建事件接收器。事件源是一个。net程序集,通过COM互操作公开其功能。

让我感到不适的是样本的最后一个音符:

因为CMySink是在堆上创建的,所以请确保将其删除到避免内存泄漏。

有几件事我要注意:

  • GetIDispatch和AfxConnectAdvise/AfxConnectUnadvise的自动递增/递减参数设置为FALSE,因此我假设sink的内部引用计数在整个练习中保持不变。
  • OnFinalRelease方法在练习中没有显示,所以我假设它是删除sink实例的默认行为。

记住示例文本中的最后一个注意事项,我的清理代码看起来像这样:

//Get a pointer to sinks IUnknown, no AddRef.
LPUNKNOWN pUnkSink = m_pSink->GetIDispatch(FALSE);
//Terminate a connection between source and sink.
//m_pUnkSrc is IUnknown of server obtained by CoCreateInstance().
//m_dwCookie is a value obtained through AfxConnectionAdvise().
AfxConnectionUnadvise(m_pUnkSrc, IID_MYEVENT, pUnkSink, FALSE,
   m_dwCookie);
delete m_pUnkSink;

这个示例中的代码在一个循环中运行,该循环包括创建接收器、将其连接起来、等待几个事件,然后将其拆除并删除。我看到的是,经过几轮循环后,OnFinalRelease突然被调用。不仅如此,OnFinalRelease在当前循环迭代的接收器实例上被调用(而不是循环的先前迭代使用的某个先前实例)。结果是当前接收器在当前循环执行下被删除,并导致一堆空指针错误。

我尝试删除删除m_pUnkSink的调用。结果是OnFinalRelease永远不会被调用。这给我留下了一个内存泄漏,因为所有这些接收器的实例都累积在堆中。

我想我可能可以在循环的每次迭代中重用相同的接收器实例,但我很好奇正确的生命周期管理是什么。我是否需要在删除实例自己和重写OnFinalRelease之间做出明确的选择,不做任何事情,而不是永远删除自己,总是期望OnFinalRelease执行删除?一个比另一个更受欢迎吗?

OnFinalRelease的默认实现是删除对象。最好将CCmdTarget子类化以获得消息映射和事件钩子,并创建自己的类,该类可以保留在所需的所有处理范围内,然后只在最后删除一次。您可以添加一个回调或通知消息来表示正在完成的所有操作,以清理您的对象。

如果没有其他原因,你不希望在循环中从堆中分配和删除内存。

更新:

如果默认实现是不需要的,就像MFC的情况一样,那么你最好的选择是覆盖virtual OnFinalRelease,让它什么都不做,而不是像在CCmdTarget中那样调用delete this;。然后就完全由你自己来删除你的对象并清理。如果你有c++ 0x支持,我会让你的sink类引用计数对象,或者把它放在像std::unique_ptr这样的智能指针中。