是否有可能有一個管理拋出異常和託管代碼抓住但那裏有介入本地幀調用堆棧上?追趕跨越本地幀
我在做這件事時遇到了麻煩。該應用程序是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");
}
}
MyKernel
是混合的C/CLI組件我們稱之爲Glue.dll
定義的管理類。 MyObject
是Glue.dll
中另一個類的實例。在有相關的方法看起來是這樣的:
void DoSomething(void)
{
_pMyNativeKernel->DoSomething();
}
DoSomething
是Native.exe
C++函數被稱爲虛擬。簡而言之,它最終會回調到Glue.dll
的管理方法中,這會提高MyEvent
。
如果MyEvent
上升,該程序在32位的Windows XP系統上運行,它的行爲預期和控制檯將顯示:
Throwing...
Caught
運行Windows 7 64位系統上我,而不是得到這個:
Throwing...
Finished
基本上,例外只是消失在空氣稀薄;整個事情繼續運行,好像它從未發生過一樣。 (例外相當於打一個窗口上的關閉按鈕,所以它只是充當雖然按鈕沒有被點擊)
運行Windows Server 2012系統通過遠程桌面,我得到這個:
Throwing...
然後整個應用程序崩潰與一個對話框說「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
這是我本來期望要不是我有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()
這整個系統已經很長一段時間工作正常,但我從來不需要拋出異常跨越受管理/本機過渡之前。我希望託管應用程序代碼能夠自由地拋出和捕獲異常,而不必擔心本地主機是否正在執行本機到受管理的回調。
我在網上找到的關於在這種轉換中拋出異常的所有信息總是關於託管catch本機異常,反之亦然。這是管理捕捉託管,但介入本機幀是複雜的事情。
所以我的問題在問候中一般投擲像這樣的:
如果這項工作?它確實在Windows XP上工作,但我不知道這是明確的行爲還是我只是幸運。
如果它應該工作,爲什麼它不能在所有系統上工作有什麼可能的原因?
如果它不應該工作,那麼我想我不得不加強所有託管回調捕捉託管異常,與本機的異常包裝它們,並趕上在託管包裝本地函數並拋出原始的託管異常。這聽起來像很多頭髮拉!
如果異常是在本地線程中觸發的,那麼您描述的問題可能與http://stackoverflow.com/questions/6124631/clr-hosting-exception-handling-in-a-non-clr-created-線程?RQ = 1。 看看是否有助於確保您始終在託管線程上執行回調。 – archgl
@archgl感謝您的提示。在我的情況下,它不應該是_unhandled_異常,但有另一個問題的線索..也許發生了什麼是它成爲一個SEH異常,由於本地框架,所以外部託管框架無法捕捉它,因爲它只試圖捕捉原始的託管異常,而不是SEH。我希望它比這更聰明(M-> N thunk _應該抓住SEH並轉換回受管理),但也許不是。回調一個單獨的線程實際上並不會允許外部託管代碼按照我的需要_catch_異常。 – Kevin