使用 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。
如果我可以制作这样的删除器,我可以使用 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 提供的某些内容?
最佳答案
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 documented class .
关于c++ - 使用 COM 进行异常安全的内存处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15419669/