2013-03-14 29 views
8

使用COM時,我通常依靠ATL智能指針(如ATL::CComPtrATL::CComBSTR)進行資源管理。但是我調用的一些方法使用輸出參數來返回指向我必須釋放的分配存儲的指針。例如:使用COM的異常安全內存處理

WCHAR *pszName = nullptr; 
if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName))) { 
    DoSomething(pszName); 
    CoTaskMemFree(pszName); 
} 

注意GetDisplayName的字符串分配內存,並通過輸出參數返回一個指向它的指針。來電者的責任是用CoTaskMemFree釋放該內存。

如果DoSomething引發異常,則上面的代碼將會泄漏。我想用pszName的某種智能指針來避免這種泄漏,但API需要WCHAR**,所以我沒有看到如何通過除啞指針地址之外的任何東西。由於我不是分配的人,所以我不能使用RAII。

可以使用RRID如果我能做出這樣的缺失者:

struct CoTaskMemDeleter { 
    void operator()(void *p) { ::CoTaskMemFree(p); } 
}; 

然後返回的指針立即分配到一個標準的智能指針這樣的:

WCHAR *pszName = nullptr; 
if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName))) { 
    std::unique_ptr<WCHAR, CoTaskMemDeleter> guard(pszName); 
    DoSomething(pszName); 
} 

這一工程,但似乎很容易引入額外的防護變量。例如,這種方法將pszName指向釋放的內存,所以很容易再次意外地使用它。

是否有更清晰的方式來使用智能指針或RAII包裝器爲輸出參數返回的COM服務器分配的內存?我錯過ATL提供的東西嗎?

+0

自己釋放記憶有什麼不好?對我來說,做任何事情似乎都不值得。 – evanmcdonnal 2013-03-14 20:40:05

+0

@evanmcdonnal:例外安全。 – 2013-03-14 21:01:07

回答

11

ATL已經有這個一個開箱即用:

CComHeapPtr<WCHAR> pszName; 
const HRESULT nResult = pShellItem->GetDisplayName(..., &pszName); 
// Hooray, pszName will be CoTaskMemFree'd for you on scope leave 
// via ~CComHeapPtr 

CHeapPtr可以推導來實現類似的方式與其他資源釋放劑。 CComHeapPtrMSDN documented class

+1

我在ATL文檔中看到了CComHeapPtr,但我沒有意識到'CoTaskMemAlloc' /'Free'使用'CComAllocator'。我的下一個反應是「如何編譯?」,因爲'&pszName'看起來像是'CComHeapPtr *'而不是'WCHAR **'。然後我發現ATL指針類重載了一元'operator&',我認爲這是被禁止的,但實際上只是強烈地不鼓勵。該過載也會阻止您將它們保存在STL容器中。 – 2013-03-14 21:25:19

+0

智能模板類與衆所周知的'CComPtr'類似,通過包裝指針並從模板參數中引用一個引用'*':'IUnkown *'由'CComPtr '封裝,'WCHAR *'由'CHeapPtr '。是的,無論是否重載'&',特別是在嘗試將非NULL指針作爲佔位符公開以接受新的原始值時引發斷言失敗。 – 2013-03-14 21:34:23

+1

出於好奇:誰不鼓勵編寫操作符&? – StuartRedmann 2013-03-19 13:08:14