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

VS2010 does not show unhandled exception message in a WinForms Application on a 64-bit version of Windows

提问人:Robert Hegner 提问时间:2/8/2011 最后编辑:participantRobert Hegner 更新时间:4/25/2020 访问量:24998

问:

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

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

C# visual-studio-2010 异常

评论

0赞 Pedro77 9/28/2011
同样的问题在这里!表单负载已经在内部捕获了 excpetions..

答:

126赞 Hans Passant 2/8/2011 #1

这是一个令人讨厌的问题,由 wow64 仿真层引起,它允许 32 位代码在 64 位版本的 Windows 7 上运行。它吞噬为响应 64 位窗口管理器生成的通知而运行的代码中的异常,例如事件。阻止调试器看到它并单步执行。这个问题很难解决,Microsoft的Windows和DevDiv小组来回指责。DevDiv 对此无能为力,Windows 认为这是正确且有据可查的行为,听起来很神秘。Load

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

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

“项目>属性”>“生成”选项卡>“平台目标 = AnyCPU”,然后取消勾选“首选 32 位”。您的应用现在将作为 64 位进程运行,从而消除了 wow64 失败模式。某些后果是,它会禁用 VS2013 之前的 VS 版本的“编辑 + 继续”,并且当您依赖于 32 位代码时,可能并不总是可行的。

其他可能的解决方法:

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

评论

6赞 Hans Passant 2/8/2011
是的,有一篇关于它的 Connect 文章。很多。这个可能是最好的:connect.microsoft.com/VisualStudio/feedback/details/357311/......
5赞 Hans Passant 2/8/2011
这里有一个表明他们没有太多线索发生了什么:connect.microsoft.com/VisualStudio/feedback/details/589858/......
1赞 ferpega 9/17/2011
我已经检查了 Winforms 应用程序上存在的 OnFormClosed 事件中存在相同的错误。而且我也无法解决将应用程序目标更改为 x86 的问题。但至少我有一个问题的解释。
1赞 tanascius 6/18/2012
另一种解决方法似乎是.设置它似乎有帮助 - 异常在 IDE 中和调试器中触发。Application.ThreadException
7赞 ThunderGr 10/1/2013
Windows 8 确实有这个问题。仅供参考。
10赞 Jonathon Reinhart 8/17/2012 #2

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

随着 KB976038 的引入,您可以再次按照您的期望进行这项工作。我从未安装过此修补程序,因此我假设它是 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赞 Martin 5/2/2013
Set/GetProcessUserModeExceptionPolicy仍未记录在 MSDN 上,并且 Windows 8 的Kernel32.dll不会导出它们。
0赞 ThunderGr 10/1/2013
难道没有办法通过编辑注册表或其他东西来做同样的事情吗?
1赞 ThunderGr 10/1/2013
我已经读过了。我还阅读了 windows 8 内核不导出这些方法的评论。我碰巧在 Windows 8 上运行。我只是问,以防万一。
1赞 ThunderGr 10/1/2013
如果您应用您指向的修补程序,则可以通过编辑注册表来解决问题,如他们所描述的那样,而不是使用方法。我记得以前在 Win7 机器上以这种方式解决了这个问题。不幸的是,您无法在 Win 8 中执行此操作。
1赞 Jonathon Reinhart 10/1/2013
@ThunderGr 谢谢你指出这一点。我想我不应该在睡前发表评论:-)
1赞 Gabe Halsmer 2/28/2013 #3

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

所以我尝试了汉斯的第四个建议。令我惊讶的是,我的代码中有多少可以毫无问题地移动到 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...

这样,工作室将在发生未捕获的异常时中断。

3赞 Jeremy Thompson 3/26/2015 #4

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

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

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

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

0赞 S.Serpooshan 1/7/2018 #5

一个简单的解决方法是,您可以将初始化代码移动到另一个事件,例如 which 调用晚于 ,并使用标志在所示的第一个形式运行启动代码:Form_ShownForm_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!

}