【问题标题】:Question about making Asynchronous call in C# (WPF) to COM object关于在 C# (WPF) 中对 COM 对象进行异步调用的问题
【发布时间】:2011-02-27 11:16:30
【问题描述】:

很抱歉问了这么一个基本问题,但我似乎对这个问题感到头晕目眩!我正在从我的 WPF 项目中调用一个 COM (ATL) 对象。 COM 方法可能需要很长时间才能完成。我想我会尝试异步调用它。我有几个演示行显示了问题。

private void checkBox1_Checked(object sender, RoutedEventArgs e)
 {
      //DoSomeWork();
      AsyncDoWork caller = new AsyncDoWork(DoSomeWork);
      IAsyncResult result = caller.BeginInvoke(null, null);            
  }

private delegate void AsyncDoWork();
private void DoSomeWork()
{
   _Server.DoWork();
}

ATL 方法 DoWork 非常令人兴奋。它是:

STDMETHODIMP CSimpleObject::DoWork(void)
{
   Sleep(5000);
   return S_OK;
}

我曾期望以这种方式运行会导致复选框被立即选中(而不是在 5 秒内),并且我能够在屏幕上移动 WPF gui。我不能 - 5 秒钟。

我做错了什么?我敢肯定这很简单。委托人签名错误?

谢谢。

【问题讨论】:

  • 我对此做了更多的思考和测试。我认为 C# 代码没有任何问题。当我用 private void DoSomeWork() { Thread.Sleep(5000); 替换 ATL 调用时一切正常。我认为发生的事情是 ATL 是一个 STA 对象。因此,即使调用最初是在工作线程上进行的,ATL 端的实际工作也是在主 GUI 线程上完成的。解决它的一种(痛苦的)方法是使 COM 组件成为 MTA。

标签: wpf com-interop atl


【解决方案1】:

我确信您对 ATL 代码的调用被编组到 GUI 线程是正确的,因为 ATL 代码是 STA,从而阻塞了您的 GUI 线程。

两种解决方案:

  1. 将 ATL 部分重新架构为 MTA,这可能不可行,或者
  2. 将 ATL 保留为 STA,但最初在为此目的创建的线程中构造 COM 对象,因此它将获得不同的单元。

一个 WPF 应用程序实际上可以使用多个 UI 线程正常运行,只要每个 UI 线程都管理自己的 UI 部分,并且这些部分由 HwndSource 分隔。换句话说,运行部分 UI 的第二个线程实现了一个 Win32 HWND,然后将其嵌入到由主线程运行的部分 UI 中。

如果您的 COM 对象本身不是 GUI 对象,那么在单独的工作线程中构造它并将其保留在那里应该很容易。由于它是一个 STA 对象,所有调用都将被封送到另一个线程。

【讨论】:

【解决方案2】:

BeginInvoke 仍将在同一个线程上执行您的调用,只是异步执行*。您可以创建一个新的Thread 对象:

Thread comthread = new Thread(new ThreadStart(delegate() { DoSomeWork(); }));
comthread.Start();

或试用 .Net 4 的新 Task library

Task.Factory.StartNew(() =>
{
    DoSomeWork();
});

它们本质上是一样的。**

*委托类型的BeginInvoke 方法与调用者在同一线程上执行,但在后台执行。我不确定是否有关于何时执行的规则,但它肯定不是你想要的顺序。但是,像 BeginRead 这样的异步方法在与主线程分开的特殊线程上执行。
**略有不同 - Thread 方法总是会创建一个新的Thread 对象,而Task 系统有一个线程池可供使用,理论上效率更高。

【讨论】:

  • JustABill,感谢您的快速回复。启动一个新线程也没有解决问题。见我上面的评论;我认为问题在于 ATL 对象存在于单线程单元中。顺便说一句,一旦我将代码替换为仅休眠 5 秒而不进行 COM 调用,BeginInvoke 工作正常(就像您的新 Thread(...) 想法一样)并且清楚地表明工作(在这种情况下是休眠)是在工作线程上完成。所以我没有看到“BeginInvoke 方法在同一个线程上执行......”。我没有 .Net 4.0 的选项 - Task.Factory 听起来很整洁。谢谢。
【解决方案3】:

我对此做了更多的思考和测试。 C# 代码没有任何问题。如果 ATL 对象是 STA 对象(在我的情况下),它将在主线程上调用,而不管 C# 代码是否尝试在工作线程上调用它。将 ATL 对象更改为 MTA 对象可以异步调用它。

【讨论】:

    猜你喜欢
    • 2010-10-01
    • 2012-01-17
    • 1970-01-01
    • 2011-06-07
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多