How to use COM to transfer data between two processes (for IPC - interprocess communication)

I have an average level of knowledge in COM and I would like to understand how COM helps in the transfer of data. Assuming there are two processes: Process-A and Process-B, and they both want to share some data with each other, of course, there are many RPC mechanisms, but I would like to use COM.

  • you cannot create COM-dll, because then it will become processing specific and cannot be used
  • we can create a Single ton COM EXE server and wrap the structure in COM CoClass and expose it as properties, and then ... I don’t know how?

Above my understanding, can any of you help me understand my understanding on this topic? basically I would like to share a data structure between two processes using COM

+6
source share
2 answers

Updated: when one object calls the method of another object (passing information in the parameters), we say that the first object sends a message to the second. This usually happens within the same address space of the process. COM allows an object in one process to call an object method in another process, thereby providing interprocess communication.

COM is a huge topic and cannot be explained in the overflow response format. I will try to demonstrate the simplest example of a local outside the process COM-server and COM-client (as short as possible) using the Visual Studio ATL wizards (as far as you mentioned ATL in the tags), which will generate most of the code, and this makes it possible to test the COM approach and Explore source sources. But for a better understanding, I recommend finding an inproc COM server implementation without ATL - only with C ++.

  1. Creating a provider structure:

    • Create a new ATL project named COMStructProvider (select the ATL project template in Visual C ++). In the wizard, select the "Executable" application type (not a DLL). Another default option. The wizard will generate project files.
    • Choose Project β†’ Add Class β†’ ATL Simple Object β†’ Add. In the short name field, enter an arbitrary name, for example MyStruct. Click Finish. This will add a header and implementation file for Coclass MyStruct. Also MyStruct.rgs will be added to help register your coclass in the registry. Now you have a minimal COM server and you can create a solution, but for this you need to run VS as an administrator (because he will register your server in the registry), otherwise registration will fail.
    • Add two data members to the CMyStruct class (by default, the class with the addition of VS and C): private: std :: string m_name; int m_age;
    • In the previous steps, the wizard created the IMyStruct interface (you can see it in the idl file). Now we want to add methods to this interface: getters and setters to our two private data elements. Select the Class View tab, select the IMyStruct interface (derived from IDispatch), select "Add Method" from the context menu. For example, the getAge method name with the LONG * parameter, the attributes of the "out" parameter and the parameter name: age (click Add to add the parameter). This adds a new method to the idl file and to the header and file of the impl. Repeat adding methods for setAge (in a parameter of type LONG), getName (out, BSTR *), setName (in, BSTR). The name of the parameters does not matter.

You will have something similar in the idl file - I am specifying this as a checkpoint so that all steps are completed correctly:

import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(AA2DA48C-CD1E-4479-83D4-4E61A5F188CB), dual, nonextensible, pointer_default(unique) ] interface IMyStruct : IDispatch{ [id(1)] HRESULT getAge([out] LONG* age); [id(2)] HRESULT setAge([in] LONG age); [id(3)] HRESULT getName([out] BSTR* name); [id(4)] HRESULT setName([in] BSTR name); }; [ uuid(E7A47886-D580-4853-80AE-F10FC69E8D73), version(1.0), ] library COMStructProviderLib { importlib("stdole2.tlb"); [ uuid(CC51EFFE-C8F4-40FA-AEA3-EB6D1D89926E) ] coclass MyStruct { [default] interface IMyStruct; }; }; 
  • Add implementation:

 STDMETHODIMP CMyStruct::getAge(LONG* age) { *age = m_age; return S_OK; } STDMETHODIMP CMyStruct::setAge(LONG age) { m_age = age; return S_OK; } STDMETHODIMP CMyStruct::getName(BSTR* name) { *name = SysAllocString(m_name.c_str()); return S_OK; } STDMETHODIMP CMyStruct::setName(BSTR name) { m_name.assign(name); return S_OK; } 
  1. Creating a customer. Add the new Win32 MyStructClient console application project to the solution (executable file). Add the following code:

 #include <iostream> // check for correct path to tlb library. Will create tlh, tli files that provide smart pointers, etc. #import "..\\COMStructProvider\\Debug\\COMStructProvider.tlb" no_namespace named_guid using namespace std; int main() { // initialize COM runtime CoInitialize(NULL); { // smart pointer simplifies work, will invoke CoCreateInstance to activate COM server IMyStructPtr spMyStruct(__uuidof(MyStruct)); BSTR name = SysAllocString(L"John"); spMyStruct->setName(name); SysFreeString(name); BSTR retreivedName; spMyStruct->getName(&retreivedName); wcout << "name " << retreivedName << endl; SysFreeString(retreivedName); spMyStruct->setAge(5); long age = 0; spMyStruct->getAge(&age); cout << "age " << age << endl; } CoUninitialize(); return 0; } 

Thus, two processes simultaneously work for you: a server that provides access to the structure, and a client that has access to the same structure (you can run several client processes in parallel. All clients access the same server process - you can considered as a singleton, but it is possible a product of a separate process with each activation). The client can modify and get the values ​​of this structure (as required). Under the hood, the client has access to the Coclass proxy in its own address space, and the COM runtime supports all interprocess communication. These proxies / stubs (in the form of C / C ++ sources) are generated by the MIDL compiler from the idl interface file mentioned above. Since you are focused on transferring data between two processes, you should be aware that there are three types of COM marshaling: user, standard, and universal. This example is quite universal, because I use only VARIANT- compatible types as method parameters. To transfer arbitrary types, you should use standard marshaling (using proxies / stubs generated in a separate project in the first step when creating a COM server. The project name is the name of the project with the server with the suffix PS). The disadvantage of standard marshaling is that you must deploy these PS libraries with your COM server.

0
source

COM exe out-of-process servers in particular are hard to write, but Microsoft has created a COM + Component service to facilitate this.

It contains many services, but here we are interested in an application service that allows you to host in-process servers (DLLs) on a surrogate node outside the process.

It's pretty simple, just write a standard ATL DLL (or use any other language / frame that you like). I recommend using automation types for the interface, so you do not need special proxies, for example, with the IDL interface defined as follows:

 interface ISharedMap : IDispatch{ [id(1)] HRESULT PutData([in] BSTR key, [in] VARIANT value); [id(2)] HRESULT GetData([in] BSTR key, [out, retval] VARIANT *pValue); }; 

Then create a new COM + application as described here: Create COM + applications and declare it as a server application. This is what you should see when this is done:

enter image description here

Now your DLL will automatically be placed in a certain process (the famous dllhost.exe ), which will be launched as soon as the clients try to connect. By default, the same process will be used for different COM clients outside the process. It will shut down after a while, but you can configure the COM + application in various ways, for example, using the "Leave to work when there is no free time" command:

enter image description here

Now you can use the memory cache for cross-processes for all COM clients that you like, for example, from simple javascript.js code:

 var map = new ActiveXObject("SharedMap"); map.PutData("mykey", "mydata") var data = map.GetData("mykey") 

Note: the cache implementation is left to the reader, but it can reuse another of the COM + services: COM + Shared Property Manager

0
source

All Articles