Search code examples
c#c++cominterprocess

How can I marshal an out-of-process COM reference across process boundaries?


I have an out-of-process COM server that needs to keep an eye on things. This server runs as a service and is, internally, a singleton. For simplicity sake, I will call him BossCom.

I have another out-of-process COM server that is a worker. It is, for system stability, a single-use server (meaning that if you create 2 WorkerCom's, there are 2 WorkerCom.exe's running). For simplicity sake, I will call him WorkerCom.

WorkerCom can be started by anything, even by itself if someone runs him via the command line with the right command line arguments.

The overall goal is for BossCom to know what WorkerComs are around, know what they are doing, and be able to give them orders (pause, stop, ramp up, etc).

My initial thought at this would be that whenever WorkerCom starts, he would CoCreateInstance a BossCom and call BossCom->RegisterWorker(IUnknown me). Then when the WorkerCom is about to shutdown, he would call BossCom->UnregisterWorker(IUnknown me). BossCom could QueryInterface the IUnknown for IWorkerCom and be able to issue commands.

That would work great if all these com objects were in the same process, but they're not. I thought about using the GlobalInterfaceTable, but it is only global in the sense of a single process.

I have spent a few days researching this and am at a loss. Maybe I'm tunnel-visioned.

How can I marshal a reference to a com object from the Worker to the Boss?

Oh, and, for what it's worth, BossCom is written in C# and WorkerCom is written in ATL C++ but I'll take solutions written in VB, Scala, Lisp, or anything. I figure I can translate the core idea. :-)


Solution

  • As per the comments, this does indeed work out of the box. There is one additional detail though that makes it work.

    Originally when I was looking at C# interfaces that dealt with copying interfaces, the argument type was IntPtr. Well, an IntPtr is just a long so it transfers the value as is and, as such, doesn't work.

    The key is to set the MarshalAs attribute on the argument. So my RegisterWorker method looks like this:

        public void RegisterWorker(
            [In, MarshalAs(UnmanagedType.IUnknown)] object ptr
            )
        {
            IWorkerCom worker = (IWorkerCom) ptr;
            Workers.Add(worker);
        }
    

    Utterly amazing.