【问题标题】:Confusion around SetThreadErrorMode(), SetErrorMode(), _set_error_mode() and _CrtSetReportMode()围绕 SetThreadErrorMode()、SetErrorMode()、_set_error_mode() 和 _CrtSetReportMode() 感到困惑
【发布时间】:2018-12-23 22:39:03
【问题描述】:

我有一个用 C++ 编写并使用 Visual Studio 2015 编译的命令行应用程序。

我需要确保此应用程序在自动、无人值守的功能测试期间不会被错误对话框阻塞,尤其是在断言失败的情况下(标准 assert() 来自 <cassert>)。

我最初认为https://stackoverflow.com/a/6925695/393756 中建议的以下调用可以完成这项工作,但它没有:

_set_error_mode(_OUT_TO_STDERR);

通过实验,我最终发现以下代码达到了预期的效果,至少在断言对话框失败时

SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);

_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);

_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);

_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);

问题:

  1. 为什么_set_error_mode(_OUT_TO_STDERR) 不够用?如果我正确理解documentation,它应该是。

  2. 为什么_CrtSetReportMode(_CRT_ASSERT)(加上相关的_CrtSetReportFile() 调用)不足以禁用断言对话框?显然我还需要配置_CRT_ERROR

  3. 为了确保没有打开任何对话框,我应该调用SetErrorMode(),如果是的话,使用什么参数?

  4. 我应该更喜欢SetThreadErrorMode()吗?

【问题讨论】:

  • 您所说的与文档相符,但与我的经验不符。以下program 使用带有控制台子系统的 VS 2015 工具集和多线程调试 DLL 运行时使用 VS 2017 编译,尽管调用了 _set_error_mode(_OUT_TO_STDERR),但确实显示了一个对话框。

标签: c++ c windows winapi


【解决方案1】:

_set_error_mode 配置assert_CrtSetReportMode 配置 _CrtDbgReport,它只在 CRT 的调试版本中定义,并通过 _ASSERTE 等宏在内部使用。

虽然_set_error_mode 似乎足以禁用来自assert 的消息框,但这还不够,因为assert 调用abort。在调试版本中,abort 的默认行为包括_WRITE_ABORT_MSG,它报告运行时错误,它调用_CrtDbgReportW 报告_CRT_ERROR。如果没有_CrtSetReportMode,您可以通过_set_abort_behavior(0, _WRITE_ABORT_MSG) 更改中止行为来避免这种情况。但考虑到您的目标是在调试版本中抑制所有消息框,您仍然需要 _CrtSetReportMode 以供 CRT 内部使用 _ASSERTE 和调用 _CrtDbgReport 的相关宏。

要配置 Windows 错误报告,请在进程启动时调用 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX)。如果子进程应该使用默认错误模式而不是继承此模式,请在 CreateProcess 调用中使用 CREATE_DEFAULT_ERROR_MODE 创建标志。

【讨论】:

    【解决方案2】:

    assert() 宏不会直接在 VS 中生成异常。检查this。您调用的函数会在发生异常时尝试阻止消息框(在这种情况下,您可以安装顶级 exception handler)。

    因此,您无法停止它,因为它在调用 abort() 之前是一个简单的 MessageBox()。创建此宏是为了在调试模式下显示编程错误,如果您不想显示任何内容,可以使用自己的 assert() 函数,不包括 assert.h。

    【讨论】:

    • 这里的重点是继续使用标准的assert() 宏。我的代码库很大(50 万行代码),不能更改为我自己的断言宏。显然,assert() 会弹出一个带有调试版本的对话框。我给出的解决方案有效,我只是对我提到的各种功能的确切作用和目的感到困惑。
    • 好吧,正如我所说,这些函数控制异常中发生的情况。断言宏不打算被抑制。此外,您可以简单地定义自己的断言并从代码中删除 assert.h 并将其替换为您自己的,而不是将所有断言调用更改为其他内容。
    • 我的目标不是“压制”断言,而是压制阻止进程的对话框,直到用户单击按钮。正如我在问题中解释的那样,在无人值守的功能测试中,使用对话框阻止进程的失败断言不是一种选择。最后,不,不能简单地替换 assert.h,因为第三方依赖项也依赖于它(在任何情况下,替换所有 <cassert> 都不是一种选择,因为代码量很大)。
    • 那么在这种情况下你会被卡住,因为 MSVC 中的 assert() 只显示一个 MessageBox,除非你挂钩 MessageBoxW(),否则无法停止。
    猜你喜欢
    • 2011-11-06
    • 2011-01-30
    • 1970-01-01
    • 2020-08-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-08
    • 2017-09-20
    • 2021-11-28
    相关资源
    最近更新 更多