Windows.Forms Application.Run() 中无法追踪的异常 [英] Untraceable Exceptions in Windows.Forms Application.Run()

查看:28
本文介绍了Windows.Forms Application.Run() 中无法追踪的异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an old Windows.Forms Application that I am trying to debug.

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

Sometimes after running a few minutes it will produce an ArithmeticException or an OverflowException. The source must be somewhere in the codebase, but the stacktrace always points to the line Application.Run(mainForm);

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

The StackTrace is useless as it only shows Windows.Forms native calls:

 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:xyProgram.cs:Zeile 102.

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

To find the source of the exception I have added an exception handler to System.Windows.Forms.Application.ThreadException and to System.AppDomain.CurrentDomain.UnhandledException.

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

I have tried enabling and disabling catching exceptions with System.Windows.Forms.Application.SetUnhandledExceptionMode();

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

The ThreadException event handler is never called. The UnhandledException event handler just reports the same exception I see in Visual Studio.

在 Visual Studio 中,我启用了在引发异常时中断执行:这没有任何影响.

In Visual Studio I have enabled breaking execution when an exception is thrown: This had no effect whatsoever.

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

What can I do to find the offending line of code?

完整的异常详细信息:

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

If I start the process without any debugger attached, and wait for it to crash before attaching a debugger, I get the following exception:

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

调试然后导致这块反汇编

Debugging then leads to this piece of disassembly

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

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

I cannot parse this, but I suspect this is merely the DispatchMessageW function

推荐答案

这里的诊断是,您的进程中有遗留的非托管代码,从您发布的调用堆栈来看,这可能是旧的 ActiveX 控件.

The diagnostic here is that you have legacy unmanaged code in your process, judging from the call stack you posted that's likely to be an old ActiveX control.

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

These exceptions are hardware exceptions generated by the FPU, the floating point processor. Which can be put in an operation mode where it reports problems by raising exceptions, like the STATUS_FLOAT_OVERFLOW and STATUS_FLOAT_INVALID_OPERATION exceptions that you are seeing. Instead of generating infinity, NaN or denormals. The FMUL instruction can easily generate such an exception.

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

Software that changes the FPU operation mode is pretty fundamentally incompatible with managed code. Which requires that FPU exceptions are always masked. Masking these exceptions is entirely normal and what is done with all modern software. Back in the previous century these exceptions were however considered an asset to diagnose floating point calculations going haywire. In particular, old Borland runtime libraries unmasked these exceptions.

好吧,如果您还没有收到该消息,这完全是个坏消息.首先要看的是尝试诊断为什么这段代码会抛出浮点异常.错误的数据往往是最常见的原因.其次,您真的必须对正在更改的 FPU 控制寄存器做一些事情,这也很容易导致托管代码失败.特别是 WPF 代码中的一个问题,它喜欢使用 NaN.

Well, this is all rather bad news in case you didn't get that message yet. First place to look is to try to diagnose why this code is throwing floating point exceptions. Bad data tends to be the most common reason. Secondly, you really have to do something about the FPU control register being changed, this can easily cause managed code to fail as well. Particularly a problem in WPF code, it likes using NaN.

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

Finding such code is pretty easy with the debugger. Use the Debug + Windows + Registers debugger window. Right-click the window and tick the "Floating point" option. The value of the CTRL register is crucial, it should be 027F in a managed program. Step through the program, coarse at first, you found the trouble-maker when the register changes. If it is 64-bit program then also tick "SSE", the MXCSR register should be 00001F80.

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

You cannot directly reset the FPU control register with managed code but you can use a trick. The CLR resets it whenever it handles an exception. So a possible fix is to intentionally throw and catch an exception, after the statement that caused the control register value to change:

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

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

Pinvoking the _controlfp() function in msvcrt.dll is a more direct way. But of course with the side-effect of both that now that library is operating in a mode that it wasn't designed for, it of course won't expect to encounter Nan and Infinity values. Long term, you really need to consider retiring that old component or library.

这篇关于Windows.Forms Application.Run() 中无法追踪的异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