很抱歉這個月到目前為止沒有寫任何有趣的東西,這是一個非常忙碌的月份。

今天看到一個問題 Stack Overflow 關於調試。用戶試圖讓 Visual Studio 在調試模式下與他的應用程序一起運行,以查看它何時以及為何崩潰。

這不是一個解決方案,因為 Windows API 中有針對這些情況的函數。更簡單地說,您可以讓您的軟件在崩潰時編寫一個 Mini Dump,然後您可以在 Visual Studio(或您想要的任何調試器)中打開它並查看它崩潰的確切位置。

以下示例可用於 Windows 上的任何 .exe 或 .dll:

 1#pragma once
 2
 3#include <Windows.h>
 4#include <Dbghelp.h>
 5#pragma comment (lib, "Dbghelp.lib")
 6
 7// 因為……為什麼不呢?!
 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        /// 添加未處理的異常過濾器
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        /// 刪除未處理的異常過濾器
65        /// </summary>
66        inline static void Clean()
67        {
68            SetUnhandledExceptionFilter(PreviousExceptionFilter);
69        }
70    };
71}

要使用它,只需在軟件入口點的開始 + 結束處調用以下命令。例如一個 .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}

隨意更改 SetErrorMode 如果你需要。

注意: 如果要打開 Mini Dump,請保留已編譯的 .exe/.dll 的原始 .pdb。否則,您將沒有任何易於閱讀的信息。

直到下一次! 😉