使用COM进行异常安全的内存处理

时间:2013-03-14 20:36:57

标签: c++ com atl smart-pointers

使用COM时,我通常依赖ATL智能指针(如ATL::CComPtrATL::CComBSTR)进行资源管理。但是我正在调用的一些方法使用输出参数来返回指向我必须释放的已分配存储的指针。例如:

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指向释放的内存,因此很容易意外地再次使用它。

对于输出参数返回的COM服务器分配的内存,是否有更简洁的方法来使用智能指针或RAII包装器?我错过了ATL提供的东西吗?

1 个答案:

答案 0 :(得分:13)

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