WPF, VB и Объект приложения

Сценарий:

  • Форма VB 6 имеет InteropControl (WinForms).
  • InteropControl имеет ElementHost
  • ElementHost имеет мой контроль WPF

Все, кажется, работает за исключением того, что Приложение. Текущий, кажется, является пустым, когда мне нужен он. Все, что я действительно хочу сделать, сцепиться в событие необработанного исключения, прежде чем первая форма будет полностью отображена.

  1. В этом сценарии Объект приложения WPF когда-нибудь создан?
  2. Если так, когда это создается?
  3. В противном случае, что заставляет сообщения быть накачанными?
  4. Что произошло бы, если бы я запустил Объект приложения на фоновом потоке?
8
задан Jonathan Allen 10 July 2010 в 19:16
поделиться

2 ответа

Сначала я объясню, как работают циклы сообщений в сценариях interop, затем отвечу на ваши вопросы и дам несколько рекомендаций.

Реализация петель сообщений в вашем сценарии

Ваш сценарий включает в себя три отдельные технологии: VB 6, WinForms и WPF. Каждая из этих технологий реализована поверх Win32. Каждая из них имеет свой собственный GetMessage()/DispatchMessage() цикл для перекачки сообщений окна Win32.

Вот где реализован каждый цикл GetMessage()/DispatchMessage():

  • VB 6 реализует его внутри
  • WinForms реализует его в System.Windows.Forms.Application
  • WPF реализует его в System.Windows.Threading.Dispatcher

Объект WPF Application необязателен

Ваш вопрос предполагает, что WPF реализует цикл сообщений в объекте Application. Это не так. В WPF все основные функции, которые в WinForms выполнялись в объекте Application, были перенесены в другие объекты, такие как Dispatcher, HwndSource, InputManager, KeyboardDevice, MouseDevice и т.д.

В WPF объект Application является полностью необязательным. Вы можете создать полноценное WPF-приложение со сложным пользовательским интерфейсом, никогда не создавая объект Application. Объект Application полезен только в том случае, если вам нужна одна из предоставляемых им служб, например:

  • Общий ResourceDictionary
  • Сопоставление WM_APPACTIVATE сообщения в Activated и Deactivated события
  • Сопоставление WM_QUERYENDSESSION сообщения в OnSessionEnding событие
  • Lifecycle управление (Startup/Run/Shutdown/Exit)
  • Автоматическое выключение при закрытии последнего окна или главного окна
  • Значок по умолчанию для окон WPF
  • Запоминание первого открытого окна (MainWindow)
  • Общая регистрация для событий NavigationService (Navigated, etc)
  • StartupUri

Класс Application также предоставляет несколько полезных статических членов, таких как FindResource, GetResourceStream и LoadComponent, которые не требуют существования объекта Application.

Когда вы вызываете Application.Run(), все, что он делает, это:

  1. Устанавливает механизм для обработки WM_APPACTIVATE и WM_QUERYENDSESSION, и
  2. Выполняет Dispatcher. Run()

Вся реальная функциональность цикла сообщений находится в Dispatcher.Run().

Регистрация не обработанных исключений в цикле сообщений WPF

Событие Application.DispatcherUnhandledException, которое вы пытались использовать, является простой оберткой вокруг события Dispatcher.UnhandledException. Я думаю, что они включили его в объект Application, потому что программисты WinForms ожидали, что оно там будет, но ваш вопрос показывает, что это могло привести к обратному результату.

Чтобы зарегистрироваться на необработанные исключения из диспетчера WPF, все, что вам нужно сделать, это:

Dispatcher.Current.UnhandledException += ...;

В отличие от Application.Current, Dispatcher.Current не может быть null: Если вы обратитесь к Dispatcher.Current из потока, у которого еще нет диспетчера, он будет создан автоматически.

