【问题标题】:How can I marshal an out-of-process COM reference across process boundaries?如何跨进程边界封送进程外 COM 引用?
【发布时间】:2011-06-09 04:24:05
【问题描述】:

我有一个进程外的 COM 服务器,需要密切关注。该服务器作为服务运行,并且在内部是单例的。为简单起见,我称他为BossCom。

我有另一个进程外 COM 服务器,它是一个工作者。为了系统稳定性,它是一个一次性服务器(这意味着如果您创建 2 个 WorkerCom,则有 2 个 WorkerCom.exe 正在运行)。为简单起见,我称他为 WorkerCom。

WorkerCom 可以由任何东西启动,即使有人通过带有正确命令行参数的命令行运行它,它也可以由它自己启动。

BossCom 的总体目标是了解 WorkerCom 周围的情况,知道他们在做什么,并能够给他们下达命令(暂停、停止、加速等)。

我最初的想法是,每当 WorkerCom 启动时,他都会 CoCreateInstance 一个 BossCom 并调用 BossCom->RegisterWorker(IUnknown me)。然后当 WorkerCom 即将关闭时,他会调用 BossCom->UnregisterWorker(IUnknown me)。 BossCom 可以为 IWorkerCom QueryInterface IUnknown 并能够发出命令。

如果所有这些 com 对象都在同一个进程中,那就太好了,但它们不是。我考虑过使用 GlobalInterfaceTable,但它只是在单个进程的意义上是全局的。

我花了几天时间研究这个并且不知所措。可能我是有远见的。

如何将 com 对象的引用从 Worker 编组到 Boss?

哦,而且,不管怎样,BossCom 是用 C# 编写的,而 WorkerCom 是用 ATL C++ 编写的,但我会采用用 VB、Scala、Lisp 或其他任何东西编写的解决方案。我想我可以翻译核心思想。 :-)

【问题讨论】:

  • 这行不通?我承认我没有对 COM 做太多事情,但它在进程外的全部意义在于您已经让 COM 跨进程边界编组调用,不是吗?
  • 对,对于大多数事情来说,但 IUnknown 只是一个指针,对吧? WorkerCom 中的“this”指针在 BossCom 中没有任何意义吧?或者编组过程是否会保持意义。我想我至少应该尝试一下。
  • 它应该可以现成的,因为有代理/存根或类型库来促进编组。事实上,编组正是为了这一点——给客户端一个镜像到服务器的假对象(“代理”)。它从代码的角度透明地工作。
  • 嗯...我想我应该尝试一下。我已经说服自己放弃了它,所以我什至没有尝试过。
  • @sharptooth 是对的,我认为。我在 EXE COM 服务器方面的经验也很有限,但除了 UI 控制和性能之外没有发现太多问题。在 ATL 中,会自动为 EXE COM 服务器生成代理 dll。客户端使用代理 dll 进程内方式,代理 dll 处理所有进程间工作。

标签: c# c++ com interprocess


【解决方案1】:

我最近不得不做一些类似的事情,发现使用共享内存对我来说效果很好。 BossCom 可以创建和拥有共享内存,工人可以通过在共享内存中创建条目来注册。这是我所说的MSDN link。记得使用互斥锁来同步访问内存...

【讨论】:

  • 这可以处理注册,但我不清楚这对工人的指挥和控制有何帮助。
  • 共享内存只是一种通过暂存器进行通信的方式。 BossCom 可以将命令写入内存并发出工人等待的事件信号。还有许多其他方法可以做到这一点,这完全取决于您的安全要求。我只是发现这种方法实施起来非常快,而且非常灵活。
  • 我很欣赏这个想法,但 UAC 让这变得更加困难。我的应用程序必须在 Windows XP 及更高版本中运行。 XP 中不存在使共享内存可跨 UAC 用户边界使用的 API。我过去曾解决过这个问题,但我不想再这样做了。
【解决方案2】:

在 C# 中创建可编组接口的能力有点受限。没有简单的方法来设置代理。 ATL中没有这个问题,在IDL中声明一个回调接口。并通过 RegisterWorker() 调用传递一个实例指针。服务器应该存储它,直到它收到取消注册调用。使用该回调接口生成通知。

【讨论】:

    【解决方案3】:

    您应该看一下Monikers,它用于识别甚至跨不同机器的 COM 对象实例。

    【讨论】:

    • 这很有趣。它可能会使我项目的其他部分更容易。我记得使用 DirectShow,名字只是文本字符串。那些应该非常容易编组。哎呀,我什至可以将它们放在 ESE 表中,这样任何人(如其他工人)都可以找到它们。快速搜索并没有显示我如何为我的对象创建一个名字对象。有什么指点吗?
    【解决方案4】:

    根据 cmets,这确实是开箱即用的。还有一个额外的细节使它起作用。

    最初,当我查看处理复制接口的 C# 接口时,参数类型是 IntPtr。嗯,一个 IntPtr 只是一个 long,所以它按原样传输值,因此不起作用。

    关键是在参数上设置 MarshalAs 属性。所以我的 RegisterWorker 方法如下所示:

        public void RegisterWorker(
            [In, MarshalAs(UnmanagedType.IUnknown)] object ptr
            )
        {
            IWorkerCom worker = (IWorkerCom) ptr;
            Workers.Add(worker);
        }
    

    太棒了。

    【讨论】:

      【解决方案5】:

      您还应该查看 ROT(运行对象表),它可能是解决此问题的不同方法。

      http://msdn.microsoft.com/en-us/library/windows/desktop/ms695276(v=vs.85).aspx

      http://www.codeproject.com/KB/COM/ROTStuff.aspx

      【讨论】:

        猜你喜欢
        • 2017-11-24
        • 2012-12-26
        • 2012-10-14
        • 2012-08-02
        • 2017-05-01
        • 2010-10-04
        • 2014-02-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多