【问题标题】:Untraceable Exceptions in Windows.Forms Application.Run()Windows.Forms Application.Run() 中无法追踪的异常
【发布时间】:2013-01-05 21:22:55
【问题描述】:

我有一个旧的 Windows.Forms 应用程序正在尝试调试。

有时运行几分钟后会产生 ArithmeticException 或 OverflowException。源代码必须在代码库中的某处,但堆栈跟踪始终指向行 Application.Run(mainForm);

StackTrace 没有用,因为它只显示 Windows.Forms 本地调用:

 bei System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   bei System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
   bei System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   bei System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   bei System.Windows.Forms.Application.Run(Form mainForm)
   bei Program.Main() in C:\xy\Program.cs:Zeile 102.

为了找到异常的来源,我添加了一个异常处理程序 System.Windows.Forms.Application.ThreadExceptionSystem.AppDomain.CurrentDomain.UnhandledException

我尝试过启用和禁用捕获异常 System.Windows.Forms.Application.SetUnhandledExceptionMode();

永远不会调用 ThreadException 事件处理程序。 UnhandledException 事件处理程序只报告我在 Visual Studio 中看到的相同异常。

在 Visual Studio 中,我启用了在引发异常时中断执行: 这没有任何效果。

我该怎么做才能找到有问题的代码行?


编辑:完整的异常细节:


如果我在没有附加任何调试器的情况下启动进程,并在附加调试器之前等待它崩溃,我会收到以下异常:

Unbehandelte Ausnahme bei 0x0c9f9e1b in program.exe: 0xC0000090: Floating-point invalid operation.

调试然后导致这块反汇编

0C9F9E12  add         esi,10h 
0C9F9E15  push        0CA1FD48h 
0C9F9E1A  push        eax  
0C9F9E1B  fmul        qword ptr ds:[0CA202E0h] 
0C9F9E21  fstp        dword ptr [esp+18h] 

我无法解析,但我怀疑这只是 DispatchMessageW 函数

【问题讨论】:

  • 您能否一次单步执行代码以确定发生这种情况的位置?您也可以尝试在整个代码的位置使用Debug 类添加断言,以尝试缩小异常可能出现的位置。
  • 我目前正在尝试单步执行代码,但问题是它是一个使用多个线程的大型代码库,并且仅在一段时间后才会发生异常。
  • 你确定异常没有信息吗?看InnerException
  • 抱歉,我忘记添加该信息了。 InnerException 为空,我将发布完整的异常
  • 您是否修改了 VS 设置以进行调试?取消选中“Unwind the callstack...”(“Aufrufliste für unbehandelte Ausnahmen entladen”——多么具有误导性的翻译)。

标签: c# winforms debugging visual-studio-2008 exception


【解决方案1】:

这里的诊断是你的进程中有遗留的非托管代码,从你发布的调用堆栈判断,这可能是一个旧的 ActiveX 控件。

这些异常是由浮点处理器 FPU 生成的硬件异常。可以将其置于通过引发异常来报告问题的操作模式,例如您看到的 STATUS_FLOAT_OVERFLOW 和 STATUS_FLOAT_INVALID_OPERATION 异常。而不是生成无穷大、NaN 或非规范化。 FMUL指令很容易产生这样的异常。

改变 FPU 操作模式的软件与托管代码根本不兼容。这要求始终屏蔽 FPU 异常。屏蔽这些异常是完全正常的,所有现代软件都会这样做。然而,在上个世纪,这些异常被认为是诊断浮点计算失控的资产。特别是,旧的 Borland 运行时库揭示了这些异常。

好吧,如果您还没有收到该消息,这将是一个相当糟糕的消息。首先要看的是尝试诊断此代码引发浮点异常的原因。不良数据往往是最常见的原因。其次,您真的必须对更改的 FPU 控制寄存器做一些事情,这也很容易导致托管代码失败。特别是 WPF 代码中的一个问题,它喜欢使用 NaN。

使用调试器很容易找到这样的代码。使用 Debug + Windows + Registers 调试器窗口。右键单击窗口并勾选“浮点”选项。 CTRL 寄存器的值至关重要,在托管程序中它应该是027F。单步执行程序,起初粗略,当寄存器更改时,您发现了麻烦制造者。如果是 64 位程序,还要勾选“SSE”,MXCSR 寄存器应该是00001F80

您不能使用托管代码直接重置 FPU 控制寄存器,但您可以使用一个技巧。 CLR 在处理异常时重置它。因此,可能的解决方法是在导致控制寄存器值更改的语句之后故意抛出并捕获异常:

        try {  throw new Exception("Resetting FPU control register, please ignore"); }
        catch { }

在 msvcrt.dll 中调用 _controlfp() 函数是一种更直接的方法。但是,当然,由于两者的副作用,现在该库正在以一种并非设计用于的模式运行,它当然不会期望遇到 Nan 和 Infinity 值。从长远来看,您确实需要考虑淘汰旧组件或库。

【讨论】:

  • 谢谢!这为我节省了几天进一步毫无结果的调试!这似乎确实是问题所在。这个程序中有几个 c/c++ 库和一个 3d 查看器应用程序,其中一个必须负责。我还没有找到问题的确切根源,但我现在可以通过使用double d = 0; d=d/d; 乱扔代码并捕获并记录生成的 ArithmeticException 来追踪它。
  • 感谢您的精彩、易懂的解释。我们在 WPF 中经常遇到这个问题,并在许多 WPF 操作之前修补了对 _fpreset() 的调用,但问题是我们正在使用托管 System.Data.Odbc 类。这些类在后台使用本机 ODBC 库和驱动程序,其中一些似乎破坏了 FPU 状态。似乎托管 ODBC 接口层应该在每次本机 ODBC 操作后重置浮点状态,这样函数调用者就不必担心了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-15
  • 2011-10-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多