0

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.
8
  • It wants an array, just pass a plain old array. Any wrapping with smart pointers has to be done after the call. Commented Sep 11, 2013 at 13:13
  • 1
    I think you want something like this: vector<CAdapt<CComPtr<IWICColorContext> > > vec; GetColorContexts(vec.size(), &vec[0].m_T, ...);. CComPtr cannot be placed into STL containers directly, because it overloads operator& which violates container requirements. That's why CAdapt was invented. Commented Sep 11, 2013 at 13:17
  • @HansPassant I was hoping to avoid that but if that is what I must needs do it is what I must needs do... still I was hoping to avoid new Commented Sep 11, 2013 at 14:03
  • @HansPassant what about a std::unique_ptr<IWICColorContext[]> with a custom deleter? Still not ideal... but it should always be released. Commented Sep 11, 2013 at 16:55
  • 1
    @IgorTandetnik that was fixed in C++11 and the fix was included in the TR1 release in VC2010 Commented Sep 11, 2013 at 18:31

3 Answers 3

2

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();
Sign up to request clarification or add additional context in comments.

1 Comment

While it wouldn't be the most efficient thing to do... I could flip this on its head and create a std::vector<IWICColorContext*> from the vector<CComPtr> this would not create reference issues and since the number of contexts is small per image anyway it wouldn't be a drain.
1

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.

2 Comments

You're relying an awful lot on the internals of the CComPtr object here. That's dangerous, especially if it works (today...)
@MarkRansom I've fixed my answer based on your answer
-1

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.