Вопрос о выполнении асинхронного вызова в C # (WPF) для COM-объекта - PullRequest
2 голосов
/ 03 июня 2010

Извините, что задаю такой простой вопрос, но у меня, похоже, зависает мозг на этом! Я вызываю объект COM (ATL) из моего проекта WPF. Метод 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 по экрану. Я не могу - в течение 5 секунд.

Что я делаю не так? Я уверен, что это довольно просто. Подпись делегата неверна?

Спасибо.

Ответы [ 3 ]

1 голос
/ 04 июня 2010

Я еще немного подумал и проверил это. В коде C # нет ничего плохого. Если объект ATL является объектом STA (как это было в моем случае), он будет вызываться в основном потоке, независимо от попыток кода C # вызвать его в рабочем потоке. Изменение объекта ATL на объект MTA позволяет вызывать его асинхронно.

1 голос
/ 05 июня 2010

Я уверен, что вы правы относительно вызова вашего кода ATL, который маршалируется в поток GUI, потому что код ATL - STA, блокируя тем самым поток GUI.

Два решения:

  1. Перестройте часть ATL, чтобы она была MTA, что, возможно, неосуществимо, или
  2. Оставьте ATL как STA, но сначала создайте COM-объект в потоке, созданном для этой цели, чтобы он получил другую квартиру.

Приложение WPF действительно отлично работает с несколькими потоками пользовательского интерфейса, если каждый поток пользовательского интерфейса управляет своей собственной частью пользовательского интерфейса, а части разделяются HwndSource. Другими словами, второй поток, который выполняет часть пользовательского интерфейса, реализует Win32 HWND, который затем внедряется в часть пользовательского интерфейса, выполняемого основным потоком.

Если ваш COM-объект сам по себе не является объектом графического интерфейса, тогда очень легко создать его в отдельном рабочем потоке и оставить его там. Поскольку это объект STA, все вызовы будут перенаправляться в другой поток.

1 голос
/ 03 июня 2010

BeginInvoke все еще будет выполнять ваш вызов в том же потоке, только асинхронно *. Вы можете создать новый Thread объект:

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

или попробуйте новую .Net 4 новую библиотеку задач 1007 *:

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

которые по сути одно и то же. **

* Метод BeginInvoke типа делегата выполняется в том же потоке, что и вызывающая программа, но в фоновом режиме. Я не уверен, есть ли правила относительно того, когда выполняется, но это, конечно, не в том порядке, в каком вы хотите. Однако асинхронные методы , такие как BeginRead, выполняются в специальном потоке, отдельном от основного.
** Существует небольшая разница: метод Thread всегда создает новый объект Thread, тогда как система Task имеет пул потоков для работы, что в теории более эффективно.

...