2013-11-01 30 views
5
託管異常

是否有可能有一個管理拋出異常和託管代碼抓住但那裏有介入本地幀調用堆棧上?追趕跨越本地幀

我在做這件事時遇到了麻煩。該應用程序是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定義的管理類。 MyObjectGlue.dll中另一個類的實例。在有相關的方法看起來是這樣的:

void DoSomething(void) 
{ 
    _pMyNativeKernel->DoSomething(); 
} 

DoSomethingNative.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上工作,但我不知道這是明確的行爲還是我只是幸運。

  • 如果它應該工作,爲什麼它不能在所有系統上工作有什麼可能的原因?

如果它應該工作,那麼我想我不得不加強所有託管回調捕捉託管異常,與本機的異常包裝它們,並趕上在託管包裝本地函數並拋出原始的託管異常。這聽起來像很多頭髮拉!

+1

如果異常是在本地線程中觸發的,那麼您描述的問題可能與http://stackoverflow.com/questions/6124631/clr-hosting-exception-handling-in-a-non-clr-created-線程?RQ = 1。 看看是否有助於確保您始終在託管線程上執行回調。 – archgl

+0

@archgl感謝您的提示。在我的情況下,它不應該是_unhandled_異常,但有另一個問題的線索..也許發生了什麼是它成爲一個SEH異常,由於本地框架,所以外部託管框架無法捕捉它,因爲它只試圖捕捉原始的託管異常,而不是SEH。我希望它比這更聰明(M-> N thunk _應該抓住SEH並轉換回受管理),但也許不是。回調一個單獨的線程實際上並不會允許外部託管代碼按照我的需要_catch_異常。 – Kevin

回答

0

我正在處理同樣的問題。我有一個表單,調用它的代碼(或調用.ShowDialog()的代碼)位於具有相應catch塊的try {}塊內。在某個時候點擊對話框上的一個按鈕會導致異常,但是捕捉沒有被擊中!

所以我編輯了代碼,然後用它自己的try/catch簡單地包圍了違規語句(在OnClick處理程序中進行了轉換)。

那麼'catch'被擊中,但裏面有一個簡單的'throw'導致用戶未處理的異常!

如果我看看堆棧,有幾個託管/本機/管理轉換。

似乎託管堆棧沒有處理程序,並且系統沒有將堆棧通過本機幀移到下一個託管框架,因此它認爲沒有處理程序。