从 c# 捕获本机异常并在它离开范围后获取 exception.what()
Posted
技术标签:
【中文标题】从 c# 捕获本机异常并在它离开范围后获取 exception.what()【英文标题】:Capturing native exception from c# and getting the exception.what() after it leaves scope 【发布时间】:2016-08-15 08:31:37 【问题描述】:我从 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()
当本地调用返回时,它要么有一个指向错误的空指针,这意味着没有异常,要么它不为空,在这种情况下我将它提取为Marshal.PtrToStringAnsi(errorText)
这有效,也无效。异常被捕获并且指针被设置,但封送调用从 IntPtr 返回 null。经过一些测试,我意识到如果我将本机错误文本指针设置为像 *errorText="a test"
这样的常量,那么它会按预期工作。
问题似乎是当本机函数返回时本机异常对象超出范围,此时what()
文本变得无效,这意味着当我尝试从指向它的指针编组内容时它是无效的.
一种解决方案是我自己总是将 const 字符串作为 throw "something bad"
之类的异常抛出并捕获它,因为这些常量字符串仍然有效,然后捕获其他 std::exception
s 只返回一个通用错误字符串,例如“undefined std::exception ”。
这显然不是完美的,尽管它可以工作。问题是为什么我在离开函数后无法访问*what()
。虽然实际的异常对象可能超出范围,但创建它的消息通常本身就是一个常量。如果我确实抛出了std::exception("something")
,那么what
应该指向常量“某物”,并且在异常超出范围后应该保持有效。
我曾考虑在 dll 中创建一个持久字符数组并将 what() 复制到其中以供以后检索,但我需要支持多个同时访问,这可能会同时出现多个异常,导致争吵这个缓冲区。
如果有人对为什么在异常离开范围后what()
不可用有一些见解,或者比抛出字符串异常更优雅地解决这个问题的好主意,我很感兴趣。
编辑:另一种解决方案是为错误消息管理端分配字符串,并将指针传递给本机端以将错误放入。我只需要不为每次调用分配一个新字符串...更愿意只在 what() 中获取指向实际消息的指针
【问题讨论】:
可能相关。 (***.com/questions/150544/…) 可能的解决方案:(***.com/questions/6850091/…) 这似乎是相关的,但我不能从我的本机代码中抛出异常,就像在引用的帖子中那样。这会导致即时终止,而且我也不能为来自本机代码的消息分配收集的垃圾......这是纯 C++,没有添加 ms。不过,给我一个想法,所以我会延长我的帖子 我很好奇为什么这被否决了。如果没有简单的解决方案,这似乎是一个普遍的问题......? 【参考方案1】:您可以为您的“errorText”参数分配一个字符缓冲区(在本机 DLL 中)并将文本“ex.what()”复制到该缓冲区。在这种情况下,内存将保持有效。 但我认为,您必须在读取 C# 中的字符串后自行释放字符缓冲区以防止内存泄漏。
【讨论】:
谢谢,但根据我的问题,我已经考虑并拒绝了。 我对您的回答有点不公平,因为您实际上并没有说应该在所有呼叫之间共享缓冲区。我现在使用的解决方案是为每个异常分配缓冲区并将指针传回托管。然后,Managed 的任务是在读取文本后对该指针调用本机删除。因此,我显然应该接受您的回答。抱歉,如果我最初误读了您。以上是关于从 c# 捕获本机异常并在它离开范围后获取 exception.what()的主要内容,如果未能解决你的问题,请参考以下文章