其他分享
首页 > 其他分享> > c-在什么情况下调用CCmdTarget :: OnFinalRelease?

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