WPF, VB и объект приложения - PullRequest
8 голосов
/ 10 июля 2010

Сценарий:

  • Форма VB 6 имеет InteropControl (WinForms).
  • InteropControl имеет ElementHost
  • ElementHost имеет мой элемент управления WPF

Кажется, все работает, за исключением того, что Application.Current кажется нулевым, когда мне это нужно.Все, что я действительно хочу сделать, это подключиться к событию необработанного исключения до того, как полностью отобразится первая форма.

  1. В этом сценарии когда-либо создается объект приложения WPF?
  2. Если так,когда он создается?
  3. Если нет, что вызывает перекачку сообщений?
  4. Что бы произошло, если бы я запустил объект Application в фоновом потоке?

Ответы [ 2 ]

12 голосов
/ 11 июля 2010

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

Реализации цикла сообщений в вашем сценарии

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

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

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

Объект приложения WPF является необязательным

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

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

  • Общий ResourceDictionary
  • Отображение WM_APPACTIVATE сообщения на Activated и Deactivated события
  • Отображение WM_QUERYENDSESSION сообщения в OnSessionEnding событие
  • Управление жизненным циклом (запуск / запуск / выключение / выход)
  • Автоматическое отключение при закрытии последнего или главного окна
  • Значок по умолчанию для окон WPF
  • Запоминание первого открытого окна (MainWindow)
  • Общая регистрация для событий NavigationService (Navigated и т. Д.)
  • 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 не может быть нулевым: если вы обращаетесь к 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?

* +1136 * Номер

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

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 после создания дочернего элемента управления вызывает пустое действие с низким приоритетом, в результате чего все ожидающие операции DispatcherOperation завершаются.

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

3 голосов
/ 11 июля 2010

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...