Exception-safe memory handling with COM
Asked Answered
W

1

11

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?

Wilsonwilt answered 14/3, 2013 at 20:36 Comment(3)
What's so bad about freeing the memory yourself? To me, it doesn't seem worth the effort to do anything else.Kiva
@evanmcdonnal: Exception safety.Wilsonwilt
@Kiva - or just not having to worry about an early return somewhere that bypasses the clean up code. Even if you get it right initially, some maintenance dev may break it later without anyone realizing. RAII is just safer.Clubhouse
B
15

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 .

Bumpkin answered 14/3, 2013 at 21:1 Comment(7)
I saw CComHeapPtr in the ATL docs, but I didn't realize that CoTaskMemAlloc/Free use CComAllocator. My next reaction was, "How does that compile?", since &pszName would appear to be CComHeapPtr<WCHAR>* rather than WCHAR**. Then I discovered that the ATL pointer classes overload unary operator&, which I thought was forbidden but is actually just strongly discouraged. That overload also prevents you from keeping those in an STL container.Wilsonwilt
The smart template class is similar to well known CComPtr by wrapping pointer and taking one reference * from the template argument: IUnkown* is wrapped by CComPtr<IUnknown>, and WCHAR* is wrapped by CHeapPtr<WCHAR>. Yes both overload & in particular to raise assertion failure once you attempt to expose non-NULL pointer as a placeholder to accept new raw value.Bumpkin
Just out of curiosity: Who discourages writing operator& ?Biomass
@Roman: Sorry, I should have added an <at>Adrian to my comment.Biomass
@StuartRedmann: Several times I've heard the advice to avoid overloading the address-of operator. Before C++11, STL containers couldn't hold objects with an overloaded address-of operator, which may have been a motivation for that advice. For example, see "Resistance to overloaded address-of operators" on this page: msdn.microsoft.com/en-us/library/hh567368.aspxWilsonwilt
@Adrian: Ah, I remember. I agree insofar that one should know the consequences of overloading the address-of operator. However, I think that STL containers should still work for objects that provide a non-standard operator& (IOW, an operator& that does some fancy stuff). I can't imagine why STL containers need to know the address of an object except for efficiency reasons.Biomass
@myself: Found it: #2720332Biomass

© 2022 - 2024 — McMap. All rights reserved.