WPF ShowDialog сглаживает исключения при загрузке окна
Диалоговое окно окна WPF отображается с помощью метода ShowDialog в классе Window, например, когда кнопка нажата в главном окне, например.
private void button1_Click(object sender, RoutedEventArgs e)
{
try
{
var window = new Window1();
window.ShowDialog();
}
catch (ApplicationException ex)
{
MessageBox.Show("I am not shown.");
}
}
В окне есть событие Loaded, подписанное в xaml, например:
<Window x:Class="Stackoverflow.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Loaded="Window_Loaded">
<Grid />
</Window>
Исключение возникает в событии Window_Loaded
private void Window_Loaded(object sender, RoutedEventArgs e)
{
throw new ApplicationException();
}
Однако исключение не улавливается уловом во время вызова ShowDialog, и вызов не возвращается. Исключение проглатывается, и окно все еще отображается.
Почему это происходит и как я могу обработать исключение в событии Window_Loaded окна WPF? Должен ли я его поймать в обработчике событий и вручную удалить окно?
В WinForms вам нужно позвонить Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException)
чтобы исключить пузырьки через вызовы ShowDialog. Есть ли аналогичный переключатель, который должен быть установлен в WPF?
Ответы
Ответ 1
Я только видел эту проблему на машинах x64, с кодом, скомпилированным с Any Cpu.
Изменение вашей программы для компиляции, поскольку x84 может ее исправить, но у меня были проблемы с самим собой в зависимости от наших сборок.
Мое единственное предложение кода следующее, и даже тогда он не гарантирует его забрать.
Поймать исключение и повторно бросить его в фоновом работнике.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
/// your code here...
throw new ApplicationException();
/// your code here...
}
catch (Exception ex)
{
if (IntPtr.Size == 8) // 64bit machines are unable to properly throw the errors during a Page_Loaded event.
{
BackgroundWorker loaderExceptionWorker = new BackgroundWorker();
loaderExceptionWorker.DoWork += ((exceptionWorkerSender, runWorkerCompletedEventArgs) => { runWorkerCompletedEventArgs.Result = runWorkerCompletedEventArgs.Argument; });
loaderExceptionWorker.RunWorkerCompleted += ((exceptionWorkerSender, runWorkerCompletedEventArgs) => { throw (Exception)runWorkerCompletedEventArgs.Result; });
loaderExceptionWorker.RunWorkerAsync(ex);
}
else
throw;
}
}
Ответ 2
Здесь объясняется "Почему": http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/
Короче говоря, исключение не может быть распространено в 64-разрядных операционных системах, потому что существует переход между режимом пользователя и ядра.
Тест IntPtr.Size в ответе @midspace не является адекватным, потому что IntPtr.Size будет равен 4 в процессе x86, работающем на x64 os (вместо этого нужно использовать Environment.Is64BitOperatingSystem
вместо .NET 4 и выше).
Теперь решение: используйте другое событие, например ContentRendered
, которое вызывается после Loaded
, или помещает ваш код в конструктор окна.
Никогда не используйте Loaded
(или OnLoad
в Winforms), потому что если там есть исключение, вы не знаете, что может произойти.
Посмотрите также на этот ответ: fooobar.com/questions/2314/...
Ответ 3
Я также реконструировал ваш ответ в Visual Studio 2010 в пустом проекте WPF 3.5.
Проект вел себя так, как ожидалось, т.е. Window_Loaded выдал исключение, и оно попало в событие нажатия кнопки.
Итак, я не уверен, почему ваш не работает, возможно, попробуйте отправить свой App.xml.cs и любой другой код, который вы здесь не показывали?
В то же время я подумал, что хочу указать несколько вещей:
WPF действительно обрабатывает исключения "uncaught" несколько иначе, чем WinForms. Попробуйте взглянуть на это для стартера:
http://msdn2.microsoft.com/en-us/library/system.windows.application.dispatcherunhandledexception.aspx
Похоже, что нет никакого точного эквивалента в WPF для метода SetUnhandledExceptionMode (см. http://social.msdn.microsoft.com/forums/en-US/wpf/thread/955c75f8-9cd9-4158-bed9-544bd7946413). Попробуйте их совет о регистрации обработчика и посмотрите, поможет ли это вам?
Я бы рекомендовал пройти через ваш код - установить точку останова в Window_Loaded и посмотреть, что происходит - обратить внимание на стек вызовов.
Удачи!
Ответ 4
Изучив это немного, я нашел эту необычную запись в блоге, описывающую аналогичную проблему.
http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/
Как оказалось, это может быть проблема 64-битной архитектуры процессора. Кто бы догадался?! Это может объяснить, почему некоторые люди не могли воспроизвести проблему на моем простом примере. Я попытался скомпилировать свой пример в "Any CPU", x64 и x86, но безрезультатно. На x64 все это фактически полностью взорвалось с помощью диалогового окна с аварийным завершением Windows.
Итак, я думаю, что это ответ, не проверив его на 32-битной машине.
Ответ 5
У меня была схожая проблема, и пытаться понять, куда она идет, расстраивает. Там есть пара вещей, которые могут вызвать проблемы.
- Вы вызываете что-либо перед вызовом метода InitializeComponent();? Раньше у меня был метод, вызывающий элементы xaml, которые еще не были инициализированы.
- Вы пытались нажать F10, чтобы запустить приложение, используя Step Over, вы увидите, что процесс начинается?
- Вы проверили свое имя? XAML может проглатывать ошибки, такие как методы, которые неправильно написаны, а затем исключение запускается во время выполнения, это особенно настоящие поставщики DataSet. Вы будете удивлены тем, с чем вы можете избавиться.
- Захват исключения для открытия формы довольно фундаментальный, я бы лучше посмотрел на любых зависимых лиц (привязки данных или XAML) и справился с ними в первую очередь.
Надеюсь, что эти точки помогут.......