首页 文章

VS2010不会在64位版本的Windows上的WinForms应用程序中显示未处理的异常消息

提问于
浏览
75

当我创建一个新项目时,我对未处理的异常会有一个奇怪的行为 . 这就是我可以重现问题的方法:

1)创建一个新的Windows窗体应用程序(C#,. NET Framework 4,VS2010)

2)将以下代码添加到 Form1_Load 处理程序:

int vara = 5, varb = 0;
int varc = vara / varb;
int vard = 7;

我希望VS中断并在第二行显示未处理的异常消息 . 但是,会发生的是第三行只是跳过而没有任何消息,应用程序一直在运行 .

我现有的C#项目没有这个问题 . 所以我想我的新项目是用一些奇怪的默认设置创建的 .

有没有人知道我的项目有什么问题?

我试过检查Debug-> Exceptions中的方框 . 但即使我在 try-catch 块中处理异常,执行也会中断;这也不是我想要的 . 如果我没记错的话,在这个对话框中有一个名为"unhandled exceptions"的列或类似的东西,这会让我感到非常兴奋 . 但在我的项目中只有一列("Thrown") .

5 回答

  • 9

    这是由wow64仿真层引起的一个令人讨厌的问题,它允许32位代码在64位版本的Windows 7上运行 . 它吞噬了响应64位窗口管理器生成的通知而运行的代码中的异常,就像 Load 事件一样 . 防止调试器看到它并踩到 . 这个问题很难解决,微软的Windows和DevDiv小组来回指责 . DevDiv对此无能为力,Windows认为这是正确且有记录的行为,听起来很神秘 .

    这肯定是documented但几乎没有人理解后果或认为这是合理的行为 . 特别是当窗口过程从视图中隐藏时,就像在任何使用包装类来隐藏窗口管道的项目中一样 . 像任何Winforms,WPF或MFC应用程序 . 基本问题是Microsoft无法弄清楚如何将异常从32位代码流回到64位代码,该代码将通知重新发送回尝试处理或调试异常的32位代码 .

    附加调试器只是一个问题,你的代码将像往常一样没有炸弹 .

    项目>属性>构建选项卡>平台目标= AnyCPU并取消选择首选32位 . 您的应用程序现在将作为64位进程运行,从而消除了wow64故障模式 . 一些后果,它会在VS2013之前禁用VS版本的编辑继续,并且当您依赖32位代码时可能并不总是可行 .

    其他可能的解决方法:

    • Debug> Exceptions>勾选CLR异常的Thrown框以强制调试器停止在抛出异常的代码行 .

    • Load 事件处理程序中写入try / catch,在catch块中写入failfast .

    • Main() 方法中使用 Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException) ,以便在调试模式下不禁用消息循环中的异常陷阱 . 然而,这使得所有未处理的异常难以调试, ThreadException 事件非常无用 .

    • 考虑您的代码是否真的属于 Load 事件处理程序 . 它很少需要它,但它在VB.NET和swan歌曲中非常流行,因为它是默认事件,双击简单地添加了事件处理程序 . 在应用用户首选项和自动缩放后,如果您对实际窗口大小感兴趣,则只需要 Load . 其他所有内容都属于构造函数 .

    • 更新到Windows 8或更高版本,他们解决了这个问题 .

  • 120

    根据我的经验,当我运行附带的调试器时,我只会看到这个问题 . 独立运行时,应用程序的行为相同:不会吞下异常 .

    随着KB976038的引入,你可以使这个工作'd expect again. I never installed the hotfix, so I' m假设它是作为Win7 SP1的一部分 .

    这篇文章中提到了这一点:

    这是一些将启用此修补程序的代码:

    public static class Kernel32
    {
        public const uint PROCESS_CALLBACK_FILTER_ENABLED = 0x1;
    
        [DllImport("Kernel32.dll")]
        public static extern bool SetProcessUserModeExceptionPolicy(UInt32 dwFlags);
    
        [DllImport("Kernel32.dll")]
        public static extern bool GetProcessUserModeExceptionPolicy(out UInt32 lpFlags);
    
    
        public static void DisableUMCallbackFilter() {
            uint flags;
            GetProcessUserModeExceptionPolicy(out flags);
    
            flags &= ~PROCESS_CALLBACK_FILTER_ENABLED;
            SetProcessUserModeExceptionPolicy(flags);
        }
    }
    

    在应用程序开头调用它:

    [STAThread]
        static void Main()
        {
            Kernel32.DisableUMCallbackFilter();
    
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    

    我已经确认(使用下面显示的简单示例),这正如您所期望的那样有效 .

    protected override void OnLoad(EventArgs e) {
        throw new Exception("BOOM");   // This will now get caught.
    }
    

    所以,我不明白,为什么调试器以前不可能处理交叉内核模式堆栈帧,但是使用这个修补程序,他们以某种方式弄明白了 .

  • 0

    正如汉斯提到的那样,编译应用程序并在没有附加调试器的情况下运行exe .

    对我来说问题是更改BindingSource控件绑定的Class属性名称 . 在没有IDE的情况下运行我能够看到错误:

    无法绑定到DataSource上的属性或列SendWithoutProofReading . 参数名称:dataMember

    修复BindingSource控件以绑定到更新的属性名称解决了问题:
    enter image description here

  • 3

    我正在使用WPF并遇到了同样的问题 . 我已经尝试了Hans 1-3的建议,但不喜欢它们,因为工作室不会停在错误的位置(所以我无法查看我的变量,看看问题是什么) .

    所以我尝试了Hans的第四个建议 . 我很惊讶我的代码有多少可以移动到MainWindow构造函数而没有任何问题 . 不知道为什么我养成了在Load事件中加入这么多逻辑的习惯,但显然很多都可以在ctor中完成 .

    但是,这与1-3的问题相同 . 在WPF的ctor期间发生的错误被包装到一般的Xaml异常中 . (一个内部异常有真正的错误,但我再次希望工作室能够突破实际的故障点) .

    最终为我工作的是创建一个线程,睡眠50毫秒,发送回主线程并做我需要的...

    void Window_Loaded(object sender, RoutedEventArgs e)
        {
            new Thread(() =>
            {
                Thread.Sleep(50);
                CrossThread(() => { OnWindowLoaded(); });
            }).Start();
        }
        void CrossThread(Action a)
        {
            this.Dispatcher.BeginInvoke(a);
        }
        void OnWindowLoaded()
        {
            ...do my thing...
    

    这样工作室就会在未捕获的异常发生时突破 .

  • 1

    一个简单的解决方法是,如果你可以将你的初始化代码移动到另一个事件,如 Form_Shown ,后面调用 Form_Load ,并使用一个标志来运行启动代码,如下所示:

    bool firstLoad = true; //flag to detect first form_shown
    
    private void Form1_Load(object sender, EventArgs e)
    {
        //firstLoad = true;
        //dowork(); //not execute initialization code here (postpone it to form_shown)
    }
    
    private void Form1_Shown(object sender, EventArgs e)
    {
        if (firstLoad) //simulate Form-Load
        {
            firstLoad = false;
    
            dowork();
        }
    }
    
    void dowork()
    {
        var f = File.OpenRead(@"D:\NoSuchFile756.123"); //this cause an exception!
    
    }
    

相关问题