【问题标题】:C# Dynamic COM in Windows Server 2012Windows Server 2012 中的 C# 动态 COM
【发布时间】:2014-03-04 15:59:19
【问题描述】:

遇到了一个问题,我是 c# 中的 COM 类型,使用

    this.rtwbType = Type.GetTypeFromProgID(progId, true);
    this.rtwb = Activator.CreateInstance(this.rtwbType);

然后我正在做一些事情,完成后我在 rtwb 上调用 exit - 这样它就可以关闭,然后调用:

    Marshal.ReleaseComObject(this.rtwb);

在 2008 R2 中这很好而且很花哨 - 但是我们带到 2012 的实例在这里抛出异常。

System.Runtime.InteropServices.COMException (0x800706BA):RPC 服务器不可用。 (HRESULT 异常:0x800706BA)

就像我说的在其他地方工作得很好。

任何指针?

【问题讨论】:

  • COM 服务器退出。可能是它出于某种原因过早地告别了它,也可能是您在不应该这样做时通过调用 ReleaseComObject() 来帮助它这样做。您需要更好地研究问题并注意服务器在做什么。
  • @JonH,哪一行会抛出这个异常?
  • @Noseratio 它是似乎抛出它的释放线。
  • @HansPassant,实际上这可以用InternetExplorer.Application 轻松复制。我想知道这是否是 Windows 8/Windows Server 2012 引入的新行为。
  • @JonH,我已经更新了答案,说明如何选择性地忽略此错误。

标签: c# .net com windows-server-2012


【解决方案1】:

以下代码重现此错误(至少在 Windows 8.1 下):

var type = Type.GetTypeFromProgID("InternetExplorer.Application", true);
dynamic ie = Activator.CreateInstance(type);

ie.Quit(); // this disconnects the COM proxy from the out-of-proc IE object

Marshal.ReleaseComObject(ie); // throws

显然,ReleaseComObject 的实现在处理 COM 代理对象时所做的不仅仅是调用 IUnknown::Release。我的猜测是,它可能正在调用IRemUnknown::RemRelease,它返回HRESULT 并出现错误,所以ReleaseComObject 抛出,因为out-of-proc 对象已经被ie.Quit() 断开并销毁。

据推测,这种行为是在 Windows Server 2012 中引入的。

您可能做的最好的事情就是忽略这个特定的错误:

try
{
    Marshal.ReleaseComObject(ie);
}
catch (COMException ex)
{
    // I'm getting 0x80010108, rather than 0x800706BA
    // The object invoked has disconnected from its clients. (Exception from HRESULT: 0x80010108 (RPC_E_DISCONNECTED)

    if ((uint)ex.ErrorCode != 0x80010108) 
        throw;
}

更新:这是 Quit 这样的 API 的预期行为,它不违反任何 COM 规则。 Quit 的目标是为显式关闭提供 API。我相信它在内部使用CoDisconnectObject,作为关机过程的一部分。这会断开所有外部代理引用,因此服务器知道它可以安全关闭。

ReleaseComObject 在那之后仍在尝试做一些事情(例如,很可能在断开连接的代理上调用 IUnknown::QueryInterface)这一事实不是 COM 服务器的错误。

