Сценарий:
Все, кажется, работает за исключением того, что Приложение. Текущий, кажется, является пустым, когда мне нужен он. Все, что я действительно хочу сделать, сцепиться в событие необработанного исключения, прежде чем первая форма будет полностью отображена.
Сначала я объясню, как работают циклы сообщений в сценариях interop, затем отвечу на ваши вопросы и дам несколько рекомендаций.
Реализация петель сообщений в вашем сценарии
Ваш сценарий включает в себя три отдельные технологии: VB 6, WinForms и WPF. Каждая из этих технологий реализована поверх Win32. Каждая из них имеет свой собственный GetMessage()/DispatchMessage()
цикл для перекачки сообщений окна Win32.
Вот где реализован каждый цикл GetMessage()/DispatchMessage()
:
System.Windows.Forms.Application
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
событиеMainWindow
)Navigated
, etc)StartupUri
Класс Application также предоставляет несколько полезных статических членов, таких как FindResource
, GetResourceStream
и LoadComponent
, которые не требуют существования объекта Application.
Когда вы вызываете Application.Run()
, все, что он делает, это:
WM_APPACTIVATE
и WM_QUERYENDSESSION
, и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 завершиться.
В результате все исключения, которые были брошены в конструкторе или сразу после него, теперь передаются в ваш обработчик исключений.
В 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
.