Как только вы подписались на Dispatcher.UnhandledException, любое необработанное исключение из цикла сообщений диспетчера в текущем потоке вызовет ваш обработчик событий. Обратите внимание, что это относится только к необработанным исключениям, когда Dispatcher.Run() перекачивает сообщения: Когда другая технология, такая как VB 6 или WinForms, перекачивает сообщения, вместо этого будет использоваться механизм обработки исключений этой технологии.

Цикл сообщений WPF также необязателен

WPF может работать не только без создания объекта Application, но и без Dispatcher.Run(), пока другая технология перекачивает сообщения окна Win32. Это делается путем создания фиктивного окна и/или подкласса окна WPF для установки крючка сообщений. Таким образом, независимо от того, какой цикл сообщений перекачивает сообщения, WPF будет работать так, как ожидается.

На самом деле, когда вы используете ElementHost, диспетчер WPF не используется для перекачки сообщений, если вы не используете один из следующих методов:

  • Window.ShowDialog
  • Dispatcher. Invoke
  • Dispatcher.Run (или эквивалентно, Application.Run)
  • DispatcherOperation.Wait

Из-за этого ваш обработчик исключений WPF, вероятно, не будет вызван. Вместо этого вам нужно установить обработчик исключений на уровне VB 6 или WinForms.

Ответы на ваши вопросы

В этом сценарии когда-либо создается объект WPF Application?

Нет.

Если нет, что вызывает перекачку сообщений?

VB 6 перекачивает сообщения.

Что произойдет, если я запущу объект Application в фоновом потоке?

Очень мало:

  • Если у вас есть ресурсы приложения, они будут созданы в фоновом потоке, что может привести к исключениям при их использовании в основном потоке.

  • Если вы добавите обработчик к Application.Current.DispatcherUnhandledException, он будет применяться только к фоновому потоку. Другими словами, обработчик никогда не будет вызван, если вы не создадите окна в фоновом потоке.

  • Ваше Application.Startup будет вызываться из фонового потока, что, вероятно, плохо. То же самое касается StartupUri.

Рекомендация

Из того, что вы спрашиваете, следует, что вы получаете необработанное исключение во время загрузки вашего элемента управления WPF и хотите поймать это исключение. В этом случае, вероятно, лучшим планом будет обернуть ваш элемент управления WPF внутри простого ContentControl, конструктор которого использует код, подобный этому, для создания дочернего элемента:

Dispatcher.Current.UnhandledException += handler;
Disptacher.Current.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
  Content = CreateChildControl();
  Dispatcher.Current.Invoke(DispatcherPriority.ApplicationIdle, new Action(() => {});
});

Как это работает: BeginInvoke откладывает создание дочернего элемента до тех пор, пока VB 6 и/или InteropControl не завершат всю обработку. Вызов Invoke после создания дочернего элемента управления вызывает пустое действие с низким приоритетом, заставляя все ожидающие DispatcherOperations завершиться.

В результате все исключения, которые были брошены в конструкторе или сразу после него, теперь передаются в ваш обработчик исключений.

12
ответ дан 5 December 2019 в 11:22
поделиться

В WPF объект Application не отвечает непосредственно за подкачку сообщений, это Dispatcher. Когда вы запускаете приложение WPF, при запуске вызывается Application.Run(), который вызывает Dispatcher.Run().

В вашем сценарии взаимодействия Application.Current вернет null, поскольку оно никогда не создавалось. Подкачка сообщений обрабатывается VB, поскольку он создает главное окно. Если вы полагаетесь на нее в своем коде, вы можете либо:

  • Создать новый объект Application:

    if (Application.Current != null)
    {
     new Application();
    }
    

    Application является синглтоном, поэтому он будет автоматически сохранен в Application.Current.

  • Избегайте полагаться на него, когда это возможно (что я считаю рекомендуемым способом). Следует отметить, что многие из сервисов, предоставляемых этим классом (например, событие Exit), в любом случае будут недоступны в вашем сценарии. Если вам нужно только событие необработанного исключения, вы можете использовать Dispatcher.CurrentDispatcher.UnhandledException.

3
ответ дан 5 December 2019 в 11:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: