I can't help myself posting this, although it is not a direct answer to the question.
There's a brilliant MSKB article from the golden ages of COM: INFO: Descriptions and Workings of OLE Threading Models. Still there, and has all the relevant info. The point is, you should not worry about whether there is marshaling or not, if you follow the rules. Just register your object as ThreadingModel=Both
, aggregate the Free-Threaded Marshaler with CoCreateFreeThreadedMarshaler
, and be done. COM will do the marshaling if needed, in the best possible way. Depending on the client's apartment model, the client code may receive the direct pointer to your interface, if it follows the rules too.
Any "alien" interface that you may receive when a method of your interface gets called, will be valid in the scope of the call, because you stay on the same thread. If you don't need to store it, that's all that matters.
If however you do need to cache the "alien" interface, the right way of doing this would be to store it using CoMarshalInterThreadInterfaceInStream
/CoGetInterfaceAndReleaseStream
:
To store it:
- Enter critical section;
- call
CoMarshalInterThreadInterfaceInStream
and store the IStream
pointer in a member field;
- Leave critical section;
To retrieve it
- Enter critical section;
- call
CoGetInterfaceAndReleaseStream
to retrieve the interface
- call
CoMarshalInterThreadInterfaceInStream
and store it again as IStream
for any future use
- Leave critical section;
- Use the interface in the scope of the current call
To release it:
- When you no longer need keeping it, just release the stored
IStream
(inside the critical section).
If the "alien" object is free-threaded too, and the things are happening inside the same process, you will likely be dealing with a direct interface pointer after CoGetInterfaceAndReleaseStream
. However, you should not make any assumptions, and you really don't need to know if the object your dealing with is the original object or a COM marshaller proxy.
This can be slightly optimized by using CoMarshalInterface
w/ MSHLFLAGS_TABLESTRONG
/ CoUnmarshalInterface
/ IStream::Seek(0, 0)
/ CoReleaseMarshalData
instead of CoGetInterfaceAndReleaseStream
/CoGetInterfaceAndReleaseStream
, to unmarshal the same interface as many times as needed without releasing the stream.
More complex (and possibly more efficient) caching scenarios are possible, involving Thread Local Storage. However, I believe that would be an overkill. I did not do any timing, but I think the overhead of CoMarshalInterThreadInterfaceInStream
/CoGetInterfaceAndReleaseStream
is really low.
That said, if you need to maintain a state that stores any resources or objects which may require thread affinity, other than aforementioned COM interfaces, you should not mark your object as ThreadingModel=Both
or aggregate the FTM.