2011-07-27 17 views
1

MSDN文檔爲CCmdTarget的:: OnFinalRelease方法很簡單:在什麼條件下調用CCmdTarget :: OnFinalRelease?

框架調用的最後一個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

似乎你沒有打電話給myEventHandler->Release()。因此,最後的參考不會被釋放,並且從不調用OnFinalRelease

+0

我的印象是第一次AddRef發生在調用GetIDispatch時,而不是在構造上,所以沒有必要在myEventHandler上調用Release。這是不正確的? – JadeMason

+0

施工後立即查看「CCmdTarget」的'm_dwRef'成員。我打賭它是1.(或者你可以檢查MFC的源代碼,MFC附帶源代碼。) –

2

使用COM,您應始終使用CoCreateInstance()AddRef()和Release()範例來管理對象的生命週期,並讓COM根據引用計數來銷燬對象。避免新的和刪除,因爲使用它們打破了這種範式,並導致有趣的副作用。您可能在引用計數的管理中存在一個錯誤。

調試引用計數不被正確管理的原因的方法是覆蓋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]傳遞接口時,主叫方或被叫方必須釋放接口。

當引用計數問題被糾正你shoudl看清物體OnFinalRelease代碼被調用和對象的興田MFC框架destoryed:

對於CCmdTarget的破壞應該發生在父最終 釋放的結果類的CWnd:

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

FYI:橫穿線接口,而無需編組接口指針是另一個常見的原因,去在COM錯誤。

相關問題