Exception-safe memory handling with COM

atl, c++, com, smart-pointers

Solution

ATL already has this one out of the box:

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

`CHeapPtr` can be derived to implement other resource releasers in a similar way. `CComHeapPtr` is a MSDN documented class .

Problem

When using COM, I typically rely on ATL smart pointers, like `ATL::CComPtr` and `ATL::CComBSTR`, for resource management. But some of the methods I'm calling use output parameters to return pointers to allocated storage that I have to free. For example: ``` WCHAR *pszName = nullptr; if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName))) { DoSomething(pszName); CoTaskMemFree(pszName); } ``` Note that `GetDisplayName` allocates memory for the string and returns a pointer to it through the output parameter. It's the caller's responsibility to free that memory with `CoTaskMemFree`. If `DoSomething` throws an exception, then the above code will leak. I'd like to use some sort of smart pointer for `pszName` to avoid such leaks, but the API takes `WCHAR**`, so I don't see how I can pass anything but the address of a dumb pointer. Since I'm not the one allocating, I can't use RAII. I can use RRID if I can make a deleter like this: ``` struct CoTaskMemDeleter { void operator()(void *p) { ::CoTaskMemFree(p); } }; ``` And then immediately assign the returned pointer to a standard smart pointer like this: ``` WCHAR *pszName = nullptr; if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName))) { std::unique_ptr<WCHAR, CoTaskMemDeleter> guard(pszName); DoSomething(pszName); } ``` That works, but it seems error-prone to introduce an extra guard variable. For example, this approach leaves `pszName` pointing at the released memory, so it would be easy to accidentally use it again. Is there a cleaner way to use a smart pointer or an RAII wrapper for COM-server allocated memory returned by output parameter? Am I missing something that ATL provides?

Original source

Related problems