views:

42

answers:

2

I'm trying to write a test suite for a compiler (LLVM) and it works perfectly fine on every platform except for Windows. On Windows I get the "critical-error-handler" message box which stops the tests indefinitely.

This problem makes it very difficult to test because, with compilers, a problem often means invalid code on the assembly level, and thus crazy, unpredictable errors.

I found http://stackoverflow.com/questions/977275/during-a-subprocess-call-catch-critical-windows-errors-in-python-instead-of-lett while searching for the answer, but this doesn't work for me. I still get the message boxes when testing.

The documentation on [SetErrorMode](http://msdn.microsoft.com/en-us/library/ms680621(VS100).aspx) Says that:

SEM_FAILCRITICALERRORS:
The system does not display the critical-error-handler message box. Instead, the system sends the error to the calling process.

SEM_NOGPFAULTERRORBOX:
The system does not display the Windows Error Reporting dialog.

Each process has an associated error mode that indicates to the system how the application is going to respond to serious errors. A child process inherits the error mode of its parent process.

However, after calling SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX) and launching the process with CreateProcess with dwCreationFlags = CREATE_NEW_CONSOLE, I still get the boxes when subprocesses fail.

In case it matters, the exact Python code I am using is:

import ctypes
# 3 is SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX
ctypes.windll.kernel32.SetErrorMode(3)

How do I fix it?

A: 

The error mode is normally be inherited by subprocesses. It sounds like the implementation of system (or execv, or whatever's being used to spawn sub-processes) is passing the CREATE_DEFAULT_ERROR_MODE flag when it calls CreateProcess. To get the behavior you want (inheriting the error mode you've set), you'll have to either modify the library implementation so whatever you're using to spawn the processes doesn't pass that flag, or else implement (and use) something new that doesn't pass it.

Jerry Coffin
CreateProcess is only being called with `CREATE_NEW_CONSOLE`.
Michael Spencer
A: 

The problem was actually Dr. Watson(link redacted), who rudely ignores SetErrorMode(link redacted). The only way to prevent Dr. Watson from stealing your joy is to prevent him from ever getting the call. There are two ways to do this.

  1. Call SetUnhandledExceptionHandler(yourexceptionhandler);

    If you call exit in this handler, the program terminates normally and Dr. Watson is never invoked.

    UnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) {
      fputs("Application crashed with unhandled exception!\n", stderr);
    
    
      // Exit to prevent anything else from handling this exception.
      _exit(-3);
    }
    
    
    int main() {
      SetUnhandledExceptionFilter(UnhandledExceptionFilter);
    }
    

    This only works if you have access to the source code and feel like modifying it. In my case I had access to the source code, but it was a collection of a few hundred separate files, and so adding this code in all of them was a non starter. It also wouldn't handle the case of an error before main, or just plain invalid executables.

  2. Run the process under a debugger using CreateProcess(..., DEBUG_PROCESS, ...)(link redacted) and WaitForDebugEvent(link redacted)

    When running a process like this, all exceptions are propagated to the debugger, and thus they can be handled appropriately.

    This is a rather complex solution to what should be a simple problem, but it is the only way I have found to completely block Dr. Watson. I am Currently writing a minimal program that tries to run the subprocess normally in every way except for catching unhandled exceptions. I will post a link here once it is committed.

Michael Spencer