Question

Problem:

In COM you occasionally find functions with signatures like this:

HRESULT STDMETHODCALLTYPE GetColorContexts( 
        UINT cCount,
        IWICColorContext **ppIColorContexts,
        UINT *pcActualCount)

The problem this presents for me is that ppIColorContexts must be an initialized array of IWICColorContext *. I have tried referencing the first element of a Vector of ATL::CComPtr<IWICColorContext> with no such luck it won't trigger the () operator so it complains about a type mismatch.

Attempted solutions:

  • vector<ATL::CComPtr<IWICColorContext>> failed due to type mismatch, as noted in the comments this has other issues as CComPtr overloads operator & which breaks STL containers. It seems that this was fixed in C++11 and was included in the STL in VC2010
  • BOOST_SCOPE_EXIT_ALL works but still means I'm manually managing the lifetime of the COM objects which is something I'd like to get away from.

Unattempted solutions:

  • Custom data structure - this is likely what I'll have to do if there is not a more elegant solution, but at least it would allow me to take advantage of destruction semantics properly.
  • Attach a CComPtr after this call - I dislike this solution because it leaves me with a period of execution where the resource may not get released if something goes wrong.
  • std::unique_ptr<IWICColorContext[]> with a custom deleter - I have yet to fully explore this possibility but it would ensure that the COM objects would always get released.
Was it helpful?

Solution 2

Ultimately the solution was described by igor tandetnik in the comments above:

Basically in VC2010+ ATL::CComPtr has a sizeof that is the same as the pointer they represent (e.g. sizeof(ATL::CComPtr<IWICColorContext>) == sizeof(IWICColorContext*)), as best I can tell this is because they have no virtual functions and thus need no vTable. This is however highly dangerous as it's relying on a compiler implementation detail. Thus the following works:

std::vector<ATL::CComPtr<IWICColorContext> > > vec(5);
// CComPtrs are created and initialized here
GetColorContexts(vec.size(), &vec[0].m_T, ...);

Mark brought up a very good point that the solution above was completely dependent on compiler implementation which is dangerous. However the solution of only attaching ATL::CComPtr after the GetColorContexts call was not palattable either as it would not have been exception safe.

Ultimately my solution (tested this morning) is to create a vector<IWICColorContext*> temporarily from the vector<CComPtr<IWICColorContext>> this temporary vector does not increment the ref count and allows me to maintain exception safety.

OTHER TIPS

I would do it by passing a vector of raw pointers to the function, then copying to another vector of CComPtr.

std::vector<IWICColorContext *> vec(5, NULL);
UINT nActualCount = 0;
GetColorContexts(vec.size(), &vec[0], &nActualCount);
std::vector<CComPtr<IWICColorContext> > results(vec.begin(), vec.begin() + nActualCount);

The only unfortunate part is that the CComPtr constructor performs an AddRef so you must do a corresponding Release on the raw pointers before they're lost.

for (auto it = vec.begin(); it != vec.end(); ++it)
    if (*it != NULL)
        (*it)->Release();
vec.clear();

I think that you need something like that:

long lSize = 0;
ptr->GetColorContexts(cCount, NULL, &lSize);//return required amount of contexts
IWICColorContext** ppColorContexts = NULL;
ppColorContexts = new IWICColorContext*[lSize];
ptr->GetColorContexts(cCount, ppColorContexts, &lSize);
//use something to wrap received raw interfaces with CComPtr - 
//for example use for loop to pass them to new container,
//which stores CComPtr<IWICColorContext>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top