可以防止单个应用程序的 Microsoft 错误报告吗?

Posted

技术标签:

【中文标题】可以防止单个应用程序的 Microsoft 错误报告吗?【英文标题】:Can one prevent Microsoft Error Reporting for a single app? 【发布时间】:2011-03-25 21:21:54 【问题描述】:

我们有一个非托管 C++ 应用程序,它利用 3rd 方 API 来读取 CAD 文件。在某些损坏的 CAD 文件中,第 3 方库崩溃并导致我们的 EXE 崩溃。因此,我们的主应用程序是一个单独的 EXE,因此它不会受到崩溃的影响。但是,我们最终会遇到烦人的 Microsoft 错误报告对话框。

我不想在系统范围内禁用 Microsoft 错误报告。有没有办法关闭单个应用程序的错误报告,这样如果它崩溃了,它会在没有错误弹出对话框的情况下静默崩溃?

【问题讨论】:

【参考方案1】:

在 Vista 及更高版本上,WerAddExcludedApplication API 函数可用于从错误报告中排除指定的应用程序可执行文件。据我所知,XP 和其他旧版操作系统没有类似的选项。

但是,由于 WER 只会启动未处理的应用程序异常,因此您应该能够通过向 EXE 添加“包罗万象”的异常处理程序来抑制它。有关如何实现这一目标的一些想法,请参阅vectored exception handling。

请注意,抑制所有未处理的异常通常是一个坏主意(例如,会导致您的应用无法通过 Windows 徽标认证),因此您不应不加选择地使用此技术...

【讨论】:

【参考方案2】:

是的,你可以做一些事情。在 main() 方法中调用 SetUnhandledExceptionFilter() 以注册回调。当没有人自愿处理异常时,将在 Microsoft WER 对话框出现之前调用它。

实际上,在回调中做某事充满了麻烦。该程序总是死于诸如 AccessViolation 异常之类的令人讨厌的事情。这经常被堆损坏绊倒。当堆被吐司时,试图做一些事情,比如显示一个消息框让用户知道是很麻烦的。死锁也总是潜伏在拐角处,准备在没有任何诊断的情况下锁定程序。

唯一安全的做法是拥有一个辅助进程来保护您的主进程。通过在回调中发送一个命名事件来唤醒它。使用内存映射文件为其提供所需的异常信息。当助手看到事件发出信号时,它几乎可以做任何它想做的事情。包括显示消息,进行小型转储。并终止主进程。

这正是 Microsoft WerFault 帮助程序的工作原理。

【讨论】:

UEF 通常存在问题且不可靠。在一些罕见的情况下,即使它们应该被系统回调,它们也不会被系统回调。此外,它们是全局的,可能会被厚脸皮的 shell 扩展、3rd 方库等覆盖调用WerAddExcludedApplication(由用户“mdb”提议)或调用SetErrorModeSEM_NOGPFAULTERRORBOX(由用户“Jan Goyvaerts”提议)是两者都更强大。【参考方案3】:

Hans Passant 关于SetUnhandledExceptionFilter 的回答是正确的。他还提出了一些关于不能在回调中做太多事情的好点,因为过程的各个部分可能处于不稳定状态。

但是,从问题描述的方式来看,除了告诉系统不要显示正常的崩溃对话框之外,您似乎不想做任何事情。在这种情况下,无论崩溃可能影响到进程的哪些部分,它都很容易并且应该是安全的。

制作一个类似这样的函数:

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;

在你的程序中的某个地方(可能尽早)设置回调:

SetUnhandledExceptionFilter(UnhandledExceptionCallback);

这应该做你想做的 - 允许该特定程序的任何崩溃静默死去。

但是,还有一点需要注意:每当您引入第 3 方组件(DLL、OCX 等)时,其中一个组件也可能会调用 SetUnhandledExceptionFilter 并因此将您的回调替换为他们自己的回调。我曾经遇到过一个 ActiveX 控件,它在实例化时会设置自己的回调。更糟糕的是,它在销毁时未能恢复原始回调。这似乎是他们代码中的一个错误,但无论如何我都必须采取额外的步骤来确保我想要的回调至少在他们应该在他们的控件关闭之后恢复。因此,如果您有时发现这似乎对您不起作用,即使您知道已正确设置回调,那么您可能会遇到类似的情况。

【讨论】:

这不起作用,没有任何处理。它只会再次重新启动错误指令,以同样的方式崩溃。无限循环。 诚然,我写这篇文章是基于记忆和一些不再能够测试的旧代码的摘录。但我只是把它放在一个快速测试程序中,我通过尝试使用 NULL 指针导致崩溃,它似乎按预期工作。我看到它默默地结束,而不是循环。不会是 EXCEPTION_CONTINUE_EXECUTION 导致它在错误指令上恢复吗? 如果我没记错的话,在你的 UEF 回调 (IsDebuggerPresent) 中检查调试器是没有意义的,因为当存在调试器时它们根本不会被回调。跨度> 【参考方案4】:

我在开发 Delphi 应用程序时发现自己正处于这种情况。我发现我需要两件事来可靠地抑制“应用程序已停止工作”对话框。

调用SetErrorMode(SEM_NOGPFAULTERRORBOX); 会抑制“应用程序已停止工作”对话框。但随后 Delphi 的异常处理程序会显示一个带有运行时错误消息的消息框。

为了抑制 Delphi 的异常处理程序,我使用自定义处理程序调用 SetUnhandledExceptionFilter,该处理程序通过调用 Halt 来终止进程。

因此,运行容易崩溃的代码的 Delphi 客户端应用程序的框架变为:

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.

【讨论】:

对于 Visual C++,很多情况下只使用 SetErrorMode(SEM_NOGPFAULTERRORBOX);够了。【参考方案5】:

我完全不确定,但也许 SetErrorModeSetThreadErrorMode 对你有用?

【讨论】:

附加说明,如果此崩溃是由未处理的结构化异常(访问冲突、堆栈溢出等)引起的,您可能需要考虑使用结构化异常处理程序。您可以使用它们来记录一些信息并静默崩溃或您可能想做的任何其他事情。

以上是关于可以防止单个应用程序的 Microsoft 错误报告吗?的主要内容,如果未能解决你的问题,请参考以下文章

Microsoft SQL Server,错误: 18456

如何防止“请告诉 Microsoft 这个问题”对话框

如何在单个查询完成执行之前锁定事务以防止出现死锁错误

Microsoft SQL Server 2008 R2 安装报错

vs2017编译项目报错:Microsoft.Cpp.Clang.targets(212,5): error MSB6006 处理方法

vs2017编译项目报错:Microsoft.Cpp.Clang.targets(212,5): error MSB6006 处理方法