在什么条件下是CCmdTarget :: OnFinalRelease调用? [英] Under what conditions is CCmdTarget::OnFinalRelease called?

查看:886
本文介绍了在什么条件下是CCmdTarget :: OnFinalRelease调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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);

//应用程序继续...事件到达...最终事件接收器被关闭

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();

//应用程序继续...事件到达...最终事件接收器被关闭

LPUNKNOWN pUnk = myEventHandler-> GetIDispatch(TRUE);
AfxConnectionUnadvise(myEventSource,DIID_IMyEventInterface,pUnk,TRUE,myCookie);
pUnk-> Release();
delete myEventHandler;
myEventHandler = NULL;

我让这个运行一整天,现在观察到OnFinalRelease从未被调用。被包装的实例的析构函数被调用,如我所料,但我感到不安,因为我显然不明白在什么情况下调用OnFinalRelease。是OnFinalRelease调用了一些延迟,还是有办法强迫它发火?什么会触发OnFinalRelease被调用?



如果重要的话,事件源是通过COM interop暴露事件的.NET程序集。


<使用COM应该总是使用CoCreateInstance()AddRef()和Release()范例来管理对象的生命周期,并让COM对你的对象进行销毁基于参考计数。避免新的和删除,因为使用它们打破了这种模式,并引起有趣的副作用。您可能在管理引用计数时有一个错误。



调试引用计数为什么没有被正确管理的方法是覆盖CCmdTarget :: InternalRelease()从oleunk.cpp复制源代码并放入一些跟踪输出或断点。

  DWORD CMyEventHandler :: InternalRelease b {
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(); //将调用PostNcDestroy
else
PostNcDestroy();
}

FYI:跨线程传递接口而不编组接口指针是另一个常见的原因在COM中获取错误。


The MSDN documentation for the CCmdTarget::OnFinalRelease method is pretty brief:

Called by the framework when the last OLE reference to or from the object is released.

I have created a sub-class of CCmdTarget

class CMyEventHandler : public CCmdTarget { ... }

I'm trying to figure out under what conditions the OnFinalRelease method will be called. I have some code that looks something like this:

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);

Using this code, I observe that the OnFinalRelease method is never called. This means I have a memory leak. So I modified the wrap-up code as follows:

LPUNKNOWN pUnk = myEventHandler->GetIDispatch(FALSE);
AfxConnectionUnadvise(myEventSource, DIID_IMyEventInterface, pUnk, FALSE, myCookie);
delete myEventHandler;
myEventHandler = NULL;

This section of code is triggered off periodically throughout the day. What I notice now is that, while the destructor for the wrapped up instance of myEventHandler is called as expected, the OnFinalRelease function is getting called now! What's worse, it is being called not on the instance that has been wrapped up, but instead on a newly created instance of CMyEventHandler! Thinking that this might be due to a reference counting issue, I modified my wire-up and wrap-up code:

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;

I let this run all day and now observe that OnFinalRelease is never called. The destructor for the wrapped up instance is called as I would expect, but I'm left feeling uneasy as I clearly don't understand the circumstances under which OnFinalRelease is called. Is OnFinalRelease called on some delay, or is there a way to force it to fire? What will trigger OnFinalRelease to be called?

If it matters, the event source is a .NET assembly exposing events via COM interop.

解决方案

With COM you should always use the CoCreateInstance() AddRef() and Release() paradigm to manage lifetime of your objects, and let COM do the destruction of your objects based on reference counts. Avoid new and delete because using them breaks this paradigm and causes interesting side effects. You probably have a bug in the management of the reference counts.

The way to debug why the reference counts are not being managed correctly is to override CCmdTarget::InternalRelease() copy the source from oleunk.cpp and put some trace output or break points.

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;
}

There are lots of times when passing IDispatch interfaces that code will bump reference counts and you have to decrement the reference count using Release(). Pay attention to where your code may be passing this interface because there is aconvention in COM that when Interfaces are passed using [in] or [out] where the caller or callee has to release the interface.

When the reference count issue is corrected you shoudl see the objects OnFinalRelease code being called and the object destoryed by hte MFC framework:

For CCmdTarget the destruction should happen as a result of the final release in the parent class CWnd:

void CWnd::OnFinalRelease()
{
    if (m_hWnd != NULL)
        DestroyWindow();    // will call PostNcDestroy
    else
        PostNcDestroy();
}

FYI: Passing interfaces across threads without marshalling the interface pointers is another common reason to get errors in COM.

这篇关于在什么条件下是CCmdTarget :: OnFinalRelease调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