Monday, May 10, 2004

 

How to implement Error Report

There were numerous Error Report Library published on CodeProject. Here are some representative ones.

1.
Add Crash Reporting to Your Applications with the CrashRpt Library
By Mike Carruth
http://www.codeproject.com/debug/crash_report.asp

2.
XCrashReport : Exception Handling and Crash Reporting
By Hans Dietrich
http://www.codeproject.com/debug/XCrashReportPt1.asp
http://www.codeproject.com/debug/XCrashReportPt2.asp
http://www.codeproject.com/debug/XCrashReportPt3.asp
http://www.codeproject.com/debug/XCrashReportPt4.asp

3.
A Reusable, Flexible Error Reporting Framework ( It is written in C# )
By brady gaster
http://www.codeproject.com/csharp/ErrorTrapper.asp

4.
Error Reporter - Enables Back-2-Base Error Reporting
By voodoopriestess
http://www.codeproject.com/internet/error_reporter.asp

All these approaches discussed how to install handle hook, what environment status should be recorded. And some approaches even provided socket based SMPT code and nice GUI features.

The following website lists some frequently used warning messages.

5.
http://rinkworks.com/said/warnings.shtml

The following blog revealed how to totally block off Windows default error popup.

6.
Disabling the program crash dialog
by Raymond Chen
from http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx

If you don't want your program to display the standard crash dialog, you can disable it by setting the SEM_NOGPFAULTERRORBOX flag in the process error mode.

The simple-minded way is just to do

SetErrorMode(SEM_NOGPFAULTERRORBOX);

but this overwrites the previous error mode rather than augmenting it. In other words, you inadvertently turned off the other error modes!

Unfortunately, there is no GetErrorMode function, so you have to do a double-shuffle.

DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);

This sets the new error mode (possibly disabling some other error modes that had been set) and saves the previous mode. Then it sets the error mode the right way, adding the SEM_NOGPFAULTERRORBOX flag to the existing error modes.

Merging with existing error modes is important. For example, the previous error mode may have included SEM_NOALIGNMENTFAULTEXCEPT. If you casually turned that off, then the program would not longer receive automatic alignment fault fixups and will start crashing.

(But if you read the documentation, you'll see that SEM_NOALIGNMENTFAULTEXCEPT is special: The system won't let you turn it off once it's been turned on. Why? Because too many people were already making this mistake. I remember before this special rule was introduced. Programs were crashing left and right because they didn't do the double-set shuffle above; as a result, they started crashing on alignment faults. So the special rule had to be added. Welcome to the world of compatibility, where it is the operating system's duty to cover for other people's mistakes. Mind you, the design of the SetErrorMode function makes this mistake a very easy one to commit.)

Note that the error mode is a process-wide setting, not a per-thread setting. This means that manipulating the process error mode is not something you should do lightly, since it may have unintended consequences for other threads (which you might not have control over). For safety's sake, a program should set its error mode when it starts up and not mess with it thereafter.

Of course, if you disable the crash dialog, then you also miss out on the opportunity to retrieve crash reports collected by Windows Error Reporting so you can see where your program is crashing in the real world.

Below is a comment from Skywing

You can also disable the harderror popup with job objects.

Setting SEM_NOGPFAULTERRORBOX changes the default behavior for kernel32!UnhandledExceptionFilter to return EXCEPTION_CONTINUE_SEARCH instead of handling the exception by raising a hard error popup. The NT kernel (not kernel32.dll) default "exception handler" then terminates the thread (NOT the process) because nobody handled the exception. Note that other threads will continue to run, assuming there are other threads in the process at the time of the unhandled exception (!), so use this option with care. Kernel32 provides the functionality of terminating the process on an unhandled exception. This may not be the case on Win9x, but it defiitely is true on NT.

You can also perform similar handling by calling SetUnhandledExceptionFilter and reacting to unhandled exceptions accordingly.

As for GetErrorMode: It turns out that there really is such a function in kernel32, but it just isn't exported (who knows why). If you have valid symbols for kernel32, you can find it and see how it works...:

0:001> u kernel32!GetErrorMode
kernel32!GetErrorMode:
77e4270d 55 push ebp
77e4270e 8bec mov ebp,esp
77e42710 51 push ecx
77e42711 6a00 push 0x0
77e42713 6a04 push 0x4
77e42715 8d45fc lea eax,[ebp-0x4]
77e42718 50 push eax
77e42719 6a0c push 0xc
0:001> u
kernel32!GetErrorMode+0xe:
77e4271b 6aff push 0xff
77e4271d ff15b810e477 call dword ptr [kernel32!_imp__NtQueryInformationProcess (77e410b8)]
77e42723 85c0 test eax,eax
77e42725 7cd7 jl kernel32!GetErrorMode+0x1a (77e426fe)
77e42727 8b45fc mov eax,[ebp-0x4]
77e4272a a801 test al,0x1
77e4272c 7505 jnz kernel32!GetErrorMode+0x2b (77e42733)
77e4272e 83c801 or eax,0x1
0:001> u
kernel32!GetErrorMode+0x33:
77e42731 c9 leave
77e42732 c3 ret
77e42733 83e0fe and eax,0xfffffffe
77e42736 c9 leave
77e42737 c3 ret

Given this, it would be trivial to write your own implementation of such a function. Of course, this is completely undocumented and unsupported, so use it at your own risk. Especially since you don't really need it.

In C#, we should do as follows

Unhandled exceptions
by Chris Brumme
from http://blogs.msdn.com/cbrumme/archive/2003/04/15/51345.aspx

There are two kinds of threads executing inside managed code: the ones we start in managed code and the ones that wander into the CLR. The ones that started in managed code include all calls to Thread.Start(), the managed threadpool threads, and the Finalizer thread(s).

For all the threads we start in managed code, the CLR has its own exception backstop and nothing will leak out to the OS unhandled exception filter. We can call the AppDomain.UnhandledException event from this backstop.

For the ones that wander into managed code, we are registered on the OS unhandled exception filter so we can call the AppDomain.UnhandledException from there. Of course, different unmanaged components are registered in a rather random order on the OS unhandled exception filter. Some of them chain nicely. Others (like the VC6 filter) might decide to rip the process without chaining under certain circumstances.

So you are only completely assured of getting the AppDomain’s UnhandledException event on the managed threads.

Another subtlety is that you must register for this event in the Default AppDomain. That’s because – by the time the exception is unhandled – the thread has unwound out to the default AppDomain. That’s where all threads start executing and that’s where they eventually unwind to. This is an unfortunate restriction for some scenarios, but it’s not clear whether it will ever be relaxed.

Finally, the CLR has some default policy for which unhandled exceptions should terminate the process and which ones should be swallowed. Generally, unhandled exceptions on threadpool threads, the Finalizer thread(s) and similar “reusable” threads are swallowed. We simply terminate the current unit of work and proceed to the next one. Unhandled exceptions on the main thread of a managed executable will terminate the process.

Nobody particularly likes the defaults that were chosen. But everyone seems to have conflicting opinions on what would have made better defaults. And, at this point, nothing is likely to change. The UnhandledException event is there so that you can install your own policy. You can terminate the process, log the failure, trap to the debugger, swallow the exception or any other behavior.



OK, I think we have get rid of almost all the difficults when writting a Error Report in our own program.



<< Home

This page is powered by Blogger. Isn't yours?