如何使用COM在两个进程之间传输数据

时间:2015-06-28 06:06:50

标签: visual-c++ com atl

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

  1. 您无法创建COM dll,因为它会变得特定于处理而无法使用
  2. 我们可以创建一个单吨COM EXE服务器并将结构包装在COM CoClass中并将其作为属性公开,然后......不知道怎么做?
  3. 以上是我的理解,你们中的任何人都可以帮助我清楚我对这个主题的理解吗?基本上我想使用COM

    在两个进程之间共享数据结构

2 个答案:

答案 0 :(得分:0)

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

COM是一个很大的话题,不可能以溢出答案的格式来解释它。我将尝试做的是在Visual Studio ATL向导的帮助下(就你在标签中提到ATL而言)演示本地进程外COM服务器和COM客户端(尽可能短)的最简单示例,它将生成大部分的代码和这提供了测试COM方法和研究样板源的可能性。但为了更好地理解,我建议在没有ATL的情况下找到inproc COM服务器实现 - 只需使用C ++。

  1. 创建结构提供程序:

    • 使用名称COMStructProvider创建新的ATL项目(在Visual C ++中选择ATL项目模板)。在向导中选择“可执行”应用程序类型(不是DLL)。其他选项默认情况下。向导将生成项目文件。
    • 选择项目 - >添加课程 - > ATL简单对象 - >加。在简短名称字段中键入任意名称,例如MyStruct。单击“完成”。这将为MyStruct coclass添加头文件和实现文件。此外,还将添加MyStruct.rgs以帮助在注册表中注册您的coclass。现在你有最小的COM服务器并且可以构建解决方案但是要做到这一点你需要以管理员身份运行VS(因为它会在注册表中注册你的服务器),否则注册失败。
    • 向CMyStruct添加两个数据成员(默认情况下,VS前置类使用C)类: 私人的: std :: string m_name; int m_age;
    • 在前面的步骤中,向导创建了接口IMyStruct(您可以在idl文件中看到它)。现在我们要向这个接口添加方法:getters和setter到我们的两个私有数据成员。选择Class View选项卡,选择IMyStruct接口(从IDispatch派生),在上下文菜单中选择“add method”。例如,方法名称getAge,参数LONG *,参数属性“out”和参数名称:age(单击Add to add parameter)。这为idl文件以及header和impl文件添加了新方法。重复添加setAge的方法(在LONG类型的参数中),getName(out,BSTR *),setName(in,BSTR)。参数名称无关紧要。
  2. 在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控制台应用程序项目MyStructClient添加到解决方案(可执行文件)。添加以下代码:
    2. 
      
      #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;
      }
      &#13;
      &#13;
      &#13;

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

答案 1 :(得分:0)

COM exe进程外服务器特别难以编写,但Microsoft已创建COM+ Component service以简化此操作。

它包含许多服务,但是我们对Application服务感兴趣,它允许您在进程外代理主机中托管进程内服务器(DLL)。

这很简单,只需编写标准ATL DLL(或使用您喜欢的任何其他语言/框架)。我建议使用自动化类型作为接口,这样就不需要特殊的代理,例如使用如下定义的IDL接口:

interface ISharedMap : IDispatch{
    [id(1)]
    HRESULT PutData([in] BSTR key, [in] VARIANT value);

    [id(2)]
    HRESULT GetData([in] BSTR key, [out, retval] VARIANT *pValue);
};

然后创建一个新的COM +应用程序,如下所述:Creating COM+ Applications,并将其声明为服务器应用程序。这是您在完成此操作后应该看到的内容:

enter image description here

您的DLL现在将自动托管在特定进程(着名的dllhost.exe)中,该进程将在客户端尝试连接时立即启动。默认情况下,相同的进程将用于各种进程外COM客户端。它会在一段时间后关闭,但您可以通过各种方式配置COM +应用程序,例如使用'空闲时保持运行'标志设置':

enter image description here

现在,您将能够为所有您喜欢的COM客户端使用跨进程内存缓存,例如,通过简单的javascript .js代码:

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

注意:缓存的实现留给读者,但它可以重用另一个COM +服务:COM+ Shared Property Manager