A pattern I sometimes see is calling CoCreateInstance
for an interface, and immediately turning around and querying for another interface, and never using the original interface.¹
wil::com_ptr<IWidget> widget; RETURN_IF_FAILED( CoCreateInstance(CLSID_Widget, nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&widget))); wil::com_ptr<IServiceProvider> provider; RETURN_IF_FAILED( widget->QueryInterface(IID_PPV_ARGS(&provider))); // use the service provider, ignore the widget
There’s rarely any need to request one interface, then immediately exchange it for another interface. You may as well go directly for the interface you want:
wil::com_ptr<IServiceProvider> provider; RETURN_IF_FAILED( CoCreateInstance(CLSID_Widget, nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&provider))); // use the service provider
For non-local interfaces, collapsing the request into a single call avoids a round trip to the server.
The only case I can think of where the initial interface is significant is if you want to ensure that the object supports the intermediate interface, even though you aren’t going to be using it right now.
// Factories must support IServiceProvider. wil::com_ptr<IServiceProvider> provider; RETURN_IF_FAILED( CoCreateInstance(factoryClassId, nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&provider))); wil::com_ptr<IWidget> widget; RETURN_IF_FAILED( provider->QueryInterface(IID_PPV_ARGS(&widget))); // use the widget, ignore the IServiceProvider for now
In that case, you can still avoid a round trip to the server by using CoCreateInstanceEx
to request multiple interfaces at once.
MULTI_QI mqi[2] = { { &__uuidof(IWidget), nullptr, 0 }, { &__uuidof(IServiceProvider), nullptr, 0 }, }; RETURN_IF_FAILED(CoCreateInstanceEx( factoryClassId, nullptr, CLSCTX_LOCAL_SERVER, nullptr, ARRAYSIZE(mqi), mqi)); wil::com_ptr<IWidget> widget; widget.attach(mqi[0].pItf); wil::com_ptr<IServiceProvider> provider; provider.attach(mqi[1].pItf); if (hr != S_OK) { // Failed to get at least one interface. return E_NOINTERFACE; }
You can refer to my earlier series on MULTI_QI
for further discussion.
¹ Note that I’m using WIL only for its smart pointer class and still programming to the ABI for the most part. WIL itself contains wrappers for many of the patterns here, but this article is really about the pattern and not the WIL wrappers.
I’m guilty of this. On the other hand, I’m also guilty of writing a COM class factory that doesn’t support this usage pattern (it only succeeded if you CoCreate(CWidget, IWidget), and you can QI the IWidget to IServiceProvider, but it did not succeed if you CoCreate(CWidget, IServiceProvider)). It was a case of “I had no idea COM could do this”.
Yeah, that breaks the rules. A COM object must be consistent about which interfaces it supports.
I thought one of the COM object Identity rules is that anytime you ask for IUnknown, it is required to represent the same object. But if you ask for another interface it can be a separate object.
But IUnknown has a special, consistent, identity, that must always be the same pointer.
So I always try to get IUnknown first, then let it spider out from there, as i Query for other Interfaces, and it possibly constructs...
While it’s true that IUnknown is special, you rarely rely on the special-ness, and if you really need to, you can QI for it at the point you need it. (Which you should be doing in general because you don’t know whether any random IUnknown given to you is the special identity-defining one.)
Another case is when you decide to respond to a QueryInterface by aggregating a stateless helper inner object without caching the inner object. In that case, the inner object must be created for IUnknown, then immediately queried for the desired interface, and have the IUnknown released.
Last time I checked, the docs say aggregation is not supported for local servers or remote servers. Since it doesn’t say the cross-apartment same-process case, I assume it’s supported —...