4

当我在事件处理程序中抛出异常时,不调用异常处理程序?

一个精简示例的示例代码开始:

应用程序.xaml

<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml"
             DispatcherUnhandledException="App_DispatcherUnhandledException" >
    <Application.Resources/>
</Application>

应用程序.xaml.cs

using System.Windows;
using System.Windows.Threading;

namespace WpfApplication1
{
    public partial class App : Application
    {
        //This method is called when ButtonA is clicked, but not when ButtonB is
        //clicked (and a (random) file is selected ofcourse).
        void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            MessageBox.Show(e.Exception.Message, "An exception occurred", MessageBoxButton.OK, MessageBoxImage.Error);
            e.Handled = true;
        }
    }
}

主窗口.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Content="Button A" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Click="ButtonA_Click"/>
        <Button Content="Button B" HorizontalAlignment="Left" Margin="90,10,0,0" VerticalAlignment="Top" Width="75" Click="ButtonB_Click"/>
    </Grid>
</Window>

主窗口.xaml.cs

using Microsoft.Win32;
using System;
using System.Windows;

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void ButtonA_Click(object sender, RoutedEventArgs e)
        {
            throw new Exception("Works!");
        }

        private void ButtonB_Click(object sender, RoutedEventArgs e)
        {
            var ofd = new OpenFileDialog();
            ofd.FileOk += (s, ce) => {
                throw new Exception("Does not work!?!?");
            };
            ofd.ShowDialog();
        }
    }
}

我已经看过这个问题这个问题,但是强制使用 32 位或 64 位甚至“任何 CPU”都不起作用。此外,当设置这四个(!)处理程序中的任何一个时,当在事件中引发异常时,它们都不会被调用。这篇文章也没有帮助。

我正在运行 VS2012(在 Win8、x64 上),该项目使用的是 .Net Framework 4.5)。我错过了什么?我要疯了吗?

为了清楚起见:我希望显示一个消息框(当我单击时它会显示ButtonA),或者实际上App_DispatcherUnhandledException是调用的方法。但是当我单击时,该方法没有被调用(因此没有显示消息框)ButtonBButtonA和之间的唯一区别ButtonB是“ A”中的异常不在事件处理程序中,而“ B”中的异常在。而且,当然,我确实在其中选择了一个文件,OpenFileDialog然后单击“打开”将其选中。调试器启动并指出“不工作!?!? ”异常被抛出,然后我继续执行并且没有显示消息框。

另外:我对 WPF 很陌生,这可能是问题的一部分:P

编辑 1

作为参考,这里有两个 zipfile 演示了确切的问题:

  1. 简单(10Kb)
  2. 扩展(10Kb)

在我的计算机上,对于上述两个项目,ButtonA 会显示一个消息框,而 ButtonB(选择文件后)则不会。曾经。甚至没有打开“调试非托管代码”。

编辑 2

因此,我在另一台机器上运行了相同的代码并发现了这一点: 在另一台机器上,调试器显示如下:

其他机器上的异常

注意Exception crossed a native/managed boundary标题。当我尝试恢复执行(继续)时,异常不断弹出。我的机器,当调试器启动时,显示:

我的机器上的异常

...然后,当我恢复执行时,异常在某种黑洞中消失了;主窗体再次显示,没有任何反应。

这应该与此设置有关:

越界异常设置

但是,即使重新启动 VS2012 并删除临时文件(以及项目中的 bin/obj 目录)、恢复默认值等,打开/关闭此选项也无济于事。

所以......我现在知道这个例外确实与托管和非托管之间的跨界有关。现在我只需要弄清楚如何解决这个问题,这样我就可以在FileOk事件中抛出异常(这样,最终我的组件也可以在那里抛出)。

4

2 回答 2

5

好的,所以我解决了我的问题。

谷歌搜索,冲浪等等。我最终在这里这里结束了。我现在很清楚 FileOk 事件是如何在另一个调度程序上处理的,因此解决方案很简单:

private void ButtonB_Click(object sender, RoutedEventArgs e)
{
    var ofd = new OpenFileDialog();
    ofd.FileOk += (s, ce) => {
        this.Dispatcher.BeginInvoke((Action)(() =>
        {
            //We can throw:
            throw new Exception("Yay! This exception is now caught by the UnhandledException handler!");

            //or, alternatively, our component can do work that possibly throws:
            Component.DoFoo();
        }));
    };
    ofd.ShowDialog();
}

这可确保将异常传递给正确的调度程序并在那里进行处理。然后正确调用该App_DispatcherUnhandledException方法,我们可以从那里获取它。

于 2013-10-03T10:49:29.187 回答
-1

If you wire it up like this you will see that it is being handled
Still not sure why it eludes App_DispatcherUnhandledException

using System.ComponentModel;
//using Microsoft.Win32;


namespace UncaughtExceptionHandler
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            //AppDomain currentDomain = AppDomain.CurrentDomain;
            //currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
            InitializeComponent();
        }
        private void ButtonA_Click(object sender, RoutedEventArgs e)
        {
            throw new Exception("WTFA!?!?");
        }
        private void ButtonB_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
            ofd.FileOk += MyCanelEventHandler;
            //ofd.FileOk += (s, ce) =>
            //{
            //    //MessageBox.Show("Throwikng Exception WTF2!?!?");
            //    throw new Exception("WTF2!?!?");
            //};
            ofd.ShowDialog();
        }
        static void MyCanelEventHandler(Object sender, CancelEventArgs e)
        {
            MessageBox.Show("MyCanelEventHandler");
            throw new Exception("WTFCEH!?!?");
        }
    }
}

Debug 101 is get a call stack.
I got a call stack in 10 minutes.

OP made three invalid assumptions and even after a long discussion does not understand

  1. Exception was being swallowed
    It was not being swallowed it was just getting past his uncaught exception handler My code demonstrated that but OP did not follow
  2. Solution has no unmanned code
    Wrong again OpenFileDialog is unmanaged code
  3. Exception was being thrown from managed code
    Wrong again
    The exception was being thrown from the callback from unmanaged code

First line in the callback
Degbug 101 is get a call stack

UncaughtExceptionHandler.exe!UncaughtExceptionHandler.MainWindow.MyCanelEventHandler(object sender, System.ComponentModel.CancelEventArgs e) Line 55 C# comdlg32.dll!CFileOpenSave::_NotifyFileOkChangeCallback() + 0x18 bytes comctl32.dll!_DPA_EnumCallback@12() + 0x20 bytes
comdlg32.dll!CFileOpenSave::_NotifyFileOk() + 0x3d bytes
comdlg32.dll!CFileOpenSave::_CleanupDialog() + 0x46c2 bytes
comdlg32.dll!CFileOpenSave::_HandleOkAndClose() + 0x3a bytes
comdlg32.dll!CFileOpenSave::_OnCommandMessage() + 0xf432 bytes comdlg32.dll!CFileOpenSave::s_OpenSaveDlgProc() + 0x1f42 bytes user32.dll!_InternalCallWinProc@20() + 0x23 bytes
user32.dll!_UserCallDlgProcCheckWow@32() + 0xa9 bytes
user32.dll!_DefDlgProcWorker@20() + 0x7f bytes user32.dll!_DefDlgProcW@16() + 0x22 bytes

于 2013-10-02T17:26:00.973 回答