2013-03-14 52 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