Kev*_*vin 5 .net clr exception-handling exception clr-hosting
是否可以通过托管代码抛出托管异常,但是调用堆栈中存在干预本机帧?
我在做这件事时遇到了麻烦.该应用程序是32位本机代码并托管MSCLR 2.0(但大多数代码是.NET 3.5.)
除非完成抛出,否则应用程序运行正常,并且当它抛出时究竟发生了什么取决于它运行的系统.
实际的应用程序非常复杂,所以至少在最初我会发布一些简单的概念代码只是为了说明.本机程序(我们将调用Native.exe)运行一个我们将调用的托管程序Managed.exe.某处内Managed.exe,用C#编写,如下:
class MyException : Exception {}
...
void OuterManaged()
{
MyObject.MyEvent += ( s, a ) =>
{
Console.WriteLine( "Throwing..." );
throw new MyException();
}
try
{
MyKernel.DoSomething();
Console.WriteLine( "Finished" );
} catch( MyException )
{
Console.WriteLine( "Caught" );
}
}
Run Code Online (Sandbox Code Playgroud)
MyKernel是我们将调用的混合C++/CLI程序集中定义的托管类Glue.dll. MyObject是另一个类的实例Glue.dll.那里的相关方法看起来像这样:
void DoSomething( void )
{
_pMyNativeKernel->DoSomething();
}
Run Code Online (Sandbox Code Playgroud)
DoSomething是一个Native.exe虚拟调用的C++函数.总而言之,它最终会回归到一种Glue.dll可以提升的管理方法MyEvent.
如果MyEvent引发并且程序在32位Windows XP系统上运行,则其行为与预期一致,控制台将显示:
Throwing...
Caught
Run Code Online (Sandbox Code Playgroud)
在Windows 7 64位系统上运行我改为:
Throwing...
Finished
Run Code Online (Sandbox Code Playgroud)
基本上,例外只是消失在空气中; 整个事情继续运行,好像它从未发生过.(例外对应于按下窗口上的关闭按钮,因此它就像没有单击按钮一样.)
通过远程桌面在Windows Server 2012系统上运行,我得到:
Throwing...
Run Code Online (Sandbox Code Playgroud)
然后整个应用程序崩溃,出现一个对话框,说"Native.exe已经停止工作",这个:
Description:
Stopped working
Problem signature:
Problem Event Name: CLR20r3
Problem Signature 01: Native.exe
Problem Signature 02: 0.0.0.0
Problem Signature 03: 5267c484
Problem Signature 04: 0
Problem Signature 05: 1.0.0.0
Problem Signature 06: 5272e299
Problem Signature 07: 208
Problem Signature 08: f
Problem Signature 09: MyException
OS Version: 6.2.9200.2.0.0.144.8
Locale ID: 1033
Run Code Online (Sandbox Code Playgroud)
如果没有try/ 我就会想到这一点catch.
如果我在VS2008SP调试器下的那个环境中运行它,调试器会捕获第一次机会异常,如果我继续它,它会将它捕获为未处理的异常.
我应该注意,本机DoSomething最终最终调用Win32 GetMessage然后DispatchMessage,并且本机到托管回调发生在从窗口过程调用的代码中.该窗口是使用Direct3D绘制的.托管程序使用Native.exe"内核"进行所有窗口和绘图操作,并且永远不会自己访问Windows.
没有Native.exe任何干预功能可以捕获任何例外.我不能说Win32 API函数中是否有任何异常处理程序; 我不这么认为,但如果有可能可以解释系统之间的行为是如何不一致的.
这大致是Server 2012上的实际调用堆栈,重复项目被删除:
Managed!MyGame.ReInitDisplay.AnonymousMethod(object s = {Engine.Display}, System.EventArgs a = {System.EventArgs}) C# // throw site
Glue.dll!CDisplayBridge::OnClosePressed() C++
[Native to Managed Transition]
Native.EXE!EngineKern::CGfxDisplay_a::HandleClosePress() C++
Native.EXE!EngineKern::CGfxDisplay::WindowProc(HWND__ * hwnd=0x000610ac, unsigned int uMsg=16, unsigned int wParam=0, long lParam=0) C++
user32.dll!74a477d8()
[Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]
user32.dll!74a47c44()
ntdll.dll!773e2f02()
user32.dll!74a48fed()
uxtheme.dll!7422254d()
user32.dll!74a475e7() // DefWindowProc
Native.EXE!EngineKern::CGfxDisplay::WindowProc(HWND__ * hwnd=0x000610ac, unsigned int uMsg=274, unsigned int wParam=61536, long lParam=4261024) C++
user32.dll!74a48a66() // DispatchMessage
Native.EXE!EngineKern::CKernel::DoEvent() C++
[Managed to Native Transition]
Glue.dll!Engine::Kernel::DoEvent() C++ // DoSomething in the example
MyClassLib!MyClassLib.Game.MainLoop() C#
MyClassLib!MyClassLib.Game.Play() C#
Managed!MyGame.Play() C#
Managed!Program.Main(string[] args = {string[0]}) C#
mscorlib.dll!System.AppDomain.ExecuteAssemblyByName(string assemblyName, System.Security.Policy.Evidence assemblySecurity, string[] args)
mscorlib.dll!System.AppDomain.ExecuteAssemblyByName(string assemblyName)
Glue.dll!__Engine__::AppDomainManager::Main(int pEngineKern = 15760932) C++
[Native to Managed Transition]
Native.EXE!EngineGlue::IManagedEntryPoint::Main(long pEngineKern=15760932) C++ // calls in by COM interop
Native.EXE!CClrHost::Run() C++
Native.EXE!wWinMain(HINSTANCE__ * hInstance=0x00e40000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x01462a80, int nCmdShow=5) C++
Native.EXE!__tmainCRTStartup() C
Native.EXE!wWinMainCRTStartup() C
kernel32.dll!74ca8543()
ntdll.dll!773fac3c()
Run Code Online (Sandbox Code Playgroud)
整个系统已经运行了很长时间但我从来不需要在托管/本机转换之前抛出异常.我确实希望托管应用程序代码能够自由地抛出和捕获异常,而不必担心本机主机是否正在执行本机到托管的回调.
我在网上找到的关于在这些转换中抛出异常的所有信息总是关于托管捕获本机异常,反之亦然.这是管理捕获管理,但介入的本机框架使问题复杂化.
所以关于这样投掷的问题一般是:
如果这方面的工作?它不会在Windows XP上工作,但我不知道这是否是良好定义的行为,或者如果我只是幸运.
如果它应该工作,它可能在所有系统上工作的原因是什么?
如果它不应该工作,那么我想我必须扩充所有托管回调以捕获托管异常,用本机异常包装它们,并在本机函数的托管包装器中捕获它并抛出原始托管异常.这听起来像很多头发拉!
小智 0
我正在处理同样的问题。我有一个表单,调用它的代码(或者更确切地说,调用 .ShowDialog() 的代码)位于带有相应 catch { } 块的 try { } 块内。在某些时候,单击对话框上的按钮会导致异常,但不会触发捕获!
因此,我编辑了代码,然后简单地用它自己的 try/catch 包围有问题的语句(OnClick 处理程序中的转换)。
好吧,“接球”被击中,但里面有一个简单的“投掷”;导致用户未处理的异常!
如果我查看堆栈,就会发现有几个托管/本机/托管转换。
托管堆栈似乎没有处理程序,并且系统不会将堆栈从本机帧遍历到下一个托管帧,因此它认为没有处理程序。