Désolé de n’avoir rien écrit d’intéressant ce mois-ci jusqu’à présent, ce fut un mois très mouvementé.

Aujourd’hui, j’ai vu une question sur Stack Overflow concernant le débogage. L’utilisateur essayait de laisser Visual Studio s’exécuter en mode débogage avec son application pour voir quand et pourquoi il se bloque.

Ce n’est pas une solution car il existe des fonctions pour ces situations dans l’API Windows. Plus simplement, vous pouvez faire en sorte que votre logiciel écrive un Mini Dump en cas de plantage, puis vous pouvez simplement l’ouvrir dans Visual Studio (ou le débogueur de votre choix) et voir exactement où il s’est planté.

L’exemple ci-dessous est prêt à être utilisé pour n’importe quel .exe ou .dll sous Windows:

 1#pragma once
 2
 3#include <Windows.h>
 4#include <Dbghelp.h>
 5#pragma comment (lib, "Dbghelp.lib")
 6
 7// Because... why not ?!
 8namespace MecanikUtils
 9{
10    LPTOP_LEVEL_EXCEPTION_FILTER PreviousExceptionFilter = 0;
11
12    LONG WINAPI DumpExceptionFilter(EXCEPTION_POINTERS* info)
13    {
14        wchar_t DumpPath[MAX_PATH] = { 0 };
15
16        SYSTEMTIME SystemTime;
17        GetLocalTime(&SystemTime);
18
19        WCHAR szExeFileName[100] = { 0 };
20        GetModuleFileNameW(NULL, szExeFileName, 99);
21
22        wsprintfW(DumpPath, L"Crash_%s_%d-%d-%d_%dh%dm%ds.dmp", szExeFileName, SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond);
23
24        HANDLE file = CreateFileW(DumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
25
26        if (file != INVALID_HANDLE_VALUE)
27        {
28            MINIDUMP_EXCEPTION_INFORMATION mdei;
29
30            mdei.ThreadId = (DWORD)GetCurrentThreadId();
31
32            mdei.ExceptionPointers = info;
33
34            mdei.ClientPointers = 0;
35
36            if (MiniDumpWriteDump(GetCurrentProcess(), (DWORD)GetCurrentProcessId(), file, (MINIDUMP_TYPE)(MiniDumpScanMemory + MiniDumpWithIndirectlyReferencedMemory), &mdei, 0, 0) != 0)
37            {
38                CloseHandle(file);
39                return EXCEPTION_EXECUTE_HANDLER;
40            }
41        }
42
43        CloseHandle(file);
44
45        return EXCEPTION_CONTINUE_SEARCH;
46    }
47
48    class MiniDump
49    {
50    public:
51
52        /// <summary>
53        /// Add Unhandled Exception Filter
54        /// </summary>
55        inline static void Init()
56        {
57            // Change if required
58            SetErrorMode(SEM_FAILCRITICALERRORS);
59
60            PreviousExceptionFilter = SetUnhandledExceptionFilter(DumpExceptionFilter);
61        }
62
63        /// <summary>
64        /// Remove Unhandled Exception Filter
65        /// </summary>
66        inline static void Clean()
67        {
68            SetUnhandledExceptionFilter(PreviousExceptionFilter);
69        }
70    };
71}

Pour l’utiliser, appelez simplement ce qui suit au début et à la fin du point d’entrée de votre logiciel. Par exemple un .dll :

 1#include "MiniDump.hpp"
 2
 3BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
 4{
 5   switch ( ul_reason_for_call )
 6   {
 7      case DLL_PROCESS_ATTACHED:
 8      MecanikUtils::MiniDump::Init();
 9      break;
10      
11      case DLL_PROCESS_DETACH:
12      MecanikUtils::MiniDump::Clean();
13      break;
14   }
15   return TRUE;
16}

N’hésitez pas à apporter des modifications à SetErrorMode si nécessaire.

Remarque: Conservez le fichier .pdb d’origine du fichier .exe/.dll compilé si vous souhaitez ouvrir le Mini Dump. Sinon, vous n’aurez aucune information facile à lire.

Jusqu’à la prochaine fois! 😉