2009-07-29 57 views
1

我們在代碼中使用Native COM支持。一切都很好,只是我們不喜歡錯誤_com_raise_error()被調用,拋出_com_error異常的事實。由於我們有自己的異常層次結構捕獲這個_com_error是不方便的 - 它不在我們的層次結構中,甚至不能從std :: exception繼承。誰擁有IErrorInfo的所有權?

所以我們需要重寫_com_raise_error()。它本身很簡單 - 只需在我們的代碼中定義它,鏈接器就會與它鏈接。

但是不清楚誰擁有IErrorInfo。簽名是

void __stdcall _com_raise_error(HRESULT hr, IErrorInfo* info); 

所以無論誰調用該函數將負責函數返回後調用IErrorInfo :: Release()。但是如果我們拋出一個異常並且控制權會轉移到其他地方,那麼函數將如何返回呢?我們檢查了所謂的AddRef(),然後在進入該函數時立即釋放(),引用計數器爲1.稍後,我們將所有權傳遞給構造的異常對象 - 它在其構造函數中調用AddRef() )在析構函數中。我想這是不正確的,因爲AddRef()會將引用計數增加到2,但只有一個Release()將被調用(在異常析構函數中)。

我正確的說,構造函數中的AddRef()會導致內存泄漏,或者是否有一些內部機制不允許IErrorInfo對象泄漏?

回答

1

我想象一下_com_raise_error會打電話給SetErrorInfo,把它傳遞給IErrorInfo這個對象。與此相關的合同是對信息的引用存儲在本地線程中,因此無論何時設置新信息,舊信息都會被釋放。此外,每當有人以後撥打GetErrorInfo時,信息的所有權將轉移給該來電者。因此,呼叫方有義務在每次可能設置呼叫失敗後調用GetErrorInfo,並相應地釋放該對象。

因此,SetErrorInfo(任何其他傳統COM調用)將調用AddRef在對象上,這樣你就不能與1

2

_com_raise_error()計數器並不意味着回到初始化。無論其類型如何,它都必須引發異常。如果您查看默認實現_com_raise_error(),則提出的_com_error對象將獲取指定的IErrorInfo對象的所有權。 _com_error的構造函數具有默認值爲false的fAddRef參數,因此不調用AddRef()。當_com_error對象被任何異常處理程序捕獲時釋放Release(),從而釋放IErrorInfo對象。

1

添加到其他的答案,這裏有一對夫婦的想法:

  • 一般的COM規則是,在參數並不需要是的AddRef:ED在任何級別,因爲調用是同步的,並該方法運行時引用計數不會奇蹟般地改變。

  • 每個AddRef調用代表一個新的穩定的對象引用,也就是說,在調用AddRef之後,你可以指望對象仍然在那裏。這意味着,如果你想存儲一個接口指針供以後閱讀,你應該調用AddRef。當你不再關心物體的生存時,請致電Release。

所以,既然你想拋出包含的IErrorInfo指針異常對象,該對象應的AddRef它,因爲它需要指向的對象生存。它的析構函數通常會釋放。

我不認爲SetErrorInfo需要參與此 - 這是拋出異常的C選擇。