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

Posted

技术标签:

【中文标题】VS2010 在 64 位版本的 Windows 上的 WinForms 应用程序中不显示未处理的异常消息【英文标题】:VS2010 does not show unhandled exception message in a WinForms Application on a 64-bit version of Windows 【发布时间】:2011-06-23 11:13:14 【问题描述】:

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

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 块中处理异常,执行也会中断;这也不是我想要的。如果我没记错的话,在这个对话框中有一个名为“未处理的异常”的列或类似的东西,它可以做我想要的。但在我的项目中,只有一列(“Thrown”)。

【问题讨论】:

同样的问题!表单加载已经在内部捕获了异常...... 【参考方案1】:

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

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

这只是附加调试器的问题,如果没有调试器,您的代码将像往常一样被炸毁。

Project > Properties > Build tab > Platform target = AnyCPU 并取消 Prefer 32-bit。您的应用程序现在将作为 64 位进程运行,消除了 wow64 故障模式。一些后果,它会禁用 VS2013 之前的 VS 版本的 Edit + Continue,并且当您依赖 32 位代码时可能并不总是可行。

其他可能的解决方法:

调试 > 异常 > 勾选 CLR 异常的抛出框以强制调试器在引发异常的代码行处停止。 在 Load 事件处理程序中写入 try/catch 并在 catch 块中进行故障快速处理。 在Main() 方法中使用Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException),以便在调试模式下不会禁用消息循环中的异常陷阱。然而,这使得所有未处理的异常都难以调试,ThreadException 事件毫无用处。 考虑您的代码是否真的属于Load 事件处理程序。很少需要它,但它在 VB.NET 和绝唱中非常流行,因为它是默认事件,双击即可轻松添加事件处理程序。只有当您在应用用户首选项和自动缩放后对实际窗口大小感兴趣时,您才真正需要Load。其他一切都属于构造函数。 更新到 Windows 8 或更高版本,他们已经解决了这个 wow64 问题。

【讨论】:

是的,有一篇关于它的 Connect 文章。其中很多。这个可能是最好的:connect.microsoft.com/VisualStudio/feedback/details/357311/… 这表明他们对发生的事情一无所知:connect.microsoft.com/VisualStudio/feedback/details/589858/… 我在 Winforms 应用程序的 OnFormClosed 事件中检查了同样的错误。而且我也无法解决将应用程序目标更改为 x86 的问题。但至少我有一个问题解释。 另一种解决方法似乎是Application.ThreadException。设置它似乎有帮助 - 异常是在 IDE 和调试器中触发的。 Windows 8 确实有这个问题。仅供参考。【参考方案2】:

根据我的经验,我只在连接调试器运行时才会看到此问题。应用程序在独立运行时的行为相同:异常不会被吞没。

随着KB976038 的引入,您可以再次按预期工作。我从未安装过修补程序,所以我假设它是作为 Win7 SP1 的一部分提供的。

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

The case of the disappearing OnLoad exception – user-mode callback exceptions in x64

这里有一些代码可以启用修补程序:

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.

所以,我不明白的是,为什么调试器以前无法处理跨内核模式堆栈帧,但有了这个修补程序,他们不知何故弄明白了。

【讨论】:

Set/GetProcessUserModeExceptionPolicy 在 MSDN 上仍然没有记录,Windows 8 的 Kernel32.dll 不会导出它们。 难道没有办法通过编辑注册表或其他方式来做同样的事情吗? 我已经阅读了它们。我还阅读了 Windows 8 内核不导出这些方法的评论。我碰巧在 Windows 8 上运行。我只是问一下,以防万一。 如果您应用您指向的修补程序,您可以按照他们的描述通过编辑注册表来解决问题,而不是使用这些方法。我记得以前用这种方法在 Win7 机器上解决过这个问题。不幸的是,你不能在 Win 8 中这样做。 @ThunderGr 感谢您指出这一点。我想我不应该在睡前发表评论:-)【参考方案3】:

正如 Hans 所说,编译应用程序并在不附加调试器的情况下运行 exe。

对我来说,问题在于更改绑定源控件绑定到的类属性名称。在没有 IDE 的情况下运行我能够看到错误:

无法绑定到 SendWithoutProofReading 上的属性或列 数据源。参数名称:dataMember

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

【讨论】:

【参考方案4】:

我正在使用 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...

这样一来,工作室就会在发生未捕获异常的地方中断。

【讨论】:

【参考方案5】:

一个简单的解决方法是,您可以将初始化代码移动到另一个事件,例如 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!


【讨论】:

以上是关于VS2010 在 64 位版本的 Windows 上的 WinForms 应用程序中不显示未处理的异常消息的主要内容,如果未能解决你的问题,请参考以下文章

USGS-EROS项目espa-surface-reflectance中的LaSRC Version 1.3.0模块利用vs2010编译出windows64位版本

USGS-EROS项目espa-surface-reflectance中的LaSRC Version 1.3.0模块利用vs2010编译出windows64位版本的使用

QT用VS还是MINGW编译好,有啥区别

在 64 位机器上注册 C++ DLL

vs2010中配置管理器win32和x64的区别

如何用pcl配置visual studio 2010