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位版本的使用