What are the rules for CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream?


Last time, we looked at the recommended ways of marshaling objects between apartments or between processes, but what if those mechanisms are not available to you? (Or if you're simply curious about the lower-level constructions that make the recommended ways possible.)

How do I share an object in one apartment with another apartment in the same process? (Assuming that the Ro­Get­Agile­Reference function is not available.)

This is the most common case. You have an interface pointer that is valid in one apartment, and you want to use that interface pointer in another apartment. The pattern for this is

  • On the originating apartment, call Co­Marshal­Inter­Thread­Interface­In­Stream. (What a mouthful.) This takes the object and generates a bunch of bookkeeping that allows the object reference to be used on a second apartment. The mental model for this is that you took the object, did an Add­Ref, and stored the reference count in a stream. The stream that it returns is safe for multi-threaded use.
  • Transmit the stream to the second apartment by whatever means you wish. Since the source and destination apartments are in the same process, you can just copy the IStream* pointer across.
  • The second apartment calls Co­Get­Interface­And­Release­Stream. This reconstitutes the object from the stream in a form that can be used by the second apartment, transferring the reference count from the stream to the reconstituted object, and releases the stream. (This last clause is such a nasty gotcha, I'm going to devote a special day to it.)
  • The second apartment happily accesses the object.

Behind the scenes, different things happens depending on the nature of the object you marshaled.

If the object being marshaled is apartment-threaded, then it expects that all its methods are called only on the original apartment. In this case, unmarshaling creates a proxy object.

(Obvious special case: If the apartment-threaded object is unmarshaled in the same apartment as it was marshaled, then no proxy is needed. But if you're going do that, why bother with marshaling in the first place?)

If the object being marshaled is free-threaded, then it is safe to call from any thread. In that case, unmarshaling the object just gives you a direct pointer to the original object.

Objects can provide custom marshaling behavior. For example, if an object is immutable, it may choose to marshal by value rather than by reference. In that case, unmarshaling creates a clone of the original.

What does the second apartment do when it is finished with the object?

The second apartment calls Release() just like a reference to any other COM object. If the second apartment got a proxy, this releases the proxy, and the proxy will notify the original object that there is one fewer outstanding reference.

What if I change my mind after calling Co­Marshal­Inter­Thread­Interface­In­Stream and decide that I don't want to share the object with a second apartment after all?

In that case, you call Co­Release­Marshal­Data from the originating apartment. This tells COM, "Hi, um, yeah, sorry about that." COM undoes its bookkeeping and releases the original object, thereby restoring the reference count.

What happens when all of the threads associated with the originating apartment release their last references to the object, while the second apartment still has an outstanding reference via a proxy?

The object is not yet destroyed because there is still an outstanding reference from the second apartment. The object gets destroyed only when all outstanding references are released.

What happens if the threads associated with the originating apartment exit while the second apartment still has an outstanding reference to the object via a proxy?

When the original apartment uninitializes COM, any outstanding proxies are disconnected from the underlying object. (When the proxies disconnect, they release their references, which causes the underlying object to be destroyed because there are no longer any references to it.) If somebody tries to use the proxy to talk to the object, the call fails with RPC_E_SERVER_DIED_DNE. The "DNE" stands for "did not execute". (If somebody was talking to the proxy while it was being destroyed, then they will get RPC_E_SERVER_DIED. The missing DNE means that the call may have started executing before the proxy was disconnected.)

What if I want to unmarshal more than once?

Oh, hey, look at the time. We'll pick up this question next time.

Bonus chatter: The need for marshaling comes from two principles:

  1. Don't force objects to support multi-threading.

If you declare your object as apartment model, then you can assume that all accesses to your object will occur from a single thread. This simplifies most objects tremendously, since multi-threading is hard. And it seems rather mean to force an object to support multi-threading if the person writing it has no intention of using it from multiple threads.

You can also look at this as a compatibility constraint: Windows 3.1 didn't support multi-threading, so all COM objects back in Windows 3.1 were implicitly single-threaded. The apartment threading model allows this code to be ported in a relatively straightforward manner.

  1. Let objects have direct pointers to each other whenever possible.

If two objects are in the same apartment and they want to talk to each other, each gets a pointer to the other with no proxy object in between. People writing high-performance code insist on this principle. "I don't want COM getting in my way and slowing down my business logic."

Marshaling reconciles the above two rules.

An alternative design would be that creating an object always created a free-threaded proxy, and all communication with the object would be done through the proxy. When a method is invoked on that proxy, it either would perform a direct call to the underlying object if the method is being invoked on the same thread that the object expects, or it would marshal the call to the correct thread if not. This would allow COM objects to be transmitted freely within a process without the need for explicit marshaling. However, it also violates rule 2.

When C++ added support for threading in C++11, it went a different way: All objects must support being created on any thread, any method can be called from any thread, and some methods are thread-safe, and some aren't, and it is the caller's responsibility to ensure that a previous not-thread-safe method call has completed before it makes a new not-thread-safe method call. This creates a burden on the implementor compared to the apartment model because the object needs to support being called from any thread, and it is responsible for its own marshaling if it needs to access resources that have hard thread affinity. It also creates a burden on the caller compared to using marshaling because the caller needs to keep in mind which methods are thread-safe and which aren't, and it needs to make sure that all threads which are sharing an object work together to ensure that only one not-thread-safe method call is active at a time.²

On the other hand, it means that there is no marshaling for in-process objects.

(C++ doesn't try to address cross-process access to objects, not does it particularly care about making C++ objects consumable from other languages, and it's okay with forcing all consumers of an object to recompile if the object's implementation changes.)

¹ Profound confusion has resulted from the fact that one of the object threading models is called apartment model, thereby overloading the word apartment. I don't know what they were thinking. It means you can have apartment model objects that aren't compatible with your apartment. (Apartment model objects are compatible with single-threaded apartments, but not multi-threaded apartments.)

² You also have to figure out how to solve problems like this:

  • Object A creates an object X.
  • Object A shares object X with object B.
  • Object A registers a callback with object X.
  • Object B calls into object X from thread 1.
  • While object X is processing the request, object A on thread 2 decide that it also wants to call into object X.
  • Object A must wait until object X has completed the request from object B.
  • The request from object B requires object X to invoke the callback registered by object A.
  • But object X cannot invoke the callback because object A is being used by thread 2 right now.

How do you avoid a deadlock?

Comments (19)
  1. Henke37 says:

    While waiting for the next part, there is no rule that you can't just create a brand new stream a second time and unmarshal that.

    [This assumes that you have a way to communicate with the original thread to say "Hey, can you marshal the object for me again?" But if you had that connection, you wouldn't need to be messing with explicit marshaling anyway! You would just have a method called GiveMeTheObjectAgain(IObject** result) and let the standard marshaler do the work. -Raymond]
  2. Antonio 'Grijan' says:

    Footnote 1 isn't referenced anywhere in the article. I assume that it should be referenced near the end of the answer to the first question, but I may be wrong.

    [Footnote 1 is actually a dangling reference from the previous article. This series was originally one giant article, and then I decided to break it up, and what you're seeing is a remnant that didn't get cleaned up properly. -Raymond]
  3. Ben says:

    I feel like these articles are muddling the difference between multi-threaded objects (which can be called from the MTA) and free-threaded objects (which can be called from any apartment). Multi-threaded objects are very common; free-threaded objects are rare and tricky to create.

    [You're right. I'm being sloppy. Multi-threaded objects and free-threaded objects are not the same thing. (Free-threaded objects should more properly be called "free-marshalled objects" since they marshal for free.) -Raymond]
  4. T says:

    > How do you avoid a deadlock?

    If I interpreted my callstacks correctly, COM knows other threads call into your thread while you're waiting, similarly to how QueueUserAPC works. So instead of your thread just waiting, it instead executes whatever other threads want it to do while it waits.

  5. SI says:

    @Ben: Shouldn't multithreaded objects work as neutral/free threaded objects as long as they don't internally store and use COM pointers to other objects?

  6. SI says:

    @T: Or you get RPC_E_CANTCALLOUT_ININPUTSYNCCALL

  7. Ben says:

    @SI: Yes, a multi-threaded object becomes free-threaded by aggregating the free-threaded marshaler and making sure it doesn't store any pointers to other COM objects.

    I'm just saying that free-threaded objects are a very special case, and let's not confuse them with the more common case of vanilla multi-threaded objects.

  8. John Doe says:

    @Ben, @SI,

    You're confusing things just like Raymond explained.

    An object declares the types of apartment it supports with the ThreadingModel configuration:

    <none>: main STA

    Apartment: any STA

    Free: MTA

    Both: any STA, MTA or NA (yes, both three!)

    Neutral: NA

    What you are probably talking about is the hack that was common before the neutral apartment, which consists of setting the TheadingModel to Both and aggregating the free-threaded marshaler.

    The major drawback of the FTM is that you must, manually (read that again), marshal or pass through the global interface table every interface pointer you get on or return from the FTMed object.

    Another drawback is that the FTMed object's lifetime is still bound to the apartment where it was created.

    The neutral apartment solves both these problems with lightweight proxy/stubs that avoid full marshaling for the object itself and performing only apartment bookkeeping.  Since it's an apartment, objects created in other apartments passed to or returned from the NA will be marshaled as usual with the optimizations Raymond just mentioned in this blog post according to the underlying thread's current apartment.  However, a thread only ever runs in the NA temporarily, either through activation (class object) or through neutral object method calls.

    You can hack your way into having an NA thread by entering an STA or the MTA and calling a method on a ThreadingModel = Neutral object that performs whatever you want indefinitely.  You can just as well achieve this with registration-free COM to achieve e.g. a CoInitializeEx2 that can make a thread belong to a NA-over-[STA|MTA].  However, since this is only useful in practice to activate ThreadingModel = Both objects in the NA, you might as well have a TheardingModel = Neutral object that simply calls CoGetClassObject or CoCreateInstance[Ex] for you.

    Since there are much more documented sources of aggregating the FTM and so few about the NA, we'll live in the dark COM ages forever.  Even Windows Store Apps simply added an extension to the STA (ASTA, or Application STA) that blocks unrelated (i.e. top-level re-entrancy) calls (probably, what the STA should have been from the start).

    @Raymond, why is the neutral apartment taboo outside COM+?  In COM's base documentation, the only mention of NA is in the CoInitialize[Ex], CoGetCallerTID, CoFreeUnusedLibrariesEx, APTTYPE, APTTYPEQUALIFIER and InprocServer32 articles, none of which develops any further about it.

    [The neutral apartment was added after the other apartments became well-established, so everybody forgets to invite him to the party. -Raymond]
  9. Goran says:

    Raymond, is there a reason to have already two articles about marshaling, but no mention of the Global Interface Table?

    [Simple. I use marshaling a lot. I don't use the Global Interface Table, so I don't feel qualified to write about it. -Raymond]
  10. John Doe says:

    @Raymond, I understand that, but the documentation has had 15 years to catch up with the implementation of the neutral apartment.  Not 15 days, 15 weeks or even 15 months.

    On the same vein, ASTA already had 3 years for documentation to catch up.  We mostly only see mentions like the neutral apartment, and the most complete documentation is titled in such a way that it seems to be solely about DirectX.

    Someone who hasn't known Windows almost like since ever will have both a hard time understanding apartments correctly and a hard time catching up with what the ASTA is.

    I guess these are actual instances where the old thing is still new.

  11. John Doe says:

    @Raymond, I was expecting you'd use the GIT a lot, since it recognizes objects marked with IAgileObject.

    For what cases do you use marshaling the most, if I may ask?

    [Getting an object from one thread to another as a one-time operation. The GIT is overkill for that sort of thing. -Raymond]
  12. Joshua says:

    [(Free-threaded objects should more properly be called "free-marshalled objects" since they marshal for free.) -Raymond]

    I really hope this doesn't mean a Free-threaded object can't be marshaled across processes.

    [Okay, so they really should be called "free-inproc-marshalled objects" since they marshal within a process for free (the C++ model). They still need help from COM for out-of-process marshaling. -Raymond]
  13. SI says:

    So marking utility COM objects which don't have any internal state as free threaded to avoid (in-process) marshaling is perfectly fine. So is having free threaded more complex COM objects with internal state, which are thread safe, and are used by STA / MTA objects. They just need to marshal any and all remote interfaces before using them.

    Since COM events are just COM calls to remote interfaces, I've been assuming that any marshaling issues for free threaded -> specific source event object also needs to be dealt with to send events, is this the case?

  14. SG says:

    Raymond, does your answer to "What happens if the threads associated with the originating apartment exit while the second apartment still has an outstanding reference to the object via a proxy?" apply in the case where the object is out-of-proc? So say ClientProcessThreadA creates ServerProcessObject, marshals it to ClientProcessThreadB, and then ClientProcessThreadA dies. Will ClientProcessThreadB still be able to use the unmarshaled interface, or will it get RPC_E_SERVER_DIED_DNE? I would expect the former, not the latter. Thank you.

    [When ClientProcessThreadA creates ServerProcessObject, the originating apartment is in the server. ClientProcessThreadA merely has a proxy to the server object. -Raymond]
  15. Roman says:

    There's something I really don't get. So unmarshalling an apartment-threaded object from a different thread creates a proxy that forwards all calls to the original thread.

    But how does it do that? That original thread could be doing arbitrary things. How does the proxy coerce it into doing the method call?

  16. Matthew w. says:

    @Roman,

    That's why STAs are required to pump messages.

  17. John Doe says:

    @SI, yes.

    "They just need to marshal any and all remote interfaces before using them."

    That's a really big "just".  Every method of an FTMed object must marshal interface pointers it stores immediately to make sure the marshaling is done in the correct apartment, or that the proxy is running in the correct apartment to call the object's marshaler, and it must unmarshal stored interface pointers it uses or returns.

    Manual marshaling due to aggregating the FTM is "just" that.  On the other hand, you only need one mistake to generate a miserable debugging session to someone in the future, perhaps yourself.  Of Course™, you should be able to identify the HRESULT in a blink.

  18. John Doe says:

    @Roman, the proxy to an STA object usually posts messages (as if through PostMessage) to the COM window in the object's STA thread.  If the method is marked as input_sync (good luck finding documentation), the message is instead sent (as if through SendMessage in an MTA thread or as if through SendMessageCallback or SendNotifyMessage in an STA).  Note that within the same STA, you don't even have a proxy, so calls are direct.

    Read carefully, there's a real difference between posted messaged and sent messages.

    The STA thread must pump messages with a minimum of GetMessage (or PeekMessage with PM_REMOVE) and DispatchMessage.

    Taking messages from the queue as soon and fast as possible makes desktop Windows happy about application responsiveness and processes messages sent from other threads, while DispatchMessage will pass posted messaged along to the destination window's WindowProc.

    In turn, the WindowProc of the COM window will unmarshal the object and the method's in arguments, invoke the method by vtable position, marshal the result and out arguments and finally notify the caller.

    If the message was posted, the STA is allowed to make inter-apartment calls, processing messages until the out-of-apartment call returns.  This is the cause of the dreaded STA reentrancy.  Since Windows 8, there's the ASTA (application STA), only available in store apps, which rejects top-level reentrant calls.  You can pretty much do the same in STA with an IMessageFilter that rejects CALLTYPE_TOPLEVEL_CALLPENDING in HandleInComingCall.

    If the message was sent, the STA is not allowed to make inter-apartment calls.  This is indirectly imposed by Win32, see (Message Deadlocks) msdn.microsoft.com/.../ms644927(v=vs.85).aspx  In essence, an input_async callee should not have a message loop risking a deadlock otherwise, so COM will fail remote calls (as if it checked IsSendMessage[Ex]).

    Exercise: what happens if an input_sync callee performs an in-process, but inter-apartment input_sync call?

  19. John Doe says:

    One correction: even in an STA, a standard marshaler (a proxy/stub from MIDL) will use SendMessage for input_sync methods.

    On the other hand, the type library marshaler (00020424-0000-0000-C000-000000000046) won't, because like many other MIDL attributes, there's no equivalent in type libraries.

Comments are closed.

Skip to main content