views:

69

answers:

5

We have an unmanaged C++ application that utilizes 3rd party APIs to read CAD files. On certain corrupted CAD files, the 3rd party library crashes and brings our EXE down with it. Because of this our main application is a separate EXE and in this way it does not get affected by the crash. Howevever, we end up with annoying Microsoft Error Reporting dialogs.

I do not want to disable Microsoft Error Reporting system wide. Is there a way to turn off error reporting for a single application, so that if it crashes, it crashes silently without error popup dialogs?

A: 

I'm not at all sure, but perhaps SetErrorMode or SetThreadErrorMode will work for you?

Jerry Coffin
Additional note, if this crash is being caused by an unhandled structured exception (access violation, stack overflow, etc.), you may want to look into using structured exception handlers. You could use them to log some information and silently crash or whatever else you may want to do.
dauphic
+3  A: 

On Vista and above, the WerAddExcludedApplication API function can be used to exclude a specified application executable from error reporting. As far as I'm aware, there's no similar option on XP and other legacy OS versions.

However, since WER will only kick in on unhandled application exceptions, you should be able to suppress it by adding a "catch-all" exception handler to your EXE. See vectored exception handling for some ideas on how to achieve that.

Note that suppressing all unhandled exceptions is generally a bad idea (and will, for example, cause your app to fail Windows Logo certification), so you should not use this technique indiscriminately...

mdb
+1 for mentioning the WER API.
bk1e
+4  A: 

Yeah, there's something you can do. Call SetUnhandledExceptionFilter() in your main() method to register a callback. It will be called when nobody volunteers to handle the exception, just before the Microsoft WER dialog shows up.

Actually doing something in that callback is fraught with trouble. The program has died with, invariably, something nasty like an AccessViolation exception. Which often is tripped by heap corruption. Trying to do something like displaying a message box to let the user know is troublesome when the heap is toast. Deadlock is always lurking around the corner too, ready to just lock up the program without any diagnostic at all.

The only safe thing to do is to have a helper process that guards your main process. Wake it up by signaling a named event in your callback. Give it the exception info it needs with a memory-mapped file. The helper can do pretty much anything it wants when it sees the event signaled. Including showing a message, taking a minidump. And terminating the main process.

Which is exactly how the Microsoft WerFault helper works.

Hans Passant
+2  A: 

Hans Passant's answer about SetUnhandledExceptionFilter is on the right track. He also makes some good points about not being able to do too much within the callback because various parts of the process could be in an unstable state.

However, from the way the issue is described, it doesn't sound like you want to do anything except tell the system not to put up the normal crash dialog. In that case, it's easy and should be safe regardless of what parts of the process the crash may have affected.

Make a function something like this:

LONG WINAPI UnhandledExceptionCallback(PEXCEPTION_POINTERS pExceptPtrs)
{
    if (IsDebuggerPresent())
        // Allow normal crash handling, which means the debugger will take over.
        return EXCEPTION_CONTINUE_SEARCH;
    else
        // Say we've handled it, so that the standard crash dialog is inhibited.
        return EXCEPTION_EXECUTE_HANDLER;
}

And somewhere in your program (probably as early as possible) set the callback:

SetUnhandledExceptionFilter(UnhandledExceptionCallback);

That should do what you want - allow any crashes of that particular program to die silently.

However, there's something else to note about this: Any time you bring in 3rd-party components (DLLs, OCXs, etc) there is a risk that one of them may also call SetUnhandledExceptionFilter and thus replace your callback with their own. I once encountered an ActiveX control that would set its own callback when instantiated. And even worse, it failed to restore the original callback when it was destroyed. That seemed to be a bug in their code, but regardless I had to take extra steps to ensure that my desired callback was at least restored when it was supposed to be after their control was shutdown. So if you find this doesn't appear to work for you sometimes, even when you know you've set the callback correctly, then you may be encountering something similar.

TheUndeadFish
This doesn't work, nothing was handled. It will just restart the faulting instruction again, crashing the same way. Endless loop.
Hans Passant
Admittedly, I wrote this based on a combination of memory and an excerpt of some old code that was no longer in a position to test this. But I just put it in a quick test program that I made crash by trying to use a NULL pointer and it appears to work as intended. I'm seeing it end silently, not loop. Wouldn't it be EXCEPTION_CONTINUE_EXECUTION that causes it to resume on the faulting instruction?
TheUndeadFish
A: 

I found myself in exactly this situation while developing a Delphi application. I found that I needed two things to reliably suppress the "app has stopped working" dialog box.

Calling SetErrorMode(SEM_NOGPFAULTERRORBOX); suppresses the "app has stopped working" dialog box. But then Delphi's exception handler shows a message box with a runtime error message instead.

To suppress Delphi's exception handler I call SetUnhandledExceptionFilter with a custom handler that terminates the process by calling Halt.

So the skeleton for a Delphi client app that runs code prone to crashes becomes:

function HaltOnException(const ExceptionInfo: TExceptionPointers): Longint; stdcall;
begin
  Halt;
  Result := 1;  // Suppress compiler warning
end;

begin
  SetErrorMode(SEM_NOGPFAULTERRORBOX);
  SetUnhandledExceptionFilter(@HaltOnException);
  try
    DoSomethingThatMightCrash;
  except
    on E: Exception do
      TellServerWeFailed(E.Message);
  end;
end.
Jan Goyvaerts