Scusate se non ho scritto nulla di interessante questo mese finora, è stato un mese molto frenetico.

Oggi ho visto una domanda su Stack Overflow riguardo al debugging. L’utente stava cercando di far funzionare Visual Studio in modalità debug con la sua applicazione per vedere quando e perché si blocca.

Quella non è una soluzione perché esistono funzioni nell’API di Windows per queste situazioni. Più semplicemente, potete fare in modo che il vostro software scriva un Mini Dump al crash e poi potete semplicemente aprirlo in Visual Studio (o qualsiasi debugger preferiate) e vedere esattamente dove si è bloccato.

L’esempio seguente è pronto all’uso per qualsiasi .exe o .dll su 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{
10LPTOP_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
72}

Per utilizzarlo, basta chiamare quanto segue all’inizio e alla fine del punto di ingresso del vostro software. Per esempio una .dll:

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

Sentitevi liberi di apportare modifiche a SetErrorMode se necessario.

Nota: Conservate il file .pdb originale dell’.exe/.dll compilato se volete aprire il Mini Dump. Altrimenti non avrete informazioni facilmente leggibili.

Alla prossima! 😉