Untracked Exceptions in Windows.Forms Application.Run ()
I have an old Windows.Forms application that I am trying to debug.
Sometimes it throws an ArithmeticException or OverflowException after running for a few minutes. The source should be somewhere in the code base, but the stacktrace always points to the lineApplication.Run(mainForm);
StackTrace is useless as it only displays normal Windows.Forms 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:\xy\Program.cs:Zeile 102.
To find the source of the exception, I added an exception handler for
System.Windows.Forms.Application.ThreadException
and System.AppDomain.CurrentDomain.UnhandledException
.
I tried to enable and disable exceptions for catching
System.Windows.Forms.Application.SetUnhandledExceptionMode();
The ThreadException event handler is never called. The UnhandledException event handler just reports the same exception that I see in Visual Studio.
In Visual Studio, I enabled termination on an exception: This had no effect.
What can I do to find the offending line of code?
edit: full details of the details:
If I start a process with no debugger attached and wait for it to crash before attaching the debugger, I get the following exception:
Unbehandelte Ausnahme bei 0x0c9f9e1b in program.exe: 0xC0000090: Floating-point invalid operation.
Debugging then leads to this disassembly part
0C9F9E12 add esi,10h
0C9F9E15 push 0CA1FD48h
0C9F9E1A push eax
0C9F9E1B fmul qword ptr ds:[0CA202E0h]
0C9F9E21 fstp dword ptr [esp+18h]
I cannot make out this, but I suspect it is just the DispatchMessageW function
source to share
The diagnosis here is that you have legacy unmanaged code in your process, judging by the call stack you posted, which may be an old ActiveX control.
These exceptions are hardware exceptions thrown by FPU, floating point processor. Which can be put into run mode where it reports problems by throwing exceptions like the STATUS_FLOAT_OVERFLOW and STATUS_FLOAT_INVALID_OPERATION exceptions you see. Instead of generating infinity, NaN or denormals. The FMUL statement can easily throw such an exception.
Software that changes the FPU's behavior is fundamentally incompatible with managed code. This requires FPU exceptions to always be masked. The masking of these exceptions is completely normal and what is done with all modern software. However, in the past century, these exceptions have been recognized as an asset for diagnosing floating point computing. In particular, the old Borland runtime libraries exposed these exceptions.
Well, this is all pretty bad news if you haven't received this message yet. The first thing to look at is trying to diagnose why this code is throwing floating point exceptions. Bad data is usually the most common cause. Second, you really need to do something with the changed FPU control register, this can cause managed code to crash. Particularly the problem is in WPF code, it likes to use NaN.
Finding such code is pretty easy with a debugger. Use the Debug + Windows + Registers debug window. Right-click the window and check the Floating Point option. The CTRL register value is critical and must be 027F
in the managed program. Step through the program, roughly at first, you found the problem when the case changed. If it is a 64-bit program, then also check "SSE", the MXCSR register must be 00001F80
.
You cannot directly reset control of the FPU register with managed code, but you can use a trick. The CLR resets it whenever it handles an exception. Thus, a possible fix is ββto intentionally throw and catch an exception after the statement that caused the change in the value of the control register:
try { throw new Exception("Resetting FPU control register, please ignore"); }
catch { }
Calling the _controlfp () function in msvcrt.dll is more direct. But, of course, with the side effect of the library now running in a mode it was not intended for, it certainly won't expect Nan and Infinity values. In the long run, you really need to consider leaving your old component or library.
source to share