当使用COM时,我通常依赖于ATL智能指针,如ATL::CComPtr
和ATL::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文档类