异常安全的COM内存处理



当使用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。

可以使用rid,如果我可以这样做一个删除器:

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提供的一些东西?

ATL已经有这个了:

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

CHeapPtr可以被派生出来,以类似的方式实现其他资源释放器。CComHeapPtr是一个MSDN文档类

相关内容

  • 没有找到相关文章

最新更新