【问题标题】:Exceptions propagating from unmanaged code to managed code?从非托管代码传播到托管代码的异常?
【发布时间】:2013-09-10 16:24:00
【问题描述】:

我在 C# 项目中重用了一大块非托管 C++ 代码。非托管 C++ 代码封装在托管 C++ 代码层中,该层与 C# 代码交互。现在,非托管 C++ 代码使用 assert(false) 来引发严重错误。由于 assert() 写入 stderr 并且我的项目是一个 GUI 程序,我认为断言打印输出不会出现。我正在考虑用 throw new exception(...) 替换 assert(false),然后在 C# GUI 层捕获并显示异常。我的问题是:

1) 用 exception() 替换 assert() 是个好主意吗?如果不是为什么?

2) 如果在非托管 C++ 代码中引发异常,它们会正确传播到托管 C++,然后传播到 C# 代码(堆栈跟踪完整且正确)吗?

谢谢,

【问题讨论】:

  • 首先使用assert 作为错误处理程序是个坏主意。如果定义了NDEBUG,则无效。 assert 将在控制台应用程序中写入 stderr 并在 GUI 应用程序中调用 MessageBox。这将是可见的(无论如何在调试版本中)。
  • @llnspectable 是的,我知道断言在发布版本中被编译出来。我对此很满意。我最关心的是在发生错误时获取堆栈跟踪。 assert 只写入 GUI 程序没有的 stderr,所以这对我来说是更大的缺点。
  • assert() 的默认行为是调用调试器中断。不要搞砸了。
  • @Hans documentation 不同意你的说法。

标签: c# c++ exception


【解决方案1】:

1) 用 exception() 替换 assert() 是个好主意吗?如果不是为什么?

assert 是一个调试工具。它将在发布版本中评估为无操作,并且不打算用作错误处理工具。在 C++ 中实现基于异常的错误报告系统是很自然的事情。

2) 如果在非托管 C++ 代码中引发异常,它们会正确传播到托管 C++,然后传播到 C# 代码(堆栈跟踪完整且正确)吗?

在从非托管代码转换到托管代码时,本机代码中引发的 C++ 异常将转换为 SEHExceptions。 C++ 异常可以在本机代码、C++/CLI 互操作层或托管代码中处理。 (人类可读)调用堆栈信息在本机代码中不可用。


混合模式环境下异常处理的背景信息:

C++ 异常(在 Microsoft 的 C++ 编译器中实现)以及 CLR 中的异常都建立在 SEH 异常之上。 Structured Exception Handling 是 Windows 中内置的一项服务,任何可以与 C 接口的环境都可以使用该服务。共享相同的基础使异常能够从托管代码无缝传播到非托管代码,反之亦然。

所有 SEH 异常都带有存储在 EXCEPTION_RECORD 中的相同信息。 ExceptionAddress 成员用作构造堆栈跟踪的入口点。托管程序集包含足够的元数据来构建人类可读的堆栈跟踪。另一方面,本机可执行映像没有。虽然可以生成堆栈跟踪(例如使用 StackWalk64),但不会包含任何符号信息,除非相应的调试信息 (.pdb) 可用。


堆栈跟踪的替代方案:

虽然堆栈跟踪可能会为您提供一些引发异常的提示,但它不包含任何对调试有用的信息,例如参数和局部变量。另一种方法是编写一个小型转储。 MiniDumpWriteDump 是一个非常强大的工具,可以让您非常精细地控制应该包含哪些数据。一个很好的介绍可以在Effective Minidumps - Part 1Effective Minidumps - Part 2 找到。 Minidump 可以加载到调试器(例如 Visual Studio 或 WinDBG)中并进行分析。使用匹配的调试符号(您应该将其添加到任何版本的源代码控制系统中),您将获得一个非常有效的基础架构来分析问题。根据可用的 minidump 信息,您将获得所有线程的调用堆栈、传递给函数的参数、局部变量的内容、加载和卸载的模块等。

【讨论】:

  • @llnspectable 感谢您提供详细信息!我特别喜欢 minidump 的建议。肯定会调查的。关于断言的一个问题:根据 Microsoft 文档,如果从基于 Windows 的应用程序调用断言,它将在消息框中显示消息。但是,如果从链接到基于 Windows 的应用程序的 DLL 内部调用 assert,仍然会使用消息框吗?
  • @llnspectable - 在发生致命错误时创建一个小型转储更好还是让它抛出异常并在最高级别捕获它然后在那里进行小型转储?后者的优点是我不必在整个代码库中进行 minidump 调用。只是想知道这种方式是否有任何缺点。
  • @Thomas assert 实现评估在 CRT 中声明的(未记录的)__app_type 变量,因此它将始终选择正确的输出方法。当引发异常时,您应该始终立即编写一个小型转储,以获取相关的调用堆栈。为此,您需要安装一个 SEH 异常过滤器(它将为您提供 MiniDumpWriteDump 所需的 EXCEPTION_RECORD)。这使您可以在一个地方处理所有异常 - 无需在整个代码中散布调用。这并非微不足道,您可能应该就该主题提出一个新问题。
猜你喜欢
  • 1970-01-01
  • 2013-08-24
  • 1970-01-01
  • 2011-11-01
  • 2011-09-06
  • 2016-06-06
  • 1970-01-01
  • 1970-01-01
  • 2014-07-09
相关资源
最近更新 更多