What is COM marshaling and how do I use it?


COM has this concept called "marshaling", with one L. Basically, marshaling is the mechanism by which an object that is accessible to one apartment can be made accessible to another apartment.

Incomplete backgrounder on apartments: In COM, an apartment is a collection of threads that are treated as equivalent from a COM standpoint. The details of apartments aren't important for today's discussion, so let's assume that all the apartments in question are single-threaded apartments (STA). Single-threaded apartments, as you might guess from their name, are apartments that consist of a single thread. In this world, the concepts of thread and apartment line up one-to-one, which makes discussion easier.

Incomplete backgrounder on threading models: Each COM object declares how it deals with threads. The most common cases are apartment model objects,¹ which can be used only on the thread that they were created on, and free-threaded objects, which can be used from any thread. Free-threaded objects are easier to use, but apartment model objects are much, much easier to write, since you don't have any of that troublesome multi-threading to deal with.

Okay, back to marshaling.

Since the rules for apartment model objects is that they can be accessed only from the thread on which they were created, you need to do some extra work if you want to access them from another thread: You need to hire a lackey. COM calls this lackey a "proxy". When you invoke a method on the proxy object, the call is routed back to the originating apartment, the method executes on its original apartment, and then the results are routed back to original caller. (And if any of the parameters to the method are themselves objects, then COM needs to create proxies for those objects, too!) Marshaling is a mechanism for creating proxies.²

What if I need to marshal an object to another thread (same process or different process), and I already have access to an object in the destination thread?

The easy way to do this is to take advantage of the parenthetical remark up above: If you already have an interface pointer to an object in another apartment, you can define a marshalable interface that accepts an interface pointer. For example, if you need to pass a widget to another object, you probably already have a method on that other object called IMumble::Colorize­Widget that takes a widget and some other parameters. Just call the method from your originating apartment. The implementation of IMumble::Colorize­Widget will receive a pointer to the proxy, and it can use the proxy to access the original widget. It can event retain that pointer (with an appropriate Add­Ref, of course) to communicate with the original widget even after the method returns.

Of course, this creates a chicken-and-egg problem: In order to get my object to another apartment, I need access to an object on that apartment. In other words, once you have one object operating across apartments, you can use it to get more objects to operate across apartments. But how do you marshal the first object?

What if I need to marshal an object from one thread to another thread in the same process, and I don't have a friendly object on the other thread yet?

You can use the Ro­Get­Agile­Reference function. You give it an interface accessible to the current apartment, and the Ro­Get­Agile­Reference function returns you an IAgile­Reference interface pointer that figures out how to create proxies so you don't have to. What makes the agile reference special is that it is free-threaded: You can share the pointer freely among apartments.³ To gain access to the original interface pointer, call IAgile­Reference::Resolve. Note, of course, that the pointer that comes out of IAgile­Reference::Resolve is valid only in the apartment in which you called IAgile­Reference::Resolve.

In the case of passing an object to a newly-created thread, you would do something like this:

  • Calling thread calls Ro­Get­Agile­Reference and gets an agile reference.
  • Calling thread creates the background thread and passes it the agile reference pointer, transferring to the background thread the obligation to release the agile reference. (If the calling thread could not create the background thread, then it needs to release the agile reference itself since it was unable to transfer the obligation.)
  • The background thread calls Co­Initialize[Ex].
  • The background thread takes the agile reference and calls IAgile­Reference::Resolve to recover the original object.
  • The background thread releases the agile reference, since it doesn't need it any more.

What if I need to marshal an object from one process to another?

Again, the easy way is to pass the object as a parameter to a method on an object you already have in the destination process.

But what if those mechanisms aren't available to me?

The Ro­Get­Agile­Reference function was introduced in Windows 8.1, so it may not be available to you. And maybe you don't have an object in the destination process that you can use as a foothold. We'll look at the mechanism that operates under the covers (and answer some more questions) over the next couple of days.

¹ This unfortunate reuse of the word apartment has been the source of significant confusion. A better name would have been something like fixed-threaded objects.

² Of course, this entire discussion assumes that the interface in question is marshalable. Otherwise, you get the mysterious E_NO­INTERFACE.

³ Note that I didn't say that you can share the pointer freely among threads. The threads you share the pointer with must still belong to apartments. For single-threaded apartments, that means calling Co­Initialize or Co­Initialize­Ex with the COINIT_APARTMENT­THREADED flag. For multi-threaded apartments, this means calling Co­Initialize­Ex with the COINIT_MULTI­THREADED flag somewhere in the process.⁴

