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

发布于 2024-09-04 03:01:23 字数 697 浏览 6 评论 0 原文

很抱歉问这么基本的问题,但我似乎在这个问题上脑子冻结了!我正在从我的 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秒钟。

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

谢谢。

Sorry to ask such a basic question but I seem to have a brain freeze on this one! I'm calling a COM (ATL) object from my WPF project. The COM method might take a long time to complete. I thought I'd try and call it asychronously. I have a few demo lines that show the problem.

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();
}

The ATL method DoWork is very exciting. It is:

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

I had expectations that running this way would result in the checkbox being checked right away (instead of in 5 seconds) and me being able to move the WPF gui around the screen. I can't - for 5 seconds.

What am I doing wrong? I'm sure it's something pretty simple. Delegate signature wrong?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我要还你自由 2024-09-11 03:01:23

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

两种解决方案:

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

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

如果您的 COM 对象本身不是 GUI 对象,那么应该很容易在单独的工作线程中构造它并将其留在那里。由于它是 STA 对象,因此所有调用都将编组到其他线程。

I'm sure you're right about the call to your ATL code getting marshaled to the GUI thread because the ATL code is STA, thereby blocking your GUI thread.

Two solutions:

  1. Rearchitect the ATL portion to be MTA, which may not be feasible, or
  2. Leave the ATL as STA but initially construct the COM object in a thread created for that purpose so it will get a different apartment.

A WPF application actually runs just fine with multiple UI threads, as long as each UI thread has manages its own part of the UI, and the parts are separated by HwndSource. In other words, the second thread that runs part of the UI implements a Win32 HWND which is then embedded in the portion of the UI run by the main thread.

If your COM object isn't itself a GUI object, then it should be very easy to construct it in a separate worker thread and leave it there. Since it is a STA object, all calls will be marshaled to the other thread.

初相遇 2024-09-11 03:01:23

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

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

或者尝试 .Net 4 的新 任务库

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

本质上是相同的东西。**

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

BeginInvoke is still going to execute your call on the same thread, just asynchronously*. You can either create a new Thread object:

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

or try out .Net 4's new Task library:

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

which are essentially the same thing.**

*A delegate type's BeginInvoke method executes on the same thread as the caller, but in the background. I'm not sure if there are rules regarding what gets executed when, but it's certainly not in the order you want. However, asynchronous methods like BeginRead execute on a special thread separate from the main one.
**There is a slight difference - the Thread method will always create a new Thread object, whereas the Task system has a pool of threads to work with, which is in theory more efficient.

紫竹語嫣☆ 2024-09-11 03:01:23

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

I have done some more thinking and testing about this. There is nothing wrong with the C# code. If the ATL object is an STA object (as it was in my case), it will be called on the main thread, regardless of attempts by the C# code to call it on a worker thread. Changing the ATL object to an MTA object makes it possible to to call it asynchronously.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文