【问题标题】:Does COM interop respect .NET AppDomain boundaries for assembly loading?COM 互操作是否尊重程序集加载的 .NET AppDomain 边界?
【发布时间】:2010-09-19 12:50:02
【问题描述】:

这是核心问题:我有一个在单独的 AppDomain 中使用 COM interop 的 .NET 应用程序。 COM 东西似乎正在将程序集加载回默认域,而不是从中调用 COM 东西的 AppDomain。

我想知道的是:这是预期的行为,还是我做错了什么导致这些与 COM 相关的程序集被加载到错误的 AppDomain 中?请参阅下面的情况更详细的描述...

该应用程序由 3 个程序集组成: - 主 EXE,应用程序的入口点。 - common.dll,只包含一个接口 IController(IPlugin 风格) - controller.dll,包含一个实现 IController 和 MarshalByRefObject 的 Controller 类。此类完成所有工作并使用 COM 互操作与另一个应用程序进行交互。

主EXE的相关部分如下所示:

AppDomain controller_domain = AppDomain.CreateDomain("Controller Domain");
IController c = (IController)controller_domain.CreateInstanceFromAndUnwrap("controller.dll", "MyNamespace.Controller");
result = c.Run();
AppDomain.Unload(controller_domain);

common.dll 只包含这两个东西:

public enum ControllerRunResult{FatalError, Finished, NonFatalError, NotRun}
public interface IController
{
    ControllerRunResult Run();
}

controller.dll 包含这个类(它也调用 COM 互操作的东西):

public class Controller: IController, MarshalByRefObject

首次运行应用程序时,Assembly.GetAssemblies() 看起来与预期一样,common.dll 加载到两个 AppDomain 中,而 controller.dll 仅加载到控制器域中。然而,在调用 c.Run() 之后,我看到与 COM 互操作相关的程序集已加载到默认 AppDomain 中,而不是在发生 COM 互操作的 AppDomain 中。

为什么会发生这种情况?

如果你有兴趣,这里有一点背景:

最初这是一个 1 AppDomain 应用程序。它与之交互的 COM 东西是一个服务器 API,长期使用并不稳定。当 COM 发生 COMException(没有关于其原因的有用诊断信息)时,必须重新启动整个应用程序,然后 COM 连接才能再次工作。简单地重新连接到 COM 应用程序服务器会再次立即导致 COM 异常。为了解决这个问题,我尝试将 COM 互操作的东西移动到一个单独的 AppDomain 中,这样当神秘的 COMExceptions 发生时,我可以卸载它发生的 AppDomain,创建一个新的并重新开始,而无需手动重新启动应用程序.无论如何,这就是理论......

【问题讨论】:

  • fyi,如果其他人看一下这个 - 这是 HP Quality Center API DLL 给我这个问题。我通过让应用自行重启来解决这个问题,但我仍然对为什么会发生这种情况很感兴趣。

标签: c# com interop com-interop appdomain


【解决方案1】:

不要将控制器设为 MBR。创建一个小型代理,它将控制器加载到第二个域中并启动它。这样控制器 dll 将不会在第一个域中加载。

【讨论】:

  • 嗨 Sunny,这就是在通用 dll 中使用 IController 接口背后的想法。 Controller.dll 不会加载到默认域中。控制器域中 Controller 类的 COM 互操作活动似乎会导致在默认域中加载与 COM 相关的 DLL。
  • 更新:使用代理在其他域中创建和运行Controller具有相同的效果。 Controller.dll 没有被加载到默认的 AppDomain 中(这不是问题),但 COM 互操作的东西似乎仍在将 COM 程序集加载回默认域中。
  • 您是否尝试在第二个域中手动加载互操作程序集,而不是在自动加载器上中继?
  • 您是建议手动加载 .NET 互操作程序集,还是建议加载互操作程序集随后导致加载的 COM 程序集?我会尽快检查一下,看看会发生什么。
  • 我必须加载互操作程序集。
【解决方案2】:

不幸的是,COM 组件是在进程空间中加载的,而不是在 AppDomain 的上下文中。因此,您将需要手动拆除(释放和卸载)您的本机 DLL(适用于 COM 和 P/Invoke)。简单地破坏一个应用程序域对你没有好处,但是重新生成整个过程不应该是重置 COM 状态所必需的(简单地重新创建 COM 对象也应该正常工作,这听起来像是组件提供程序代码中的一个错误,也许他们能解决吗?)

参考文献

(TechNet) Process Address Space

(MSDN) Application Domains

(MSDN) Boundaries: Processes and AppDomains

【讨论】:

  • 感谢您的回复,肖恩。现在已经很久了,我不记得我是否尝试过重新创建 COM 对象,但我怀疑我做过。有时间我会试试你的建议。同时,+1 用于提及进程空间。
  • 所以我还没有机会尝试这个,但你确实回答了主要问题,所以我将其标记为已回答。非常感谢大家的意见。
  • 是的,我确实怀疑这是提供商代码中的错误,尤其是导致我尝试卸载/重新加载 COM 组件的错误原因是一个真正的谜。总有一天我应该重新检查我自己的代码,然后再责怪别人。
  • 谢谢,这很有帮助。谁能提供链接或其他参考资料,以便我更好地了解 Process Space 与 App Domain,以及 .NET-as-COM 如何使用 Process Space?
  • 已添加链接。 “进程空间”是“进程地址空间”的缩写,指的是一个进程的整个内存占用。 “应用程序域”是进程地址空间中的一小块内存,保留供 .NET 应用程序使用。一个 .NET 应用程序可以分配多个 AppDomain,但通常只有一个(称为主 AppDomain)。COM 和 WinAPI DLL 被加载到 AppDomain 分配内存之外的进程地址空间中,然后 CLR 将它们映射到每个加载的 AppDomain 以便它们对内部的 .NET 代码可见。 HTH
【解决方案3】:

这是肖恩·威尔逊的答案正确的证据

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    • 2010-12-10
    • 2010-11-14
    • 1970-01-01
    • 1970-01-01
    • 2010-12-24
    • 1970-01-01
    相关资源
    最近更新 更多