近日在处理 C++ Win32 程序异常时,采用 Minidump 来保存程序崩溃时的栈记录,生成的 dmp 文件保存在配置数据目录下。 如果程序启动时检查发现存在 dmp 文件,则弹出提示框让用户选择路径来保存该文件,主要逻辑如下所示:
1LONG WINAPI unhandled_handler(EXCEPTION_POINTERS* e) {
2 const wstring dumpfile = LAppDefine::documentPath + L"/minidump.dmp";
3 HANDLE hFile = CreateFile(dumpfile.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4 if (hFile && (hFile != INVALID_HANDLE_VALUE)) {
5 MINIDUMP_EXCEPTION_INFORMATION mdei;
6 mdei.ThreadId = GetCurrentThreadId();
7 mdei.ExceptionPointers = e;
8 mdei.ClientPointers = FALSE;
9
10 MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
11 hFile, MiniDumpNormal, &mdei, NULL, NULL);
12 CloseHandle(hFile);
13 }
14 return EXCEPTION_CONTINUE_SEARCH;
15}
16
17int main() {
18 SetUnhandledExceptionFilter(unhandled_handler);
19 // ...
20 if (std::filesystem::exists(std::filesystem::path(filepath))) {
21 OPENFILENAME ofn; // Common dialog box structure
22 wchar_t szFile[260] = L"minidump.dmp\0"; // Buffer for file name
23
24 ZeroMemory(&ofn, sizeof(ofn));
25 ofn.lStructSize = sizeof(ofn);
26 ofn.hwndOwner = nullptr;
27 ofn.lpstrFile = szFile;
28 ofn.nMaxFile = sizeof(szFile) / sizeof(wchar_t);
29 ofn.lpstrFilter = L"Dump Files\0*.dmp\0";
30 ofn.nFilterIndex = 1;
31 ofn.lpstrFileTitle = NULL;
32 ofn.nMaxFileTitle = 0;
33 ofn.lpstrInitialDir = NULL;
34 ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
35
36 if (GetSaveFileName(&ofn) == TRUE) {
37 CopyFile(filepath.c_str(), ofn.lpstrFile, TRUE);
38 DeleteFile(filepath.c_str());
39 }
40 }
41 // ...
42 struct stat statBuf {};
43 if (stat(relative_path, &statBuf) == 0) {
44 //...
45 }
46}
实现后程序出现了奇怪的问题,当有崩溃报告存在时,后续在通过 stat 获取其他文件时必然失败,因此还导致了其他地方发生崩溃,继而程序永远无法正常启动。
后来经过逐步调试,发现只要 GetSaveFileName() 调用成功(用户选定路径并确认)就会导致后续的 stat 出现问题。考虑到 stat 使用的路径为相对路径,猜测 GetSaveFileName() 会改变程序当前的工作目录,且经查询文档确认了这一点。
而 ofn.Flags 也提供了解决这一问题的方法:OFN_NOCHANGEDIR。