Создание диалога с WebBrowser из BackgroundWorker - PullRequest
1 голос
/ 10 января 2012

У меня есть программа очистки веб-страниц.В главной форме вы можете выбрать веб-сайт и клиента и нажать кнопку «Перейти», и он запустит поток BackgroundWorker, который использует WebRequest и HTMLAgilityPack для обработки серии запросов для SiteX и ClientX, причем каждый запрос состоит из нескольких страниц.Каждый поток BackgroundWorker имеет проверки, которые выполняются, и если он сталкивается с проблемой, он выдает диалоговое окно для пользователя, чтобы прервать поток, отменить запрос, проигнорировать ошибку или (если запустить из IDE) ввести в код.Я хочу, чтобы это диалоговое окно содержало элемент управления WebBrowser для отображения красиво оформленной HTML-страницы.Однако, поскольку он вызывается из потока BackgroundWorker, я получаю исключение «текущий поток не находится в однопоточной квартире».

Вот функция, которая создает диалоговое окно:

protected bool ValidatePage( bool pagePasses, string msg ) {
  if ( pagePasses == false ) {
    AbortIgnoreSuspend ais = new AbortIgnoreSuspend( responsehtml, msg );
    ais.ShowDialog();
    switch ( ais.DialogResult ) {
      case DialogResult.Abort: // Aborts entire thread
        Abort = true;
        worker.CancelAsync();
        return false;
      case DialogResult.Cancel: // Aborts this case
        Abort = true;
        return false;
      case DialogResult.Ignore: // Ignore and continue
        return true;
      case DialogResult.Retry:  // Debug
        Debug.Assert( false, "Suspending Thread" );
        return true; // Will return you to calling thread and allow you to continue
      default:
        return true;
    }
  }
  return true;
}

Я нашел примеры, когда запуск Thread () с ApartmentState, установленным в ApartmentState.STA, мог бы создать мой WebBrowser, поэтому я сделал следующую корректировку кода:

protected bool ValidatePage( bool pagePasses, string msg ) {
  if ( pagePasses == false ) {
    bool setAbort = false;
    bool assertError = false;
    bool cancelWorker = false;
    bool returnContinue = true;

    Thread th = new Thread( () => {
      AbortIgnoreSuspend ais = new AbortIgnoreSuspend( responsehtml, msg );
      ais.ShowDialog();
      switch ( ais.DialogResult ) {
        case DialogResult.Abort: // Aborts entire thread
          setAbort = true;
          cancelWorker = true;
          returnContinue = false;
          break;
        case DialogResult.Cancel: // Aborts this case
          setAbort = true;
          returnContinue = false;
          break;
        case DialogResult.Ignore: // Ignore and continue
          returnContinue = true;
          break;
        case DialogResult.Retry:  // Debug
          assertError = true;
          returnContinue = true; // Will return you to calling thread and allow you to continue
          break;
        default:
          returnContinue = true;
          break;
      }

    } );
    th.SetApartmentState( ApartmentState.STA );
    th.Start();
    th.Join();

    Abort = setAbort;
    Debug.Assert( !assertError, "Suspending thread for debugging" );
    if ( cancelWorker ) { worker.CancelAsync(); }
    return returnContinue;
  }
  return true;
}

Это, похоже, работает(Я тестировал с одним BackgroundWorker), однако я вполне уверен, что из-за недостатка опыта работы с потоками я допустил некоторые ошибки в безопасности потоков.Что я сделал не так, что я пропустил?

1 Ответ

0 голосов
/ 12 апреля 2012

В зависимости от того, как вы создаете потоки в .NET, .NET установит для квартиры этого потока зверя с именем MTA или STA . И MTA, и STA являются технологиями COM; кроме этого они мало используются в .NET (как упомянуто в документации MS .NET). MS предпочитает использовать по умолчанию определенные потоки .NET для MTA, если вы не установили это явно, я подозреваю, что ваш поток не является основным потоком STA пользовательского интерфейса.

Я вижу, что вы используете довольно изящный BackgroundWorker в .NET. Если вы установите WorkerReportsProgress на true , установите обработчик для ProgressChanged и переместите свой диалоговый код для вызова внутри этого нового обработчика, это должно решить вашу проблему.

ProgressChanged на самом деле не должен фактически сообщать о прогрессе, вы можете использовать его для чего угодно, особенно когда вам нужно переключение контекста потока. Аргумент, который вы передаете ReportProgress, может быть любым объектом, который вам нравится.

Почему это работает? ProgressChanged автоматически выполняет маршалинг потока из контекста рабочего потока в контекст основного потока пользовательского интерфейса (UI). Только в потоке пользовательского интерфейса вы можете безопасно выполнять вызовы пользовательского интерфейса.

...