7

Before I ask this question I would like to make it clear that I know there are libraries and techniques for Inter process communcation. This though, is a learning question about COM. I also do know about out-of-proc servers but that's not what I am looking for.

The question:

What I want to know, because I don't know this, is it possible, and if yes how, to share an in-proc COM object (object defined in a DLL) living in one process (has been instantiated in the process) across another process? Ie, how do I obtain a pointer to the in-proc object from proces A in process B?

Thanks in advance.

ActiveX
  • 913
  • 13
  • 31

3 Answers3

10

Yes, it's possible. The underlying principle is the same regardless of whether you are sharing a single object instance between apartments in a single process, or between separate processes.

There's two approaches here: perhaps the simplest is to use the Running Object Table: this is essentially a workstation-wide table of named COM objects. You have one process add an object to the table with a well-known name, and have the other process look up that object.

The other approach is to use marshaling. Marshaling is the process of using a COM API to get a series of bytes that describe the location of an object. You can then copy that series of bytes to another process using any means you want to (shared memory, file, pipe, etc), and then use another COM API in the receiving process to unmarshal the object; COM then creates a suitable remoting proxy in that process that communicates back to the original one. Check out the APIs CoMarshalInterface and CoUnmarshalInterface for more details.

Note that both of these require that you have suitable remoting support in place for the object; the interfaces you are using need to be described in IDL and compiled and registered appropriately.

--

I don't have code handy for either of these cases unfortunately.

For the CoMarshalInterface approach, the process is something like:

  • Use CreateStreamOnHGlobal (with NULL hglobal) to create an IStream that's backed by a HGLOBAL that COM allocates as needed
  • Use CoMarshalInterface to marshal the interface pointer to the stream (which in turn writes it to the memory backed by the HGLOBAL)
  • Use GetHGlobalFromStream to get the HGLOBAL from the stream
  • Use GlobalLock/GlobalSize to lock the HGLOBAL and access the marhaled data (GlobalUnlock when done)
  • Use whatever means you want to to copy the bytes to the target process.

On the far side, use:

  • GlobalAlloc/GlobalLock/GlobalUnlock to create a new HGLOBAL and populate it with the marshaled data
  • CreateStreamOnHGlobal with your new HGLOBAL
  • Pass this stream to CoUnmarshalInterface

Normal COM and Windows refcounting/resource rules apply across all of this; AddRef/Release as appropriate; use GlobalFree to free any HGLOBALs that you allocate, etc.

Ian Boyd
  • 220,884
  • 228
  • 805
  • 1,125
BrendanMcK
  • 13,414
  • 41
  • 48
  • Answer I was looking for. Do you have a complete code example of GetRunningObjectTable? or CoMarshallInterface? – ActiveX Mar 28 '11 at 02:46
  • Actually, lets say if I marshal my interface pointer using CoMarshallInterface into a stream ... what then? How do I pass the stream to another process? Via Inter-process communication method such as named pipe/socket/memory mapped file? If that is the case then the marshaling is really not saving me any work. – ActiveX Mar 28 '11 at 03:39
  • I don't have any code handy for these scenarios; I used a variation on the CoMarshal approach on a project some years ago; have updated the answer above with more details on the steps involved. – BrendanMcK Mar 28 '11 at 04:59
  • @ActiveX: It's saving you plenty of work, because you only have to do it once, in one direction, to get the object to the other process. DCOM then does all the work of passing parameters back and forth for each and every method call and property access. – Ben Voigt Mar 28 '11 at 05:22
  • 1
    Yeah, you right. It does save me some work. I was expecting COM to have more native support then that, like a COM Services method CoCreateSharedInstance() that does all the work for me :) Any how, thanks for the answers, very helpful. I also found a gem online (http://msdn.microsoft.com/en-us/magazine/cc302324.aspx) I will explore alternatives. A customer class factory combined with your ideas looks very promising – ActiveX Mar 28 '11 at 16:29
  • There is a complete example of the CoMarshalInterface approach in C#, in the .NET source code: [SerializeToBlob()](http://referencesource.microsoft.com/#System.Management/InteropClasses/WMIInterop.cs,e8edaee84f76492f,references) shows you how to use CoMarshalInterface, and [DeserializeFromBlob()](http://referencesource.microsoft.com/#System.Management/InteropClasses/WMIInterop.cs,f18000223143ff9c) shows you how to use CoUnmarshalInterface – transistor1 Jan 22 '15 at 03:00
1

There is also another possible solution using the window message WM_GETOBJECT:

In the application, which has the object, you simply create a window with your own class. In the handler, you need to handle the window message like this (I use IDispatch as example interface):

LRESULT WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
    case WM_GETOBJECT:
        {
            if(lParam == OBJID_NATIVEOM)
            {
                return LresultFromObject(IID_IDispatch, wParam, g_MyGlobalIDispatchPointer);
            }
            else
            {
                // Not handled
                break;
            }
        }
        return 0;
    }

    // Default
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

The other application must find that specific window an get the object via AccessibleObjectFromWindow

In example:

HWND hWndCommunicator = FindWindow(_T("MyWindowClassOfTheOtherApplication"), _T("MyWindowTitleOfTheOtherApplication"));
if(hWndCommunicator)
{
    IDispatch* poObject = nullptr;
    HRESULT hr = AccessibleObjectFromWindow(hWndCommunicator, static_cast<DWORD>(OBJID_NATIVEOM), IID_IDispatch, &poWindow);
    if(SUCCEEDED(hr))
    {
        // Do something with the object of the other process
        // i. e. poObject->Invoke
    }
}

Marshalling is automatically done using this solution.

David Gausmann
  • 1,274
  • 11
  • 17
0

That's what CoRegisterClassObject and CoGetClassObject is for.

Sheng Jiang 蒋晟
  • 14,859
  • 2
  • 26
  • 44