6

...kind of. As illustrated by this extremely simplistic example,

enter image description here

very rarely (only once reported so far), it happens that one of my applications crashes this way. I want to terminate it as I normally do when an unspecific exception occurs. My strategy is to (low-level) log the problem, then terminate. The application is part of a subsystem and I want to (re)start it, if any problem is detected. It's built with C++-Builder 6 and runs on Windows (XP...7, also 8). I learned that an abort() most probably caused the error message. The application has a GUI, that's why a message box is shown instead of just making an (unblocking) output to stderr.

And as long as the message box isn't accepted by a user, my application keeps obviously running, for example it handles timers (the lifebeats in the above example increase) or inter-process messages, fully unaware about the problem.

After reading some answers to What is the easiest way to make a C++ program crash? and Difference between raise(SIGABRT) and abort() methods, I tried the following

void mySignalHandler(int sig)
{
    // low-level error reporting here
    exit(-1);
}

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    signal(SIGABRT, mySignalHandler);
    // some more initialisation here
}

which lets my application terminate properly also if abort() or raise(SIGABRT) is called. (I also wish to prevent Windows from "searching for a solution of the problem".)

Is this (registering a signal handler for abort and calling exit there) reliable from your point of view? ...or at least something one can build upon?

Community
  • 1
  • 1
Wolf
  • 9,679
  • 7
  • 62
  • 108
  • 3
    Why not just simply try to debug the program to find the *cause* of the crashes? If you fix the crashes, they won't happen again. – Some programmer dude Aug 14 '14 at 10:22
  • 3
    Using the POSIX signal api to catch a Windows problem isn't going to work so well. Use SEH. – bmargulies Aug 14 '14 at 10:23
  • @JoachimPileborg it's not simple, but I do this. Meanwhile the system has to run anyway... – Wolf Aug 14 '14 at 10:23
  • @bmargulies A user reported this message box (only once until today), my application doesn't use POSIX signals, it must be caused by some library. – Wolf Aug 14 '14 at 10:28
  • Try normarl exit with code, not abort or raise, because those functions calls additional OS facilities. – Tanuki Aug 14 '14 at 10:29
  • @Tanuki I don't call `abort` or `raise` I've to deal with it. – Wolf Aug 14 '14 at 10:31
  • bmargulies: SIGABRT isn't just in POSIX, it's in Standard C. – Shao Aug 14 '14 at 10:39
  • @JoachimPileborg you might not solve all problems... it's a good idea to have your program exit cleanly in case it does crash for unexpected reasons – M.M Aug 14 '14 at 10:54
  • 1
    "And as long as the message box isn't accepted by a user, my application keeps obviously running," - wat – M.M Aug 14 '14 at 10:57
  • @MattMcNabb clear: except that no user input is accepted, because the GUI is blocked (as usual for modal dialogues). Seems that some Borland developer had this great idea. – Wolf Aug 14 '14 at 11:25
  • C++Builder ships with the source code for the RTL and VCL so if it really is coming from there you should be able to change the behaviour. I would have said this only comes from uncaught non-VCL exceptions though.. – M.M Aug 14 '14 at 11:31
  • That's not a Borland dialog. That's a system dialog. – David Heffernan Aug 14 '14 at 11:33
  • @DavidHeffernan are you sure? Which system: Windows? (I added a screenshot) – Wolf Aug 14 '14 at 11:58
  • No, you are probably right. So now you can search for that text in the RTL/VCL source and work out what causes it. – David Heffernan Aug 14 '14 at 12:02

4 Answers4

4

In the C++Builder installation folder, check the following files:

  • source\cpprtl\Source\misc\errormsg.c - implementation of _ErrorMessage
  • source\cpprtl\Source\procses\abort.c - implementation of abort, which calls _ErrorMessage
  • source\cpprtl\Source\misc\assert.c - implementation of _assert, which calls _ErrorMessage

errormsg.c defines an undocumented _messagefunc function pointer that you can set to override the default behavior. Although it's undocumented and not declared in any header files, you can declare it as an extern and access it that way. Sample usage:

extern int (_RTLENTRY * _EXPDATA _messagefunc)(char *msg);

static int LogAndDie(char *msg)
{
  LogMessageToSomeFile(msg);
  exit(1);
  return 0;
}

void InitializeErrorHandling()
{
  _messagefunc = LogAndDie;
}
Josh Kelley
  • 56,064
  • 19
  • 146
  • 246
  • I think your suggestion needs a rethink. The function pointed to by `_messagefunc` should only deal with the error message, i.e. log it. It's called indirectly by `_ErrorMessage`, and this function is also used in non-fatal circumstances. – Wolf Aug 19 '14 at 11:09
  • @Wolf - Hmm. We've been running code using `_messagefunc` in production for several years (not terminating as you're wanting to, but using `_messagefunc` to turn errors into problem reports for our issue tracker). The only non-fatal errors I've seen in practice are math errors (such as taking `log(0)`), which may not be a problem for your application domain. At any rate, checking `strcmp(msg, "Abnormal program termination") == 0` should be enough to get only fatal errors. – Josh Kelley Aug 19 '14 at 12:23
  • The suggested solution didn't help, but the suggestion for research did. It helped me better understand what's going on. So I accept this as the best most helpful answer so far. – Wolf Mar 12 '15 at 11:50
