CCmdTarget :: OnFinalRelease方法的MSDN文档非常简短:
框架在最后一次OLE引用或从中引用时调用 对象被释放。
我创建了一个CCmdTarget的子类
class CMyEventHandler : public CCmdTarget { ... }
我试图找出在什么条件下调用OnFinalRelease方法。我有一些看起来像这样的代码:
CMyEventHandler* myEventHandler = new CMyEventHandler();
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(FALSE);
AfxConnectionAdvise(myEventSource, DIID_IMyEventInterface, pUnk, FALSE, myCookie);
// Application continues...events arrive...eventually the event sink is shutdown
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(FALSE);
AfxConnectionUnadvise(myEventSource, DIID_IMyEventInterface, pUnk, FALSE, myCookie);
使用此代码,我发现永远不会调用OnFinalRelease方法。这意味着我有内存泄漏。所以我修改了总结代码如下:
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(FALSE);
AfxConnectionUnadvise(myEventSource, DIID_IMyEventInterface, pUnk, FALSE, myCookie);
delete myEventHandler;
myEventHandler = NULL;
此部分代码会在一天内定期触发。我现在注意到的是,虽然myEventHandler的包装实例的析构函数按预期调用,但OnFinalRelease函数现在被调用了!更糟糕的是,它不是被包装的实例调用,而是在新创建的CMyEventHandler实例上调用!认为这可能是由于引用计数问题,我修改了我的连线和总结代码:
CMyEventHandler* myEventHandler = new CMyEventHandler();
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(TRUE);
AfxConnectionAdvise(myEventSource, DIID_IMyEventInterface, pUnk, TRUE, myCookie);
pUnk->Release();
// Application continues...events arrive...eventually the event sink is shutdown
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(TRUE);
AfxConnectionUnadvise(myEventSource, DIID_IMyEventInterface, pUnk, TRUE, myCookie);
pUnk->Release();
delete myEventHandler;
myEventHandler = NULL;
我让这一整天运行,现在观察到从未调用OnFinalRelease。包装实例的析构函数按照我的预期调用,但是我感到不安,因为我显然不了解调用OnFinalRelease的情况。 OnFinalRelease是否有一些延迟,或者有没有办法迫使它发射?什么会触发OnFinalRelease被调用?
如果重要,事件源是一个.NET程序集,通过COM interop公开事件。
答案 0 :(得分:3)
使用COM,您应始终使用CoCreateInstance()AddRef()和Release()范例来管理对象的生命周期,并让COM根据引用计数来销毁对象。避免使用new和delete,因为使用它们会破坏这种范例并导致有趣的副作用。您可能在引用计数管理方面存在错误。
调试引用计数未正确管理的原因的方法是覆盖CCmdTarget :: InternalRelease()从oleunk.cpp复制源并放置一些跟踪输出或断点。
DWORD CMyEventHandler::InternalRelease()
{
ASSERT(GetInterfaceMap() != NULL);
if (m_dwRef == 0)
return 0;
LONG lResult = InterlockedDecrement(&m_dwRef);
if (lResult == 0)
{
AFX_MANAGE_STATE(m_pModuleState);
OnFinalRelease();
}
return lResult;
}
传递IDispatch接口时,很多时候代码会影响引用计数,您必须使用Release()减少引用计数。请注意您的代码可能通过此接口的位置,因为COM中存在使用[in]或[out]传递接口的情况,其中调用者或被调用者必须释放接口。
当引用计数问题得到纠正时,你应该看到被调用的对象OnFinalRelease代码以及被hte MFC框架破坏的对象:
对于CCmdTarget,销毁应该是最终结果 在父类CWnd中发布:
void CWnd::OnFinalRelease()
{
if (m_hWnd != NULL)
DestroyWindow(); // will call PostNcDestroy
else
PostNcDestroy();
}
仅供参考:在没有编组接口指针的情况下跨线程传递接口是在COM中获取错误的另一个常见原因。
答案 1 :(得分:0)
您似乎没有打电话给myEventHandler->Release()
。因此,永远不会释放最后一个引用,并且永远不会调用OnFinalRelease
。