【问题标题】:WPF ShowDialog swallowing exceptions during window loadWPF ShowDialog在窗口加载期间吞咽异常
【发布时间】:2011-06-15 23:32:10
【问题描述】:

使用 Window 类中的 ShowDialog 方法显示 WPF 窗口对话框,就像在主窗口上按下按钮时一样。

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var window = new Window1();
                window.ShowDialog();
            }
            catch (ApplicationException ex)
            {
                MessageBox.Show("I am not shown.");
            }
        }

窗口在 xaml 中订阅了一个 Loaded 事件,如下所示:

<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 调用周围的 catch 没有捕获到异常,调用也没有返回。异常被吞下,窗口仍然显示。

为什么会发生这种情况,我将如何处理 WPF 窗口的 Window_Loaded 事件中的异常?我必须在事件处理程序中捕获它并手动处理窗口吗?

在 WinForms 中你需要调用Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException)

为了让异常通过 ShowDialog 调用冒泡。是否有类似的开关需要在 WPF 上设置?

【问题讨论】:

  • 我试图重现您的情况,但没有成功。以通常的方式捕获异常。我想,您已经简化了代码,但似乎重点在于细节。提供他们,我会尽力提供帮助。
  • 感谢您尝试复制。我在一个非常简单的例子中重现了这个问题,我从这里获取了发布的源代码。我正在使用 VS2010。我将编辑我的问题并添加一些相关信息以进行复制。

标签: wpf exception


【解决方案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;
    }
}

【讨论】:

  • 哇,它成功了……这真的很奇怪也很可怕。我想我现在不想了解更多。 :) 我认为这是 WPF 设计。非常感谢!
  • 哦,旁注不要像这样“throw ex;”再次抛出异常- 最好重新抛出。就像这个“扔;”。
  • 我已经删除了 throw "ex"。它的存在比其他任何东西都更清晰。不过,我们遇到过这样一种情况,即 IntPtr.Size 在 x64 机器上返回 4,并且在通过另一个程序集的反射加载 Window 时异常仍然被吞没,所以我不会说这是一个 100% 的解决方案。但是,删除 If 语句可以解决此问题。
  • 是的,我不会使用 IntPtr.Size == 8 表达式来检测 64 位。最后,我实际上最终执行了所有可能在显示窗口之前抛出的代码。这还具有窗口不闪烁的优点。不过,您的回答仍然是对原始问题的解释。
【解决方案2】:

这里解释了“为什么”:http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/

简而言之,该异常无法在 64 位操作系统中传播,因为在用户模式和内核模式之间存在转换。

@midspace 答案中的 IntPtr.Size 测试不充分,因为 IntPtr.Size 在 x64 操作系统上运行的 x86 进程中将等于 4(您需要在 .NET 4 及更高版本上使用 Environment.Is64BitOperatingSystem 而不是) .

现在的解决方案:使用另一个事件,例如 ContentRendered,它在 Loaded 之后调用,或者将您的代码放在窗口构造函数中。

永远不要使用Loaded(或Winforms中的OnLoad),因为如果那里有异常,你不知道会发生什么。

也看看这个答案:https://stackoverflow.com/a/4934010/200443

【讨论】:

    【解决方案3】:

    我还在 Visual Studio 2010 中的一个空白 WPF 3.5 项目中重建了您的答案。

    项目表现如预期,即 Window_Loaded 抛出异常,并被按钮单击事件捕获。

    所以我不确定为什么你的不工作,也许尝试发布你的 App.xml.cs 和你没有在这里显示的任何其他代码?

    与此同时,我想我会指出一些事情:

    WPF 确实处理“未捕获”的异常与 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 位处理器架构问题。谁会猜到?!它可以解释为什么有些人无法重现我的简单示例的问题。我试图在“任何 CPU”、x64 和 x86 中编译我的示例,但无济于事。在 x64 上,整个事情实际上完全崩溃了,并出现了一个 Windows 崩溃对话框。

      所以我猜这是没有在 32 位机器上验证的答案。

      【讨论】:

        【解决方案5】:

        我也遇到过类似的问题,试图了解它的来龙去脉令人沮丧。有几件事可能会导致问题。

        • 在方法调用 InitializeComponent(); 之前你有没有调用任何东西? ?以前我有一个调用 xaml 元素的方法,它还没有被初始化。
        • 您是否尝试过按 F10 启动应用程序,使用 Step Over 可以让您看到确切的启动过程?
        • 您检查过您的命名吗? XAML 可以吞下诸如方法拼写错误之类的错误,然后在运行时引发异常,这尤其适用于 DataSet 提供程序。你会惊讶于你能侥幸逃脱。
        • 捕获打开表单的异常是非常基本的,我宁愿查看任何依赖项(数据或 XAML 绑定)并首先处理它们。

        希望这些观点有所帮助.......

        【讨论】:

        • * 除问题中所述外,未显示其他方法。 InitializeComponents 在两个窗口的构造函数中都是单独的。
        • * 好点。我想一种方法是在显示对话框之前运行所有可能抛出的代码。
        猜你喜欢
        • 2016-01-21
        • 2013-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-26
        • 2014-08-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多