2013-11-01 13 views
5

E 'possibile avere un un'eccezione gestito gettato e catturato dal codice gestito ma dove ci sono intervengono frame nativi sul stack di chiamate?Facendo un'eccezione gestita attraverso frame nativi

Ho problemi a fare questo. L'app è un codice nativo a 32 bit e ospita MSCLR 2.0 (ma la maggior parte del codice è .NET 3.5.)

L'applicazione viene eseguita a meno che non venga eseguito questo passaggio e che cosa accade esattamente quando viene eseguito il tiro dipende dal sistema sta funzionando.

L'applicazione effettiva è piuttosto complessa quindi almeno inizialmente pubblicherò qualche semplice codice concettuale solo per illustrazione. Il programma nativo (che chiameremo Native.exe) esegue un singolo programma gestito che chiameremo Managed.exe. Da qualche parte all'interno Managed.exe, scritto in C#, è la seguente:

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 è una classe gestita definita in un misto C++/CLI assemblaggio che chiameremo Glue.dll. MyObject è un'istanza di un'altra classe in Glue.dll. Il metodo rilevante ci sembra qualcosa di simile:

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

DoSomething è una funzione C++ in Native.exe che si chiama virtualmente. Per farla breve, alla fine si finisce per richiamare in un metodo gestito in Glue.dll che solleva MyEvent.

Se MyEvent viene sollevata e il programma è in esecuzione su un sistema a 32 bit di Windows XP, si comporta come previsto e la console visualizzerà:

Throwing... 
Caught 

esecuzione su un sistema Windows 7 64-bit I invece get this:

Throwing... 
Finished 

Fondamentalmente, l'eccezione svanisce nel nulla; il tutto continua a correre come se non fosse mai accaduto. (. L'eccezione corrisponde a colpire il pulsante di chiusura su una finestra, quindi si comporta come se il pulsante non è stato cliccato)

esecuzione su un sistema Windows Server 2012 Più di desktop remoto, ottengo questo:

Throwing... 

e poi tutto l'applicazione si blocca con una finestra che dice "Native.exe ha smesso di funzionare" e questo:

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 

questo è quello che mi sarei aspettato non avessi avuto il try/catch.

Se lo eseguo in tale ambiente con il debugger VS2008SP, il debugger rileva l'eccezione di prima scelta e, se lo proseguo, lo rileva come un'eccezione non gestita.

Vorrei sottolineare che il nativo DoSomething alla fine finisce per chiamare il Win32 GetMessage e poi DispatchMessage, e il callback nativo-to-gestito si verifica in codice chiamato da una routine di finestra. Quella finestra è disegnata con Direct3D.Il programma gestito utilizza il "kernel" Native.exe per tutte le operazioni di gestione di finestre e disegni e non accede mai a Windows da solo.

Nessuna delle funzioni che intervengono in Native.exe rileva alcuna eccezione. Non posso dire se ci sono gestori di eccezioni nelle funzioni dell'API Win32; Non lo penserei, ma se ci fosse, si potrebbe in teoria spiegare come il comportamento fosse incoerente tra i sistemi.

Questo è più o meno lo stack di chiamate reale sul Server 2012, con elementi ripetitivi tagliato:

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()  

L'intero sistema è stato funzionando benissimo per un lungo periodo di tempo, ma non ho mai bisogno di generare eccezioni in tutto il gestito/transizioni native prima. Voglio che il codice dell'applicazione gestita sia in grado di lanciare e catturare le eccezioni liberamente senza preoccuparsi se l'host nativo sta eseguendo una callback da nativa a gestita.

Tutte le informazioni che trovo on-line su come lanciare eccezioni attraverso tali transizioni è sempre di essere riusciti a rilevare un'eccezione nativa o viceversa. Questo è gestito gestito cattura, ma i fotogrammi nativi intervengono complicano le cose.

Così le mie domande in materia di gettare come questo, in generale, sono:

  • caso questo lavoro? È fa funziona su Windows XP ma non so se questo è stato un comportamento ben definito o se sono stato solo fortunato.

  • Se il numero deve essere, quali sono le possibili ragioni per cui non funziona su tutti i sistemi?

Se è non dovrebbe lavorare, allora credo che avrei dovuto per aumentare tutti i call-back riusciti a intercettare le eccezioni gestite, avvolgerli con un'eccezione nativa, e cattura che nel wrapper gestito per la funzione nativa e lancia l'eccezione gestita originale. Sembra un sacco di strappi!

+1

Se l'eccezione viene attivata in un thread nativo, il problema descritto potrebbe essere correlato a http://stackoverflow.com/questions/6124631/clr-hosting-exception-hand-in-a-non-clr-created- infilare? rq = 1. Verificare se è utile assicurarsi di eseguire sempre la richiamata su un thread gestito. – archgl

+0

@archgl Grazie per il suggerimento. Nel mio caso non dovrebbe essere un'eccezione _unhandled_, ma c'è un indizio nell'altra domanda ... forse quello che sta succedendo è che sta diventando un'eccezione SEH dovuta ai frame nativi e quindi il frame gestito esterno non lo cattura perché è solo cercando di catturare l'eccezione gestita originale, non quella SEH. Speravo che fosse più intelligente di quello (il M-> N thunk _should_ cattura il SEH e riconvertito in gestito) ma forse no. La chiamata su un thread separato non consentirà effettivamente al codice gestito esterno di _catch_ l'eccezione come voglio. – Kevin

risposta

0

Ho a che fare con lo stesso problema. Ho una forma, il codice che la chiama (o piuttosto il codice che chiama .ShowDialog()) si trova all'interno di un blocco try {} con un blocco catch {} corrispondente. Ad un certo punto un clic sul pulsante fa apparire un'eccezione, MA il colpo non viene colpito!

Così ho modificato il codice e quindi ho semplicemente circondato l'istruzione incriminata (una conversione, nel gestore OnClick) con il proprio try/catch.

Bene, la "presa" viene colpita ma al suo interno c'è un semplice "lancio"; porta all'eccezione User-Unhandled!

Se guardo lo stack, ci sono diverse transizioni gestite/native/gestite.

Sembra che lo stack gestito non abbia un gestore e che il sistema non passi lo stack attraverso i frame nativi al frame gestito successivo, quindi considera che non ci sia alcun gestore.