Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In-Proc COM object sharing across another Process

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.

like image 923
ActiveX Avatar asked Mar 24 '11 21:03

ActiveX


2 Answers

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.

like image 131
BrendanMcK Avatar answered Oct 19 '22 18:10

BrendanMcK


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.

like image 44
David Gausmann Avatar answered Oct 19 '22 17:10

David Gausmann