从 c# 捕获本机异常并在它离开范围后获取 exception.what()
Capturing native exception from c# and getting the exception.what() after it leaves scope
我从 C# Mono 调用本机 dll,但我无法使用 try catch 子句 c# 端捕获异常。显然 Mono 不支持这一点,无论是否设置了标志(根据此处有关此问题的其他帖子)。一旦异常离开本机端,Mono 总是会立即关闭。
我想到的解决方案是通过 c# 中的所有 dll extern 方法传递 [Out]IntPtr errorText
。这是作为 char** 接收到的本机端。本机 c++ 函数将所有内容包装在 try catch(const std::exception& ex)
子句中。如果没有异常,我将 errorText 设置为 nullptr 但如果有异常,我将其设置为 *errorText=ex.what()
当本机调用 returns 时,它要么有一个指向错误的空指针,这意味着没有异常,要么它是非空的,在这种情况下我将其提取为 Marshal.PtrToStringAnsi(errorText)
这行得通,也行不通。捕获到异常并设置指针,但是编组从 IntPtr 调用 returns null。经过一些测试,我意识到如果我将本机错误文本指针设置为 *errorText="a test"
之类的常量,那么它会按预期工作。
问题似乎是当本机函数 returns 时本机异常对象超出范围,此时 what()
文本变得无效,这意味着当我尝试时它无效整理指向它的指针的内容。
一个解决方案是我自己总是将 const 字符串作为异常抛出,例如 throw "something bad"
并捕获它,因为这些常量字符串仍然有效,然后捕获其他 std::exception
s 只返回一个通用错误字符串,例如"undefined std::exception".
虽然它可以工作,但显然并不完美。问题是为什么我在离开函数后无法到达 *what()
。虽然实际的异常对象可能超出范围,但创建它的消息本身通常是一个常量。如果我抛出 std::exception("something")
那么 what
应该指向常量 "something" 并且在异常超出范围后应该保持有效。
我考虑过在dll中做一个persistent char数组,然后把what()复制进去供以后检索,但是我需要支持多个同时访问,这可能会同时出现多个异常,导致t争夺这个缓冲区。
如果有人对异常离开范围后 what()
不可用的原因有一些了解,或者比抛出字符串异常更优雅地解决这个问题的好主意,我非常感兴趣。
编辑:另一种解决方案是为错误消息管理端分配字符串,并传递指向本机端的字符串以将错误放入其中。我只需要不需要在每次调用时都分配一个新字符串...更愿意只在 what()
中获取指向实际消息的指针
您可以为您的 "errorText" 参数分配一个字符缓冲区(在本机 DLL 中)并将文本 "ex.what()" 复制到该缓冲区。在这种情况下,记忆将保持有效。
但我认为,在 C# 中读取字符串后,您必须自己释放 char-buffer 以防止内存泄漏。
我从 C# Mono 调用本机 dll,但我无法使用 try catch 子句 c# 端捕获异常。显然 Mono 不支持这一点,无论是否设置了标志(根据此处有关此问题的其他帖子)。一旦异常离开本机端,Mono 总是会立即关闭。
我想到的解决方案是通过 c# 中的所有 dll extern 方法传递 [Out]IntPtr errorText
。这是作为 char** 接收到的本机端。本机 c++ 函数将所有内容包装在 try catch(const std::exception& ex)
子句中。如果没有异常,我将 errorText 设置为 nullptr 但如果有异常,我将其设置为 *errorText=ex.what()
当本机调用 returns 时,它要么有一个指向错误的空指针,这意味着没有异常,要么它是非空的,在这种情况下我将其提取为 Marshal.PtrToStringAnsi(errorText)
这行得通,也行不通。捕获到异常并设置指针,但是编组从 IntPtr 调用 returns null。经过一些测试,我意识到如果我将本机错误文本指针设置为 *errorText="a test"
之类的常量,那么它会按预期工作。
问题似乎是当本机函数 returns 时本机异常对象超出范围,此时 what()
文本变得无效,这意味着当我尝试时它无效整理指向它的指针的内容。
一个解决方案是我自己总是将 const 字符串作为异常抛出,例如 throw "something bad"
并捕获它,因为这些常量字符串仍然有效,然后捕获其他 std::exception
s 只返回一个通用错误字符串,例如"undefined std::exception".
虽然它可以工作,但显然并不完美。问题是为什么我在离开函数后无法到达 *what()
。虽然实际的异常对象可能超出范围,但创建它的消息本身通常是一个常量。如果我抛出 std::exception("something")
那么 what
应该指向常量 "something" 并且在异常超出范围后应该保持有效。
我考虑过在dll中做一个persistent char数组,然后把what()复制进去供以后检索,但是我需要支持多个同时访问,这可能会同时出现多个异常,导致t争夺这个缓冲区。
如果有人对异常离开范围后 what()
不可用的原因有一些了解,或者比抛出字符串异常更优雅地解决这个问题的好主意,我非常感兴趣。
编辑:另一种解决方案是为错误消息管理端分配字符串,并传递指向本机端的字符串以将错误放入其中。我只需要不需要在每次调用时都分配一个新字符串...更愿意只在 what()
中获取指向实际消息的指针您可以为您的 "errorText" 参数分配一个字符缓冲区(在本机 DLL 中)并将文本 "ex.what()" 复制到该缓冲区。在这种情况下,记忆将保持有效。 但我认为,在 C# 中读取字符串后,您必须自己释放 char-buffer 以防止内存泄漏。