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 obje开发者_开发技巧ct 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. :-)
You should take a look at Monikers, that are used to identify a COM object instance even across different machines.
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.
I had to do something similar recently and found that using shared memory worked very well for me. The BossCom could create and own the shared memory and the workers could register by making an entry in the shared memory. Here is an MSDN link to what I am talking about. Remember to use a mutex to synchronise access to the memory...
You're a bit limited in being able to create marshalable interfaces in C#. No easy way to setup the proxy. No such problem in ATL, declare a callback interface in the IDL. And pass an instance pointer with the RegisterWorker() call. The server should store it until it gets the unregister call. Use that callback interface to generate notifications.
You should also look into the ROT (Running Object Table), it may be a different way to solve this problem.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms695276(v=vs.85).aspx
http://www.codeproject.com/KB/COM/ROTStuff.aspx
精彩评论