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


77

创建新项目时,未处理的异常会产生奇怪的行为。这是我可以重现此问题的方法:

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

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

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

我希望VS中断并在第二行显示未处理的异常消息。但是,发生的情况是,仅跳过了第三行而没有任何消息,并且应用程序继续运行。

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

有谁知道我的项目有什么问题吗???

我尝试选中“调试”->“异常”中的复选框。但是,即使我在一个try-catch块中处理异常,执行也会中断;这也不是我想要的。如果我没记错的话,此对话框中有一列“未处理的异常”或类似的内容,它们可以满足我的要求。但是在我的项目中,只有一列(“ Thrown”)。


同样的问题在这里!表负荷已经抓住excpetions内部..
Pedro77

Answers:


124

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

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

连接调试器只是一个问题,您的代码将像往常一样炸毁而没有一个。

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

其他可能的解决方法:

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

6
是的,有一篇有关Connect的文章。其中很多。这一次可能是最好的:connect.microsoft.com/VisualStudio/feedback/details/357311/...
汉斯帕桑特

5
而这里的一个建议,他们没有太多的线索是怎么回事什么:connect.microsoft.com/VisualStudio/feedback/details/589858/...
汉斯帕桑特

1
我已经检查了WinForms应用程序的OnFormClosed事件是否存在相同的错误。我也无法解决将应用程序目标也更改为x86的问题。但是至少我有一个问题解释。
ferpega 2011年

1
似乎另一种解决方法Application.ThreadException。设置它似乎有帮助-异常是在IDE中以及由调试器触发的。
tanascius 2012年

7
Windows 8确实有此问题。仅供参考。
ThunderGr

10

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

通过引入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.
}

所以,我不明白的是,为什么调试器以前不可能处理交叉的内核模式堆栈帧,但是有了此修补程序,他们以某种方式弄清楚了。


Set/GetProcessUserModeExceptionPolicy仍未在MSDN上记录,并且Windows 8的Kernel32.dll不会导出它们。
马丁

是否没有办法通过编辑注册表或其他方法来执行相同的操作?
ThunderGr

1
我看过了 我还阅读了Windows 8内核不导出这些方法的评论。我碰巧在Windows 8上运行。我只是在问,以防万一。
ThunderGr

1
如果应用了指向的修补程序,则可以通过编辑注册表来解决问题,如它们所描述的那样,而不使用方法。我记得以前曾经在Win7机器上解决过这个问题。不幸的是,您无法在Win 8中做到这一点
。– ThunderGr

1
@ThunderGr感谢您指出这一点。我想我不应该在睡前发表评论:-)
Jonathon Reinhart

3

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

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

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

修复BindingSource控件以绑定到更新的属性名称可以解决此问题: 在此处输入图片说明


1

我正在使用WPF并遇到同样的问题。我已经尝试过Hans 1-3的建议,但不喜欢它们,因为studio不会停在错误所在的位置(因此我无法查看变量并查看问题出在哪里)。

所以我尝试了汉斯的第四条建议。令我感到惊讶的是,我有多少代码可以毫无问题地移到MainWindow构造函数中。不知道为什么我习惯在Load事件中放置这么多的逻辑,但是显然很多事情可以在ctor中完成。

然而,这具有与1-3相同的问题。WPF的ctor期间发生的错误被包装到通用Xaml异常中。(内部异常有真正的错误,但我又想让Studio在实际的故障点中断)。

最终对我有用的是创建一个线程,休眠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...

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


0

一个简单的解决方法是,您可以将init代码移至另一个事件(如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!

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.