SetUnhandledExceptionFilter und MiniDumpWriteDump



  • Hallo!

    Da es mir jetzt schon mehrmals passiert ist, die Minidumps von Anwendungen vor dem Löschen zu sichern wollte ich mein Programm eine Funktion einbauen, die beim Absturz selber Minidumps erstellt.

    1.) Hierzu würde ich gerne wissen, wie SetUnhandledExceptionFilter und MiniDumpWriteDump funktionieren bzw. wie man am besten vorgeht.

    2.) Was ist der Unterschied zwischen einer Dump Datei vom TaskManager und dem von Windows erzeugten Minidump (außer die Größe)?

    3.) Schaltet sich ab Vista/7 immer noch die "Problem senden" Funktion ein auch wenn ich SetUnhandledExceptionFilter verwende?

    Gruß
    Jörg



  • zu 1:
    z.B. so:

    #include <windows.h>
    #include <dbghelp.h>
    
    typedef BOOL (__stdcall *tMDWD)(
      IN HANDLE hProcess,
      IN DWORD ProcessId,
      IN HANDLE hFile,
      IN MINIDUMP_TYPE DumpType,
      IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
      IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
      IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
      );
    
    static tMDWD s_pMDWD;
    static HMODULE s_hDbgHelpMod;
    static MINIDUMP_TYPE s_dumpTyp = MiniDumpNormal;
    static LPCTSTR s_szMiniDumpFileName = _T("test.dmp");  // initialize with whatever appropriate...
    
    static LONG __stdcall MyCrashHandlerExceptionFilter(EXCEPTION_POINTERS* pEx)
    {  
    #ifdef _M_IX86
      if (pEx->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)  
      {
        // be sure that we have enought space...
        static char MyStack[1024*128];  
        // it assumes that DS and SS are the same!!! (this is the case for Win32)
        // change the stack only if the selectors are the same (this is the case for Win32)
        //__asm push offset MyStack[1024*128];
        //__asm pop esp;
        __asm mov eax,offset MyStack[1024*128];
        __asm mov esp,eax;
      }
    #endif
      bool bFailed = true;
      HANDLE hFile;
      hFile = CreateFile(s_szMiniDumpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
      if (hFile != INVALID_HANDLE_VALUE)
      {
        MINIDUMP_EXCEPTION_INFORMATION stMDEI;
        stMDEI.ThreadId = GetCurrentThreadId();
        stMDEI.ExceptionPointers = pEx;
        stMDEI.ClientPointers = TRUE;
        // try to create an miniDump:
        if (s_pMDWD(
          GetCurrentProcess(),
          GetCurrentProcessId(),
          hFile, 
          s_dumpTyp,
          &stMDEI,
          NULL,
          NULL
          ))
        {
          bFailed = false;  // suceeded
        }
        CloseHandle(hFile);
      }
    
      if (bFailed)
      {
        return EXCEPTION_CONTINUE_SEARCH;
      }
    
      // Optional display an error message
      FatalAppExit(-1, _T("Application failed!"));
    
      // or return one of the following:
      // - EXCEPTION_CONTINUE_SEARCH
      // - EXCEPTION_CONTINUE_EXECUTION
      // - EXCEPTION_EXECUTE_HANDLER
      return EXCEPTION_CONTINUE_SEARCH;  // this will trigger the "normal" OS error-dialog
    }
    
    void InitMiniDumpWriter()
    {
      if (s_hDbgHelpMod != NULL)
        return;
    
      // Initialize the member, so we do not load the dll after the exception has occured 
      // which might be not possible anymore...
      s_hDbgHelpMod = LoadLibrary(_T("dbghelp.dll"));
      if (s_hDbgHelpMod != NULL)
        s_pMDWD = (tMDWD) GetProcAddress(s_hDbgHelpMod, "MiniDumpWriteDump");
    
      // Register Unhandled Exception-Filter:
      SetUnhandledExceptionFilter(MyCrashHandlerExceptionFilter);
    
      // Additional call "PreventSetUnhandledExceptionFilter"...
      // See also: "SetUnhandledExceptionFilter" and VC8 (and later)
      // http://blog.kalmbachnet.de/?postid=75
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
      InitMiniDumpWriter();
    
      char *szTemp = (char*) 0x1;
      strcmp(szTemp, "12");
    
    	return 0;
    }
    

    zu 2: Keiner

    zu 3: Das hängt davon ab, was Du zurückgibst... wenn Du "EXCEPTION_CONTINUE_SEARCH" zurück gibst, dann ja...



  • Ich denke du solltest MyCrashHandlerExceptionFilter mit einem hübschen Spin-Lock absichern, falls mal zwei Threads gleichzeitig da reinkommen. Sonst wollen die nämlich den selben Bereich als Stack verwenden (x86).



  • Schnik Schnak...



  • hihi 🙂



  • Vielen Dank für die Infos Jochen.

    Muss mein Programm die dbghelp.dll mus ausliefern oder ist die ab Windows XP dabei?

    Grüsse
    Jörg


  • Mod

    Dabei... Nur für Win2K musste man die mit ausliefern.



  • Habe noch ein Frage vergessen.

    1.) Welche Typ von Minidumps erzeugt das Auffangsieb von Windows Vista bzw. Windows 7?

    2.) Reicht der Typ MiniDumpNormal, um ggf. einen Fehler zu beheben?

    Grüsse
    Jörg


  • Mod

    zu 1: MiniDumpNormal
    zu 2: Das kommt darauf an. Viele Simple Bugs lassen sich so leicht identifizieren. Callstack hast Du und genau Crash Position.
    Aber Du hast keine Daten. Du kannst Objekte nicht auf Ihre Inhalte kontrollieren. etc.

    Ich habe eine Option für meine Programme die es mir erlauben bei Bedarf einen FullDump zu erzeugen... D.h. auf der Befehlszeile oder in der Registry wird abgelegt welche Art von Dump erzeugt werden soll.

    BTW: Um Vista/Windows 7 Compatible zu sein musst Du den Crash an den Default-Handler von Windows weitergeben.



  • Martin Richter schrieb:

    zu 2: Das kommt darauf an. Viele Simple Bugs lassen sich so leicht identifizieren. Callstack hast Du und genau Crash Position.
    Aber Du hast keine Daten. Du kannst Objekte nicht auf Ihre Inhalte kontrollieren. etc.

    Gibt es noch einen "Zwischendump-Typ" der Objektinhalte enthält, aber kein FullDump ist? Also nicht ganz so groß.

    Martin Richter schrieb:

    BTW: Um Vista/Windows 7 Compatible zu sein musst Du den Crash an den Default-Handler von Windows weitergeben.

    Das wird imho ja erreicht in dem ich "EXCEPTION_CONTINUE_SEARCH" zurückgebe, korrekt?

    Noch ein Frage:
    Ich verstehe Jochens Blog Beitrag nicht ganz. Ich verwende VS2008 später dann VS2010. Meine Anwendung liegt als x86 und x64 Anwendung vor. Muss ich jetzt etwas beachten?

    Grüsse
    Jörg


  • Mod

    Lies die Doku und experimentiere.
    MiniDumpWithDataSegs!

    Ich benutze nur entweder Normal oder WithFullMemory...



  • Meine Kommentare zu den letzten Einträgen:

    Ich enpfehle Dir die dbghelp.dll mit auszuliefern... die von XP mitgelieferte ist nicht auf dem aktuellen Stand... aber über dieses Thema kann man lange diskutieren... wenn es sich bei Deiner Anwendung um ein reines native-Programm handelt, dann brauchst Du sie nicht auszuliefern, da die von XP ausreichend ist... wenn es aber auch managed Code enthält, dann ist die OS DLL zu alt...

    Und ob Du ab VS2008 etwas beachten musst: Ja 😉 ruf einfach das "PreventSetUnhandledExceptionFilter" auf, dann hast Du alles beachtet 😉

    Und der Vista/Win7 TaskManager erstellt ein FullDump

    Und ein "Auffangsieb" von Vista/Win7 erzeugt AFAIK gar kein MiniDump; es sei denn, Du hast Dein Programm bei WinQual angemeldet...



  • Jochen Kalmbach schrieb:

    Und ob Du ab VS2008 etwas beachten musst: Ja 😉 ruf einfach das "PreventSetUnhandledExceptionFilter" auf, dann hast Du alles beachtet 😉

    Im Deinem Blog schreibst Du "You can achieve this for x86 with the following code" und prüfst das Ganze auch noch mit dem Präprozessor. Meine Anwendung existiert aber auch als native x64 Anwendung.

    Jochen Kalmbach schrieb:

    Und ein "Auffangsieb" von Vista/Win7 erzeugt AFAIK gar kein MiniDump; es sei denn, Du hast Dein Programm bei WinQual angemeldet...

    Der Dialog "Awendung ist abgeschmiert..." und Fehlerbericht senden kommt immer. Man hat nur keinen Zugriff auf die gesendeten, wenn man bei WinQual nicht angemeldet ist. 😉

    Grüsse
    Jörg



  • Jörg2010 schrieb:

    Im Deinem Blog schreibst Du "You can achieve this for x86 with the following code" und prüfst das Ganze auch noch mit dem Präprozessor. Meine Anwendung existiert aber auch als native x64 Anwendung.

    Ja, das ist noch ein TODO 😉



  • Hi Jochen,

    sag mal bei deinem Source mit dem Call

    RaiseException(EXCEPTION_STACK_OVERFLOW,0,0,0);
    

    Kommt am ende des exception handlers die meldung:

    MsgBox schrieb:

    Run-TimeCheck Failure#0 - The value of ESP was not properly saved across a
    function call.

    Sollte man da nicht noch ESP lokal sichern??

    greetz
    -----------------------------------------
    Edit: so funzt's

    //...
    static DWORD cEsp;
    
    #ifdef EXCEPTION_HANDLER_STACK_RESERVE
    #ifdef _M_IX86
    if(pEx->... == EXCEPTION_STACK_OVERFLOW)
    {
      EnterCriticalSection(csException)
      static char MyStack[1024*196];
      __asm mov eax, offset MyStack[1024*196];
      __asm mov cEsp,esp;
      __asm mov esp,eax;
    }
    #endif
    #endif
    //...
    
    //on conditional or default return
    #ifdef EXCEPTION_HANDLER_STACK_RESERVE
    #ifdef _M_IX86
    if(pEx->... == EXCEPTION_STACK_OVERFLOW)
    {
       __asm mov esp,cEsp;
       LeaveCriticalSection(csException);
    }
    #endif
    #endif
    
    return EXCEPTION_EXECUTE_HANDLER
    


  • WIE hast Du denn Dein Projekt kompiliert!? /EHsc !?

    Dan stell mal um nach /EHa



  • Jo,..
    ich hatte es auf /EHsc,..

    thx


Anmelden zum Antworten