2008-10-24 3 views
10

Sto lavorando a un progetto per migliorare le nostre capacità di debug della produzione. Il nostro obiettivo è quello di produrre in modo affidabile un minidump su qualsiasi eccezione non gestita, sia che l'eccezione sia gestita o non gestita, sia che si verifichi su un thread gestito o non gestito.Come funziona SetUnhandledExceptionFilter nelle applicazioni .NET WinForms?

Attualmente utilizziamo l'eccellente libreria ClrDump per questo, ma non fornisce esattamente le funzionalità esatte di cui abbiamo bisogno e mi piacerebbe capire i meccanismi alla base del filtro delle eccezioni, quindi ho deciso di provarlo da solo.

Ho iniziato seguendo questo articolo del blog per installare personalmente un gestore SEH: http://blogs.microsoft.co.il/blogs/sasha/archive/2007/12.aspx. Questa tecnica funziona per le applicazioni console, ma quando provo la stessa cosa da un'applicazione WinForms, il mio filtro non viene chiamato per nessuna varietà di eccezioni non gestite.

Cosa può fare ClrDump che non sto facendo? ClrDump produce dump in tutti i casi, quindi il suo filtro di eccezioni deve ancora essere chiamato ...

Nota: sono a conoscenza delle funzionalità di ADPlus e abbiamo anche considerato l'utilizzo delle chiavi di registro AeDebug ... Queste sono anche le possibilità , ma hanno anche i loro compromessi.

Grazie, Dave

// Code adapted from <http://blogs.microsoft.co.il/blogs/sasha/archive/2007/12.aspx> 
LONG WINAPI MyExceptionFilter(__in struct _EXCEPTION_POINTERS *ExceptionInfo) 
{ 
    printf("Native exception filter: %X\n",ExceptionInfo->ExceptionRecord->ExceptionCode); 

    Beep(1000,1000); 
    Sleep(500); 
    Beep(1000,1000); 

    if(oldFilter_ == NULL) 
    { 
     return EXCEPTION_CONTINUE_SEARCH; 
    } 

    LONG ret = oldFilter_(ExceptionInfo); 
    printf("Other handler returned %d\n",ret); 

    return ret; 
} 



#pragma managed 

namespace SEHInstaller 
{ 
    public ref class SEHInstall 
    { 
    public: 
     static void InstallHandler() 
     {  
     oldFilter_ = SetUnhandledExceptionFilter(MyExceptionFilter); 
     printf("Installed handler old=%x\n",oldFilter_); 
     } 


    }; 
} 

risposta

9

Windows Form ha un gestore di eccezioni built-in che fa la seguente per impostazione predefinita:

  • catture un'eccezione gestita gestita quando:
    • alcun debugger attaccato, e
    • eccezione si verifica durante la finestra elaborazione dei messaggi e
    • jitDebugging = false in App.Config.
  • Mostra la finestra di dialogo all'utente e impedisce la terminazione dell'app.

È possibile disattivare il primo comportamento impostando jitDebugging = true in app.config. Ciò significa che l'ultima possibilità di interrompere l'interruzione dell'app è catturare l'eccezione non gestita registrando l'evento Application.ThreadException, ad es. in C#:

Application.ThreadException += new Threading.ThreadExceptionHandler(CatchFormsExceptions); 

Se si decide di non intercettare l'eccezione non gestita qui, allora si avrà bisogno di controllare e/o modificare l'impostazione DbgJitDebugLaunchSetting in HKLM \ Software.NetFramework Registro di sistema.Questo è uno dei tre valori di cui sono a conoscenza:

  • 0: mostra la finestra di dialogo utente che richiede "debug o termina".
  • 1: consente di eseguire un'eccezione per CLR.
  • 2: avvia il debugger specificato nella chiave di registro DbgManagedDebugger.

In Visual Studio, andare in Strumenti> Opzioni> Debug> JIT per impostare questa chiave su 0 o 2. Ma un valore pari a 1 è di solito quello che si vuole sulla macchina di un utente finale. Si noti che questa chiave del Registro di sistema viene applicata prima dell'evento di eccezione non gestito CLR di cui si discute.

Quindi è possibile impostare il filtro delle eccezioni nativo di cui si è parlato.

2

SetUnhandledExceptionFilter installa un gestore che viene invocato quando un Win32 excpetion raggiunge la sommità del filato CallStack senza essere gestite.

In molti runtime linguistici, incluso gestito, le eccezioni di lingua vengono implementate utilizzando le eccezioni Win32. Ma il runtime gestito avrà un blocco __try __catch (...) di primo livello nella parte superiore di ogni thread che catturerà qualsiasi win32 in eccezioni di runtime ed elaborerà senza farli scappare al gestore di primo livello di Win32.

La conoscenza del runtime specifico sarebbe necessaria per iniettare un gestore a questo livello poiché le eccezioni non saranno mai consentite per l'escape al gestore TheadProc di Win32.

4

Se si desidera che le eccezioni filo GUI di lavorare proprio come quelli your-non GUI, in modo che essi ottenere trattati allo stesso modo, si può fare questo:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); 

Ecco lo sfondo:

in un'applicazione GUI manged, per impostazione predefinita, le eccezioni provenienti nel thread GUI sono gestite da tutto ciò che è assegnato al Application.ThreadException, che è possibile personalizzare in questo modo:

Application.ThreadException += 
    new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); 

Le eccezioni che hanno origine negli altri thread sono gestite da AppDomain.CurrentDomain.UnhandledException, che è possibile personalizzare in questo modo:

AppDomain.CurrentDomain.UnhandledException += 
    new UnhandledExceptionEventHandler(Program.CurrentDomain_UnhandledException); 

Assegnazione di UnhandledException funziona esattamente come chiamare Win32 SetUnhandledExceptionFilter.

Se l'obiettivo è creare minidumps e quindi utilizzarli, è necessario utilizzare Debugging Tools per Windows, sos.dll. Avrai bisogno di produrre minidump MiniDumpWithFullMemory.

E quindi, anche allora, probabilmente non avrai tutto ciò che potresti desiderare. System.Diagnostics.StackTrace per ottenere lo stack delle chiamate gestite dalla chiamata.