1

You might be able to use Windows Error Reporting to create a dump of the process, when an unhandled exception causes a termination. Then you can review the dump at your leisure and allow some parent-process or other watchdog to restart your process. If you chose this strategy, you would not try to deal with the failure in your code, but to allow it.

Shao
  • 537
  • 3
  • 7
  • Besides the Windows Error Reporting idea (I've still to explore): I don't think that there is a flying exception in this case. – Wolf Aug 14 '14 at 12:05
1

If you want to capture any program exit you should look at atexit(). If you want to capture all termination events then look at std::set_terminate(), if you want to caputure all unexpected exceptions then look at std::set_unexpected(). If you want to capture only abort() you can call signal() with the SIGABRT signal value. You can also wrap your code with try{your code}catch(...){custom event handler}.

  • Maybe I wasn't clear enough about this: Exceptions are really not my problem, but rather the absence of exceptions. – Wolf Aug 14 '14 at 12:02
  • `If you want to capture only abort() you can call signal() with the SIGABRT signal value.` This is exactly what I did. There is no question that this is technically possible. (All other informations in your answers are not related to my problem, so, sorry, -1) Instead I wish to know, how to exit my program properly when this function is called. I'll try to add this detail to the question. – Wolf Aug 15 '14 at 09:05
  • @Wolf I answered as I understood your question the first time you asked it here - since then you have had 6 edits of your original questions. You yourself admitted above that you was perhaps not clear enough. –  Aug 15 '14 at 14:11
  • I felt your answer did not match the first version of the question http://stackoverflow.com/posts/25305653/timeline and I said that in my first comment. Your answer is really far to common about possible programming mistakes (compare it with for example [this answer](http://stackoverflow.com/a/25308616/2932052)). Wouldn't you think you can be more specific? – Wolf Aug 15 '14 at 14:42
  • @Wolf If your question was clear in the first place then why did you need six edits to your question afterwards? Perhaps you should not vote people down when it is your own badly written question that is the problem. –  Aug 17 '14 at 10:31
  • The reference you gave states "The function pointed by func is automatically called without arguments when the program terminates **normally**." I my case it's an abnormal termination... – Wolf Aug 19 '14 at 09:52
  • @Wolf Using `std::set_terminate()` without calling `abort()` from your terminate handler function will prevent abnormal termination. –  Aug 19 '14 at 10:52
  • If I call `abort()`, this terminate handler isn't called, instead I see an access violation (the same as without `std::set_terminate`) - I know the set-terminate-mechanism only in combination with uncaught exceptions... – Wolf Aug 19 '14 at 11:23
  • @Wolf `abort()` may or may not trigger the `atexit()` handlers, you could try it. –  Aug 19 '14 at 11:29
  • tried in a console application. `throw 0;` causes my handler being called ("my terminate" on stderr), whereas `abort();` gives "abnormal program termination" in the output. – Wolf Aug 19 '14 at 11:37
1

I could do some tests, and I only can confirm that registering a SIGABRT signal handler is simply a NOOP.

I tried it with a very simple GUI application written with VS2008 Express. :

  • no framework, nor .NET but only Win API
  • one menu with Exit and Fatal
  • menu managed directly in WndProc
  • Fatal execute 1/0

Here are the result :

  • no special action => windows opens a MessageBox indicating a fatal error ...
  • signal handler for SIGABRT => same MessageBox
  • C++ try catch(...) => same MessageBox
  • SEH in WndProc : can intercept the error !
  • SEH around message loop : can intercept the error !

If I put bot SEH handlers, the most internal (WndProc) catches.

The good new for you is that if is enough to protect the message loop, and you do not have to go into every WndProc.

The bad new is that I do not know C++ builder and cannot say where to find the message loop.

Just to give you a clue, here is how I could protect the message loop in a WinAPI application :

__try {
while (GetMessage(&msg, NULL, 0, 0))
{
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}
}
__except (EXCEPTION_EXECUTE_HANDLER){
    ::MessageBox(NULL, _T("FATAL"), _T("MAIN"), MB_OK | MB_ICONERROR);
}

That way, I can see my own message box, but nothing else and if I comment my message box the application silently exits.

But ... as the message you show is not original Windows one, I suspect C++ builder to already have such an exception handler in its message loop.

Hope it helps ...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Thanks for your research! C++-Builder(6) GUI applications are based on the VCL framework. The message loop is run by `Application::Run` and not directly affected by the application source. I wish better to stick to this convention. – Wolf Aug 14 '14 at 13:38
  • I onced (very, very long time ago) used a BC compiler, If I correctly remember, the main application class extended Application, and it was possible to override many things (including run, or ...loop). Is it still possible to do so ? – Serge Ballesta Aug 14 '14 at 13:47
  • This was in good old [TVision](http://en.wikipedia.org/wiki/Turbo_Vision) time, it changed under Windows, because Windows itself is based on an own event controlled GUI concept. – Wolf Aug 14 '14 at 13:50
  • Not so old, Windows 3.11 time, but it is still far in my memory ... As VCL is not publicly available I cannot help you further than advising you to look after all overridable methods ... – Serge Ballesta Aug 14 '14 at 13:54