c-在什么情况下调用CCmdTarget :: OnFinalRelease?
作者:互联网
CCmdTarget :: OnFinalRelease方法的MSDN文档非常简短:
Called by the framework when the last OLE reference to or from the
object is released.
我创建了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互操作公开事件.
解决方法:
使用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代码,并且该对象已由MFC框架销毁:
对于CCmdTarget,销毁应作为最终结果
在父类CWnd中发布:
void CWnd::OnFinalRelease()
{
if (m_hWnd != NULL)
DestroyWindow(); // will call PostNcDestroy
else
PostNcDestroy();
}
仅供参考:跨线程传递接口而不编组接口指针是导致COM错误的另一个常见原因.
标签:c,visual-c,mfc,com,com-interop 来源: https://codeday.me/bug/20191011/1890372.html