views:

177

answers:

3

I have a Windows C++ console program, and if I don't call ReleaseDriver() at the end of my program, some pieces of hardware enter a bad state and can't be used again without rebooting. I'd like to make sure ReleaseDriver() gets runs even if the program exits abnormally, for example if I hit Ctrl+C or close the console window.

I can use signal() to create a signal handler for SIGINT. This works fine, although as the program ends it pops up an annoying error "An unhandled Win32 exception occurred...".

I don't know how to handle the case of the console window being closed, and (more importantly) I don't know how to handle exceptions caused by bad memory accesses etc.

Thanks for any help!

+2  A: 

Under Windows, you can create an unhandled exception filter by calling SetUnhandledExceptionFilter(). Once done, any time an exception is generated that is not handled somewhere in your application, your handler will be called.

Your handler can be used to release resources, generate dump files (see MiniDumpWriteDump), or whatever you need to make sure gets done.

Note that there are many 'gotchas' surrounding how you write your exception handler function. In particular:

  1. You cannot call any CRT function, such as new
  2. You cannot perform any stack-based allocation
  3. If you do anything in your handler which causes an exception, Windows will immediately terminate your application by ripping the bones out of its back. You get no further chances to shut down gracefully.

You can call many Windows API functions. But you can't sprintf, new, delete... In short, if it isn't a WINAPI function, it probably isn't safe.

Because of all of the above, it is advisable to make all the variables in your handler function static variables. You won't be able to use sprintf, so you will have to format strings ahead of time, during initialization. Just remember that the machine is in a very unstable state when your handler is called.

John Dibling
Nothing you do in user-mode, including breaking all the exception filter rules, can cause a BSOD. Only hardware failures or bugs in kernel code can do that.
Ben Voigt
@Ben: I think I've done it, but I'll assume my memory is faulty.
John Dibling
@John: user mode code can certainly do something unusual that exposes a kernel bug, but the kernel bug is still the cause.
Ben Voigt
Thanks John that looks perfect.
Meekohi
+1  A: 

If I'm not mistaken, you can detect if the console is closed or the program is terminated with Ctrl+C with SetConsoleCtrlHandler:

#include <windows.h>

BOOL CtrlHandler(DWORD)
{
    MessageBox(NULL, "Program closed", "Message", MB_ICONEXCLAMATION | MB_OK);
    exit(0);
}

int main()
{
    SetConsoleCtrlHandler((PHANDLER_ROUTINE)&CtrlHandler, TRUE);
    while (true);
}

If you are worried about exceptions, like bad_alloc, you can wrap main into a try block. Catch std::exception& which should ideally be the base class of all thrown exception, but you can also catch any C++ exception with catch (...). With those exceptions, though, not all is lost, and you should figure out what is being thrown and why.

Avoiding undefined behavior also helps. :)

UncleBens
+1  A: 

You can't (guarantee code runs). You could lose power, then nothing will run. The L1 instruction cache of your CPU could get fried, then your code will fail in random ways.

The most sure way of running cleanup code is in a separate process that watches for exit of the first (just WaitForSingleObject on the process handle). A separate watchdog process is as close as you can get to a guarantee (but someone could still TerminateProcess your watchdog).

Ben Voigt