10

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

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

4

1 回答 1

14

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 文档类

于 2013-03-14T21:01:05.463 回答