Which COM smart pointer classes to use?

2019-01-19 12:50发布

问题:

I'm confused by the choices for COM smart pointers classes for C++ programming:

There's three four I'm aware of:

  • CCOMPtr from ATL
  • _com_ptr_t from the MS Com Support Classes
  • TComInterface (because I'm using C++Builder 2009)
  • CCOMQIPtr, (which I'd previously forgotten)

I've read about the error vs. exception handling differences of the first two, but TComInterface seems totally undocumented. Both the first two seem to have gotchas or 'unexpected' behaviour, from what I can find.

Ideally, I'd like something that's clean and modern C++, but boost::com doesn't exist as far as I know...

I need to control an application from another vendor. They provide a COM interface via a TLB file.

回答1:

If you are using the type-library import stuff, it will generate code based upon _com_ptr_t and the related COM Support Classes. Go ahead and use those if you are just using stuff from those libraries.

If you are writing ATL-based code, then use CCOMPtr and the other ATL-based classes.

Things may get ugly if you need to mix-and-match both types, but you should probably just get comfortable with both and use whichever makes the most sense for whatever you are doing at the time.

The nice thing is that you have the source for all of these things, so you don't have to rely only on the documentation (which has not been getting much care since .NET appeared).



回答2:

Since you are asking for "clean and modern" C++ style, and giving boost as an example, I'll throw in two more: std/boost::shared_ptr and boost::intrusive_ptr. The intrusive_ptr is obviously the more natural choice, since COM objects have an intrusive reference counting mechanism. shared_ptr works just as well, you only need to use a custom deleter that calls IUnknown::Release(), and a small object generator function that does the IUnknown::AddRef() and returns the smart pointer.

I usually go with the intrusive_ptr, so I'm going to explain that in more detail. First, of course, intrusive_ptr_add_ref and intrusive_ptr_release have to be implemented for all IUnknowns. The intrusive_ptr constructor already has a handy feature to skip adding another reference, since many COM functions will do the AddRef() for you.

Now there's one problem with this approach: the intrusive_ptr does not expose its underlying bare pointer like some of the other COM pointers. This is a problem since a common way to create COM objects is by passing a pointer to a pointer to some creation function in another object. Since you cannot pass the intrusive_ptr into these functions, you end up with clumsy temporary bare-pointers that are used to initialize the intrusive_ptr. This is not very elegant, let alone exception-safe (if you need this at all with COM code, which naturally doesn't throw exceptions. I translate COM errors into exceptions though.)

So what I do here is use another tool function that turns a function that takes any com function and returns a callable where any parameter that was a pointer-to-pointer-to-T can either be that or a reference to an intrusive_ptr. Anything else is just like the "input function". These functions then do all the converting between T** and intrusive_ptr& for me. For example, HRESULT CreateBuffer(IBuffer** bufferOut, int size) becomes a callable with the signature HRESULT CreateBuffer(instrusive_ptr<IBuffer>& bufferOut, int size). They are a bit tedious to write for "all" arities, unless you have a code generator, lots of patience, or I presume, variadic templates. But once you have them, it actually makes working with COM very nice.



回答3:

I've used the first two, CComPtr and _com_ptr_t.

It sounds like you've already read about the difference with errors and exception handling, so pick the one you liked best, or that fits in most consistently with the rest of the error handling in your code.



回答4:

As kenny mentioned you can use CComPtr to manage pointers to the COM Interfaces. This class is well documented (see msdn) and uses automatic reference counting, what, I suppose, you are wanting.

OTOH, Rob question is important: do you really uses COM Interfaces? For just pointers you can use smart pointers from STL or boost libraries which anyway better (in general) than CComPtr.