我有一个C#应用程序,它调用一个故意访问无效地址的托管C ++ DLL;我在我的C ++项目中启用了SEH Exceptions
,我在我的C ++代码中添加了_se_translator_function
,并且在SIGSEGV
发生时我还添加了一个信号处理程序。使用来自纯粹本机测试的C ++代码,一切都运行良好,但是当我从.net应用程序调用我的C ++代码时,应用程序崩溃了:
未处理的异常:System.AccessViolationException:尝试 读或写受保护的内存。这通常表明其他 记忆已腐败。
at K.killnative() in c:\users\ebascon\documents\visual studio 2013\projects\consoleapplication3\consoleapplication4\source.cpp:line 32
这是我的C#控制台应用程序:
namespace ConsoleApplication3 {
class Program {
static void Main(string[] args) {
try {
var k = new NativeKiller();
k.kill();
}
catch (Exception ex) {
Console.WriteLine("Catching " + ex);
}
}
}
}
这是调用的C ++ / CLI代码:
void MXXExceptionTranslator(unsigned int, struct _EXCEPTION_POINTERS*)
{
throw std::exception("Crash happens");
}
void killnative() {
try {
_set_se_translator(MXXExceptionTranslator);
signal(SIGSEGV, [](int) {
puts("Exception");
exit(-1);
});
int* p = reinterpret_cast<int*>(0xDEADBEEF);
printf("%d\n", *p);
}
catch (...) { //Removing this catch does not change anything
puts("Doing nothing");
}
}
public ref class NativeKiller {
public:
void kill() {
killnative();
}
};
你觉得我做错了什么?在我的现实世界问题中,我需要这个C ++ / CLI进程(这是一个与遗留应用程序的桥梁)来记录错误消息并优雅地消失,而不是弹出“程序停止工作”窗口。
提前致谢,
埃内斯托
答案 0 :(得分:4)
这是一个很好的问题,它可以帮助您发现您没有正确构建代码。 C ++ / CLI编译器非常强大,几乎太功能强大,几乎可以将任何本机代码转换为IL。与C#编译器生成的完全相同的IL。并且它在运行时被视为相同,抖动在运行时将其转换为机器代码。
这通常不是你真正想要的东西。原生C或C ++代码应该由编译器直接转换为机器代码。 _set_se_translator()的MSDN文章对此做了很好的警告:
在托管代码(使用/ clr编译的代码)或混合本机代码和托管代码中使用_set_se_translator时,请注意翻译器仅影响在本机代码中生成的异常。托管代码中生成的任何托管异常(例如在引发System :: Exception时)都不会通过转换器函数进行路由。
通常的方法是在自己的源文件或库项目中单独编译本机代码。但更简单的是利用C ++ / CLI编译器在单个源文件中动态地在IL和机器代码生成之间来回切换的能力。修正:
#pragma managed(push, off)
void MXXExceptionTranslator(unsigned int, struct _EXCEPTION_POINTERS*) { ... }
void killnative() { ... }
#pragma managed(pop)
public ref class NativeKiller { ... }
您现在可以看到异常翻译工作正常。