关于在C#(WPF)中向COM对象进行异步调用的问题

时间:2010-06-03 18:43:31

标签: wpf com-interop atl

很抱歉提出这样一个基本问题,但我似乎对这个问题进行了大脑冻结!我正在从我的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秒钟。

我做错了什么?我相信这很简单。代表签名错了吗?

感谢。

3 个答案:

答案 0 :(得分:1)

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系统有一个可以使用的线程池,这在理论上更有效。

答案 1 :(得分:1)

我已经做了一些更多的思考和测试。 C#代码没有任何问题。如果ATL对象是一个STA对象(就像我的情况那样),它将在主线程上被调用,无论C#代码尝试在工作线程上调用它。将ATL对象更改为MTA对象可以异步调用它。

答案 2 :(得分:1)

我确信您对ATL代码的调用是正确的,因为ATL代码是STA,因此会阻塞您的GUI线程。

两种解决方案:

  1. 将ATL部分重新架构为MTA,这可能不可行,或
  2. 将ATL保留为STA,但最初在为此目的创建的线程中构造COM对象,以便它将获得不同的公寓。
  3. WPF应用程序实际上可以很好地运行多个UI线程,只要每个UI线程都管理自己的UI部分,并且这些部分由HwndSource分隔。换句话说,运行部分UI的第二个线程实现了Win32 HWND,然后将其嵌入主线程运行的UI部分中。

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