使用COM在两个进程之间

使用COM在两个进程之间

本文介绍了如何使用COM在两个进程之间传输数据(用于IPC-进程间通信)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对COM有一定的了解,并且想了解COM如何帮助数据传输.假设有两个进程,Process-A和Process-B,并且它们两个都希望彼此共享一些数据,那么当然有很多RPC机制,但是我想使用COM.

  1. 您无法创建COM dll,因为它会变得特定于进程并且无法使用
  2. 我们可以创建一个单吨COM EXE服务器并将结构包装在COM CoClass中并将其成员公开为属性,然后...不知道如何做吗?

以上是我的理解,你们中的任何人都可以帮助我清除我对该主题的理解吗?基本上我想使用COM在两个进程之间共享数据结构

解决方案

已更新:当另一个对象的一个​​对象调用方法(在参数中传递信息)时,我们说第一个对象向第二个对象发送消息.通常,这是在一个进程地址空间内发生的. COM允许一个进程中的对象调用另一进程中的对象方法,从而实现进程间通信.

COM是一个巨大的话题,不可能以溢出答案的形式进行解释.我将尝试做的是借助Visual Studio ATL向导(就您在标记中提到的ATL)来展示本地进程外COM服务器和COM客户端(尽可能短)的最简单示例,该示例将生成大多数代码,从而可以测试COM方法并研究样板资源.但是为了更好地理解,我建议找到不带ATL的Inproc COM服务器实现-仅使用C ++.

  1. 创建结构提供程序:

    • 使用名称COMStructProvider创建新的ATL项目(在Visual C ++中选择ATL项目模板).在向导中,选择可执行"应用程序类型(不是DLL).默认情况下为其他选项.向导将生成项目文件.
    • 选择项目->添加类-> ATL简单对象->添加.在短名称字段中,输入任意名称,例如MyStruct.点击完成".这将为MyStruct协同类添加头文件和实现文件.另外还将添加MyStruct.rgs,以帮助在注册表中注册您的共同类.现在,您拥有最少的COM服务器并可以构建解决方案,但是要做到这一点,您需要以管理员身份运行VS(因为它将在注册表中注册您的服务器),否则注册将失败.
    • 将两个数据成员添加到CMyStruct(VS prepend类,默认情况下为C)类:私人的:std :: string m_name;int m_age;
    • 在先前步骤中,向导创建了界面IMyStruct(您可以在idl文件中看到它).现在,我们想向该接口添加方法:两个私有数据成员的getter和setter.选择类视图"选项卡,选择IMyStruct界面(源自IDispatch),在上下文菜单中选择添加方法".例如,方法名称为getAge的参数为LONG *,参数属性为"out",参数名称为age :(单击添加"以添加参数).这将新方法添加到idl文件以及标头和impl文件中.重复添加setAge(在LONG类型的参数中),getName(out,BSTR *),setName(in,BSTR)的方法.参数名称无关紧要.

在idl文件中,您将拥有类似的内容-我将其作为检查点,以确保所有步骤均正确完成:

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;
    };
};

  • 添加实现:

 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. 创建客户端.将新的Win32 Console应用程序项目MyStructClient添加到解决方案中(可执行).添加以下代码:

 #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;
} 

因此,您有两个进程同时运行:提供对结构访问权限的服务器和具有相同结构访问权限的客户端(您可以并行运行更多客户端进程.所有客户端都访问同一服务器进程-可以视为单例) -但每次激活都可以生成单独的进程).客户可以更改并获取此结构的值(根据需要).客户端可以在其自己的地址空间中访问coclass的代理,并且COM运行时支持所有进程间通信.这些代理/存根(以C/C ++源代码的形式)是由MIDL编译器从上述接口idl文件生成的.就着重于在两个进程之间传输数据而言,您应该知道COM封送处理分为三种类型:自定义,标准和通用.在此示例中,通用就足够了,因为我仅使用 VARIANT 兼容类型作为方法参数.要传递任意类型,您应该使用标准封送处理(在创建COM服务器时第一步在单独项目中生成的代理/存根dll的帮助下.项目名称是带有后缀PS的服务器的项目名称).标准封送处理的缺点-您应该在COM服务器上部署这些PS dll.

i have a average knowledge in COM and will like to understand how COM helps in data transfer. Assuming there are two processes, Process-A and Process-B and both of them wants share some data with each other, of course there are many RPC mechanisms but i would like to use COM.

  1. you cannot create a COM dll because then it would become specific to process and cannot be used
  2. can we create a Single ton COM EXE server and wrap the structure in COM CoClass and expose it members as properties and then ...no idea how to ?

Above is my understanding, can anyone of you help me clear my understanding on this topic? basically i would like to share a data structure between two process using COM

解决方案

Updated: when one object call method of another object (passing information in parameters) we say that first object sends message to second one. Usually this happen s within one process address space. COM allows object in one process to call method of object in another process - thus enabling interprocess communication.

COM is huge topic and it is not possible to explain it in format of overflow answer. What I will try to do is to demonstrate simplest example of local out of process COM server and COM client (as short as possible) with help of Visual Studio ATL wizards(as far as you mentioned ATL in tags) that will generate most of the code and this gives the possibility to test COM approach and investigate boilerplate sources. But for better understanding I recommend to find inproc COM server implementation without ATL - just with C++.

  1. Creating Structure provider:

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

In idl file you will have something like that - I provide this as checkpoint that all steps are done 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 client. Add new Win32 Console application project MyStructClient to solution (executable). Add 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;
}

So, you have two processes running simultaneously: server that provides access to structure and client that has access to the same structure (you can run more client processes in parallel. All clients access the same server process - can be considered as singleton - but it's possible to spawn separate process with each activation). Client can change and get values of this structure (as was required). Under the hood client has access to proxy of coclass in its own address space and COM runtime support all interprocess communication. Those proxy/stubs (in the form of C/C++ sources) are generated by MIDL compiler from interface idl file mentioned above. As far as you focused on transfering Data between two process you should know that there are three types of COM marshaling: custom, standard and universal. In this example universal is sufficient because I use only VARIANT compatible types as method parameters. To pass arbitrary types you should use standard marshaling (with help of proxy/stub dlls, generated in separate project on first step when creating COM server. name of project is the name of project with server with suffix PS). Disadvantage of standard marshaling - you should deploy those PS dlls with your COM server.

这篇关于如何使用COM在两个进程之间传输数据(用于IPC-进程间通信)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 05:30