⁴ Preferably, you called Co­Initialize­Ex with the COINIT_MULTI­THREADED flag on the thread itself. It is technically legal to call it from some other thread, but then you're using the implicit MTA, and that has its own problems.

Comments (25)
  1. Rich says:

    Honest to God who the heck thought this up?

  2. Ivo says:

    Is there an easy way to debug such code that marshals a call to another thread? Assuming it goes to the same process, of course. I tried stepping inside such call once and it led into the guts of COM.

  3. dalek says:

    @Ivo

    This will probably depend on your definition of easy :)

  4. Ken Hagan says:

    @Rich: The backgrounders are very incomplete, understandably, but if you think of processes instead of threads then the apparent complexity is no more than you might expect. Until you have a helper of some description in the target process, you can't call into it. Marshaling is just a system-supplied helper for this task.

    Once the helper is there, you have run-time support for aspect-oriented programming (such as transparent remote procedure calls and/or ACID transactions), so it's all worthwhile.

  5. Josh says:

    I think this is supposed to be "even"

    It can event retain that pointer

  6. DWalker says:

    I have heard of STA and MTA.  Are there corresponding "things" that are not apartments (houses, condos, etc.)?  IOW, what is "apartment" a member of?

    [An "apartment" is a group of threads. I guess you could say that they are therefore members of a process. -Raymond]
  7. I have a question: What is the use of COM anyway? I keep hearing about it and it seems like a super-complex way of doing what I do anyway, with security risk coming as a free bonus. But what's the upside to all this complexity?

    I tried Wikipedia but it just confused me.

  8. Gabe says:

    Fleet Command: COM is used when you have a situation where you need objects that need to be able to communicate.

    Default C++ objects can't be used because the in-memory layout is not specified, there are no clear rules about memory management, there's no way to access an object that isn't in your address space (in another process or on another computer), there's no way to call methods that weren't known to you at compile time, and so on.

    You can solve some of those problems yourself, but if you decided to solve all of them, you'd end up with something similar to COM.

  9. @Gabe: Thanks a lot. :)

    I keep forgetting that a lot of things that I don't use and don't understand have something to do with C++ and its nature. I could not understand the buffer overrun attack for years (because everyone kept telling it in a language-agnostic way) until someone told me how things in C++ were. (I remember feeling miserable.)

    But I already have "objects that communicate", using SOAP. Can't C++ use SOAP? Or maybe I don't understand "communicate" correctly here. Er... Interfaces? Or named pipes? Or is it delegates?

    [You have two modules in your process, written at different times, with different compilers, using different runtime libraries. How would you use SOAP to get module X to create an object from module Y and call methods on it? -Raymond]
  10. Brian says:

    @Fleet Command:

    Well, the SOAP spec was partially authored by Don Box, who, at the time didn't work for Microsoft (he does not, I think), but was probably the most well known non-MSFT COM expert of the 1990s.  So, SOAP was, in some ways, the result of lessons learned from COM.  A lot of what is in the .NET Framework is the result of lessons learned from COM (.NET started life as the next version of COM+).

    COM (and DCOM - distributed COM) relies on a binary (binary-rather than text, not binary-as in two) communication mechanism to allow disparate bits of code to talk to each other.  Under the covers, it looks a lot like C++ vtables, but that's just an implementation detail.  Any language that can lay things out in the appropriate way can use COM to have the code it produces communicate to other COM implementations.  It may be hard to believe, but one of the great selling points of COM (apartments and threading-models and all) was its simplicity (particularly in comparison to CORBA - the other big "object RPC" standard in the 1990s.

    The other thing to remember about COM is that a chunk of its oddities are the result of it providing a path for VB programmers to be able to do some pretty amazing things.  In particular, VB (traditional VB, not VB.NET) was basically a single-threaded language with an initially closed type system.  COM opened up the type system (the underlying type system in late-90s VB was COM) and provided a way for VB programmers to write components that could plug into massively multi-threaded environments (like browsers, web apps, etc.).  The oddball apartment and marshaling mechanisms provide the path for that multi-threading capability.

  11. itaish says:

    Since the suggestion box doesn’t seem to work (last post from 2010) I’m posting here a quick, off-topic, follow up on your 2003 post “Why isn't my time zone highlighted on the world map?”.

    I thought it would be nice to note that 20 years later, with Windows 10, Microsoft returned the time zone map feature (well, sort of) in the Alarms & Clock UWP app ;-)

    [There is no time zone map (no highlight). It just has some popular cities. -Raymond]
  12. @Brian: Thanks a lot. That's probably one of the most valuable comments I've read. It all makes sense too. After all, the 1990s thinking was what made PCs like lumbering giants that were fast in action but slow in pace.

    Oh, and by the way, Don Box joined Microsoft in 2002 and is still there. He was transferred to entertainment unit in 2011. (And, yes, I researched these just now.)

    @Raymond: I understand what you say is a rhetorical question that means "You can't use SOAP to get module X to create an object from module Y and call methods on it." Well, no, but it is far simpler than that. I'd link to Module Y via "use" keyword. I'd call the class constructor by name and receive an object pointer to its instance. And then I call the object method. Now, I assume all of this is impossible in C++, right?

    [It requires that modules X and Y both be written in the same language, compiled from the same header files, with the same version of the same compiler, linked to the same runtime library. If you can enforce that, then great. But it's not very interoperable. For example, if you change a class, you have to recompile all modules that use it. If you decide to upgrade to a newer version of the compiler, you have to recompile the entire app. Anybody who writes a plug-in for your app must use exactly the same compiler that your app is written in. -Raymond]
  13. @Raymond: Oh, yes, I just remembered this particular Microsoft C++ problem you are talking about. Wow. Using ffmpeg or VLC with Visual C++ must be torture then. So, in essence, you are saying that COM implemented the plug-in infrastructure that C++ lacked up to that point. Go, COM!

    [Plug-ins, or just getting two apps written independently to interoperate. Also, it implemented a model for thread safety. It may not be a model you would have chosen, but it was better than no model at all. (ffmpeg and VLC solve the interop problem by using flat C, and not supporting cross-process use. Not sure how they deal with threading.) -Raymond]
  14. Joshua says:

    [(ffmpeg and VLC .... Not sure how they deal with threading.)]

    Sanely, like most .NET objects. The objects don't care what thread so long as there's no more than one thread at once.

    [That makes it easy for the object, but hard for the clients. (If one client hands the object to another component, it really needs to hand two things to the other component. One is the shared object. And the other is a synchronization policy so that the two components won't try to use the object at the same time.) -Raymond]
  15. Euro Micelli says:

    @Rich (and to some extent @FleetCommand):

    These things exist to solve real problems. My experience with COM - time and time again - has been that I look at some aspect of it, I tell myself "bonkers, that's too complex", I ask myself how *I* would approach the same problem the simplest possible way... and assuming I manage to be smart enough to think it through, I invariably end up reinventing the same solution. I have the utmost respect for the people that created and maintain COM: they know what they doing.

    (I still try to follow that mental process because it teaches me "why" something was designed the way it is)

    We've been talking about the COM infrastructure and API, but the same thing goes also for all the rules of COM usage and implementation. They are there to protect us. Just because someone doesn't understand why a rule exist doesn't mean they get to ignore it (and yet, many try).

  16. nikos says:

    you forgot to mention CoMarshalInterThreadInterfaceInStream, that works on windows prior to 8.1

    [I didn't forget. I said that the topic will continue next time. -Raymond]
  17. Rich says:

    @Nikos: Silly of him! How could anybody forget CoMarshalInterThreadInterfaceInStream?

  18. McBucket says:

    @nikos: your wish is granted: blogs.msdn.com/.../10649190.aspx

  19. DWalker says:

    Thanks, Raymond.  I never knew if there was an "apartment" threading model and some other, non-apartment threading model.  Now I know.

  20. John Doe says:

    @Rich, COM has a huge historic baggage, it's not like your question deserves a one-line answer.  Definitely, it wasn't all thought out to be what it became.

    @Raymond, interesting that the MSDN article for RoGetAgileReference is under "Archive" in the topic hierarchy, what gives?  Quoting:

    "Archived articles

    This section of the Library contains archived articles. Some articles cover deprecated or superseded technologies, and some cover niche subject areas that you may still find interesting."

  21. John Doe says:

    Apartments used to be groups of threads when you only had STA and MTA, but then came the NA.

    Here are my two cents (pick your currency):

    A COM apartment is a top-level context for COM objects.  There is a sub-level of object contexts in COM+, but I won't cover COM+ here.

    A COM object is bound to an apartment, its lifetime depends on the apartment's lifetime.  The kind of apartment it supports depends on its ThreadingModel configuration (either in the registry or in an assembly manifest) and on the caller's current apartment.  See this MSDN article (Threading Model Attribute): msdn.microsoft.com/.../ms681753(v=vs.85).aspx

    An STA (single-threaded apartment) takes one and only one thread that pumps window messages, its lifetime depends on that thread being in the STA.  There can be any amount of STAs.

    The Main STA is the first STA ever created.  It's the apartment for objects that don't declare a ThreadingModel.

    The Host STA is the STA created by COM's infrastructure.  It's created when a no-ThreadingModel or an Apartment object is created in a non-STA.

    It's bad practice to exit the thread or post a quit message while running under the Host STA.

    The Main STA and the Host STA may be the same.

    An MTA (multithreaded apartment) takes one or more threads, its lifetime depends on the existence of at least one thread in the MTA.  Since Windows 8, its lifetime can be extended with CoIncrementMTAUsage/CoDecrementMTAUsage.  There can be at most one MTA.

    A host MTA thread is an MTA thread created by COM's infrastructure.  It may be created from a thread pool when no MTA thread is available to create an MTA object or to handle its method calls.  Since Windows 7, COM uses the default thread pool, but you can change that with IGlobalOptions.

    When the MTA is alive, every thread that is not in an apartment is implicitly in the MTA.  However, relying on this behavior means you're subject to sudden unexpected apartment changes allowed by the next call to CoInitialize[Ex].

    An NA (neutral apartment) is an apartment that doesn't require thread exclusivity, its lifetime is not documented, but it must be at least as long as the last contained object's final release, and it may exist indefinitely once created.  There can be at most one NA.

  22. John Doe says:

    (second cent, comments must be under 3072 characters):

    Threads may enter an STA or an MTA with CoInitialize[Ex].  The thread can either reinforce this with CoInitialize[Ex] or leave the apartment with as many calls to CoUninitialize as previous successful calls to CoInitialize[Ex].  It can then enter an apartment again.

    A thread must be in an apartment to be considered a COM thread.  Failing to enter an apartment commonly exposes errors in calls to CoCreateInstance[Ex] or CoGetClassObject.

    A thread can only run under the NA apartment through activation (in the class factory) or through the created object's methods.

    Avoid having to wait for multiple handles.  When implementing a Both object, a Neutral object or an object with the free-threaded marshaler, consider using CoWaitForMultipleHandles instead of developing your own function that dispatches to WaitForMultipleObjectsEx or MsgWaitForMultipleObjectsEx.

    A thread that pumps window messages should only enter an STA.  In an MTA, idle threads in the pool, inter-apartment calls and CoWaitForMultipleHandles block the thread, so window messages are not processed and after a while Windows may consider the application as not responding.

    The current apartment may be none, STA, main STA, MTA, implicit MTA, NA over STA, NA over main STA, NA over MTA and NA over implicit MTA.

    Calls across apartments require marshaling.  A common workaround is to aggregate the the FTM (free-threaded marshaler) and declare the ThreadingModel of an object as Both.  An NA object has a lightweight proxy or stub that doesn't perform in-process marshaling, although it performs apartment enter and exit logic.  The advantage of NA's proxy/stubs is that it simply works across apartments without having to deal with every object reference through manual marshaling or the GIT (global interface table).

    An object that aggregates the FTM still belongs to the apartment where it was created, so if that apartment ceases to exist and you still hold a reference to the object, consequences of further using the object are undefined.

  23. Brian says:

    @John Doe:

    Ah, that's the COM I remember - so simple and easy to remember :)

    However, one usually works within a framework that is already established and for which there are strict rules.  As long as you aren't mucking with that framework or messing with the rules, programming in COM (particularly in a framework with good smart pointers) isn't that difficult.

    Several times in the past 10 years, I've had people explain their plug-in mechanism to me.  It would usually include a universal naming mechanism, a factory-based object creation system (using that name), an interface-based "interface" to the objects, and a formal way of defining object lifetime.  Sometimes it included a way to marshal calls across threads (a la Windows Forms' Control.Invoke).  At the end of the explanation, I'd often say "congrats, you just re-invented COM yet again".

  24. DWalker says:

    @John Doe:  Thanks for that comprehensive explanation.  

    @Brian:  I have seen that kind of thing happen with other technologies.  You think "wow, that's complex, I can do a better job", and (as you said earlier) you end up doing the same thing.

  25. KISS says:

    This is what you get when you never deprecate a single feature from a framework optimized for 16-bit real-mode windows.

    @Euro Micelli

    No, adding interop with BASIC isn't "they know what they doing". That's bloat.

Comments are closed.

Skip to main content