【讨论】:

  • 我走上了这条路——但是我记录并忽略了 ReleaseComObject 的所有 COMException 异常。感谢您的帮助!
  • 请注意,ReleaseComObject 很可能只是在代理上调用IUnknown::Release。服务器拉地毯不是RCW的错。这只是异常的运行时行为,只是在这种情况下,应用程序(COM 服务器)没有崩溃,它在有外部引用时粗暴地退出。
  • @acelent,IUnknown::Release 没有事件返回 HRESULT。它如何变成异常?此外,如果使用Quit 明确要求,进程内服务器断开外部引用并没有错。这就是 Quit 的用途,这是正确的行为。
  • 您对异常的看法是正确的,它一定是在做更多的事情。但是,我不同意Quit 应该这样做,但事实是确实如此。它的稀疏文档没有描述它的残酷性,所以我假设它从一开始就是这样实现的,并且新版本没有为了向后兼容性而改变。 IE。人们曾经期望Quit 不会等待外部引用,因此他们相信IE 进程会真正退出。我想说 op 正在使用的 COM 服务器是一样的,甚至可能没有更新。除非他真的能换服务器。
  • @acelent,我可以放心,自从我第一次在 IE5.5 中尝试以来,Quit for InternetExplorer.Application 一直是这样工作的。 Word.Application 也一样。我自己碰巧开发了自定义的进程外服务器,并且出于相同的目的使用了CoDisconnectObject,我不认为这很残酷。
【解决方案2】:

我假设rtwbInternetExplorer.Application,因为Noseratio's test 似乎很好地复制了这个问题。

似乎 Internet Explorer 的 Quit() 方法在应用程序上下文之外调用并不安全,例如通过流程外自动化。它违反了 COM 服务器应用程序的规则。

例如,即使在Quit() 之后,Office 应用程序也会在有外部引用时继续运行,所以这确实有点出乎意料。

为了安全起见,您可以在退出时放开引用,使用 OnQuit 事件:

using System;
using System.Threading;
using System.Runtime.InteropServices;

public class TestIE
{
    public static void Main()
    {
        Console.WriteLine("Creating application");
        dynamic app = Activator.CreateInstance(Type.GetTypeFromProgID("InternetExplorer.Application"));
        Console.WriteLine("Created application");
        app.Visible = true;

        app.OnQuit += new Action(() => {
            Console.WriteLine("Entered OnQuit");
            Marshal.ReleaseComObject(app);
            app = null;
            Console.WriteLine("Leaving OnQuit");
        });

        Console.WriteLine("Sleeping");
        Thread.Sleep(5000); // enough time to see if iexplore.exe is running
        Console.WriteLine("Slept");

        Console.WriteLine("Quitting");
        app.Quit();
        Console.WriteLine("Quit");

        Console.WriteLine("Sleeping");
        Thread.Sleep(5000); // enough time to see if iexplore.exe is running
        Console.WriteLine("Slept");

        Console.WriteLine(app == null);
    }
}

如果您确实需要使用ReleaseComObject,请丢弃它可能抛出的任何异常。我不确定你是否应该只丢弃COMException,在开发过程中尝试一下。

【讨论】:

  • 我喜欢这种优雅的解决方案,但 Internet Explorer 只是@Noseratio 在其他地方使用的一个示例。我的 Com 服务器没有我可以挂钩的有用事件!
  • 你能指定它是哪个服务器吗?使用 Excel 和 Powerpoint,实现类似行为的一种方法是确保您打开了一个永远不会明确关闭的虚拟工作簿或演示文稿,并附加到 WorkbookBeforeClosePresentationClose 事件并检测它何时关闭。无论哪种方式,如果 COM 服务器是您的并且它退出并带有挂起的外部引用,您可能应该修复它。也就是说,您应该调用CoSuspendClassObjects,但要继续运行,直到所有引用都消失。
  • @acelent, “看起来 Internet Explorer 的 Quit() 方法在应用程序上下文之外调用并不安全……它违反了 COM 服务器应用程序的规则。” - 我认为这违反了任何 COM 规则,请参阅我发布的更新。
  • 应在最终用户强制关闭应用程序(COM 服务器)时断开远程对象,即用户关闭主窗口并被警告它可能产生的问题到其他应用程序,仍然说“是”。 Application.Quit() 不属于这种情况,它是程序化的,只要有外部引用,服务器就应该保持活动状态。
猜你喜欢
  • 2016-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-10
  • 1970-01-01
  • 2014-02-22
  • 2013-03-11
  • 1970-01-01
相关资源
最近更新 更多