【问题标题】:Calling COM Interop API from a Background Thread?从后台线程调用 COM 互操作 API?
【发布时间】:2014-11-22 18:12:33
【问题描述】:

我实例化了一个服务类,它包含一个执行终端自动化的 COM 互操作组件。我正在使用 Microsoft 的任务库 (TPL)。我想从 TPL 任务(后台线程)调用 COM 对象,这样我的 UI 在 COM 对象工作时不会冻结。

但是,当我从后台线程(接收 IntPtr)调用我的第一个函数时,会引发 COM 异常,详细说明 HRESULT: 0xC0000005。

我知道这是一个访问冲突异常,我认为我没有以正确的方式混合我的对象。

如何从后台线程调用主线程中创建的 COM 对象的方法?

public void Button1_Click(object sender, EventArgs e) 
{
    var comWrapper = new COMWrapper(); // A simple wrapper for a COM object

    Task.Factory
        .StartNew(() => LoadStuff(comWrapper))
        .ContinueWith(() => {
            // Output results...
        });
}

int LoadStuff(COMWrapper w)
{
    return w.LoadStuffFromCOM();
}

调用COM对象的方法:

int LoadStuffFromCOM()
{
    string buffer;
    IntPtr pointer = Marshal.StringToHGlobalUni(buffer);

    return comObject.GetValue(pointer); // Exception here...
}

【问题讨论】:

  • COM 异常 0xC0000005access denied。这是一个特权错误。由于您没有发布任何代码,因此很难找出可能导致它的原因。 “当我调用我的第一个函数时”恐怕没有什么好说的了。您可以编辑您的问题以提供更多详细信息吗?
  • 我正在从后台线程调用一个函数。它接收一个从 COM 输出一些值的 IntPtr。如果我在主线程上运行它就可以了。
  • 对不起,伙计,我不能提供太多代码,因为我不允许这样做。
  • 阅读我在上面的评论。真正的代码只是一个调用 COM 组件的方法。

标签: c# com task-parallel-library


【解决方案1】:

许多旧版 COM 对象被设计为在桌面应用程序中运行。这意味着他们希望在 UI 线程上运行,并将 Windows 消息泵作为唯一的同步方法。

您现在正尝试在它可能从未“听说过”的环境中运行该代码。您很有可能违反了作者在编写代码时所做的假设。

如果您不违反假设,代码可能会起作用,但如果您这样做,那么您将遇到问题(或两个,或两打)。

【讨论】:

    【解决方案2】:

    可以让 COM / OLE 互操作对象在后台运行,但必须使用正确的线程模型编译它们

    如果是 Delphi for MTA,它应该被编译

    initialization
      TTypedComObjectFactory.Create(ComServer, TSomeLogic, Class_SomeLogic,
        ciMultiInstance, tmFree);
    end.
    

    如果是 STA,它默认使用

    initialization
      TTypedComObjectFactory.Create(ComServer, TSomeLogic, Class_SomeLogic,
        ciMultiInstance, tmApartment);
    end.
    

    在 C/C++ 和其他非托管语言中应该是类似的

    更多信息可以在这里找到: http://msdn.microsoft.com/en-us/library/ff647812.aspx#scalenetchapt07 _topic11

    【讨论】:

      【解决方案3】:

      如果您绝望,您可以生成一个执行 com 代码的完整独立进程。那么你只有需要写ipc

      【讨论】: