Windows 7下MiniDumpWriteDump执行失败及未知异常码问题求助
Windows 7下MiniDumpWriteDump调用失败问题排查与解决
问题背景
需要实现Windows系统运行进程的转储收集功能,依赖MiniDumpWriteDump函数。该函数在Windows 7之前的系统运行正常,但在Windows 7上调用返回值为0,转储生成失败。此前针对Windows 7设置exceptionInfo.ClientPointers = TRUE,旧系统设为FALSE,但当前通过EXCEPTION_POINTERS获取到的错误码148696056未在官方文档中定义。
原实现代码:
for( const auto& process : GetActiveProcesses() ) { if( process.ExeFile() == mProcessName ) { try { String dump_name = GetDumpFileName( process.ProcessID() ); bfs::wpath dump_path = assembly_directory / dump_name; MINIDUMP_EXCEPTION_INFORMATION exceptionInfo = { 0 }; if( IsWindows8OrGreater() ) exceptionInfo.ClientPointers = FALSE; else exceptionInfo.ClientPointers = TRUE; HANDLE hProcess = ::OpenProcess( PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, process.ProcessID() ); exceptionInfo.ThreadId = GetThreadId(hProcess) ; EXCEPTION_POINTERS exceptionPointers; exceptionInfo.ExceptionPointers = &exceptionPointers; if( hProcess == INVALID_HANDLE_VALUE ) { // error log } HANDLE hDumpFile = ::CreateFile( dump_path.wstring().c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if( hDumpFile == INVALID_HANDLE_VALUE ) { ::CloseHandle( hProcess ); // error log } auto res = ::MiniDumpWriteDump( hProcess, process.ProcessID(), hDumpFile, ( MINIDUMP_TYPE )( MiniDumpNormal | MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithDataSegs | MiniDumpWithFullMemory ), &exceptionInfo, nullptr, nullptr ); ::CloseHandle( hProcess ); ::CloseHandle( hDumpFile ); if( res ) { // work with dump } else { bfs::remove( dump_path ); DWORD exceptionCode = exceptionInfo.ExceptionPointers->ExceptionRecord->ExceptionCode; String result = L""; switch (exceptionCode) { case EXCEPTION_ACCESS_VIOLATION: result = L"Access Violation Exception"; break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: result = L"Array Bounds Exceeded Exception"; break; case EXCEPTION_BREAKPOINT: result = L"Breakpoint Exception"; break; case EXCEPTION_DATATYPE_MISALIGNMENT: result = L"Datatype Misalignment Exception"; break; case EXCEPTION_FLT_DENORMAL_OPERAND: result = L"Floating Point Denormal Operand Exception"; break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: result = L"Floating Point Divide By Zero Exception"; break; case EXCEPTION_ILLEGAL_INSTRUCTION: result = L"Illegal Instruction Exception"; break; case EXCEPTION_IN_PAGE_ERROR: result = L"In Page Error Exception"; break; case EXCEPTION_INT_DIVIDE_BY_ZERO: result = L"Integer Divide By Zero Exception"; break; case EXCEPTION_INVALID_DISPOSITION: result = L"Invalid Disposition Exception"; break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: result = L"Noncontinuable Exception"; break; case EXCEPTION_PRIV_INSTRUCTION: result = L"Privileged Instruction Exception"; break; case EXCEPTION_STACK_OVERFLOW: result = L"Stack Overflow Exception"; break; default: result = L"Unknown Exception"; break; } ErrorMsg( Format( L"Error type: %1%"_sv, result ) ); } } catch( ... ) { // error log } } }
核心问题分析
EXCEPTION_POINTERS未初始化:代码中声明了EXCEPTION_POINTERS但未赋值,直接传入MiniDumpWriteDump会导致读取未初始化内存,得到的错误码是随机无效值。GetThreadId参数错误:GetThreadId需要传入线程句柄,代码中传入的是进程句柄hProcess,导致ThreadId值完全错误。- 进程权限不足:
OpenProcess仅请求了PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,生成全内存转储需要PROCESS_VM_OPERATION、PROCESS_DUP_HANDLE等额外权限。 - 错误码获取方式错误:非异常上下文下,
EXCEPTION_POINTERS无效,应该用GetLastError()获取系统真实错误码。 - 转储类型冗余:
MiniDumpWithFullMemory已包含MiniDumpNormal等类型的内容,重复指定可能引发兼容性问题。
修正后的代码
for( const auto& process : GetActiveProcesses() ) { if( process.ExeFile() == mProcessName ) { try { String dump_name = GetDumpFileName( process.ProcessID() ); bfs::wpath dump_path = assembly_directory / dump_name; MINIDUMP_EXCEPTION_INFORMATION exceptionInfo = { 0 }; // 非异常上下文下,ExceptionPointers设为NULL即可,无需设置ClientPointers exceptionInfo.ThreadId = GetCurrentThreadId(); // 若需目标进程线程,需枚举其线程列表 // 请求转储所需的完整权限 HANDLE hProcess = ::OpenProcess( PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_DUP_HANDLE, FALSE, process.ProcessID() ); if( hProcess == INVALID_HANDLE_VALUE ) { // 记录GetLastError()获取的权限错误 continue; } HANDLE hDumpFile = ::CreateFile( dump_path.wstring().c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if( hDumpFile == INVALID_HANDLE_VALUE ) { ::CloseHandle( hProcess ); // 记录文件创建错误 continue; } // 简化转储类型,避免冗余 auto res = ::MiniDumpWriteDump( hProcess, process.ProcessID(), hDumpFile, (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithDataSegs), nullptr, // 无异常时传入NULL nullptr, nullptr ); ::CloseHandle( hProcess ); ::CloseHandle( hDumpFile ); if( res ) { // 处理转储文件 } else { bfs::remove( dump_path ); DWORD lastError = GetLastError(); String result; switch (lastError) { case ERROR_ACCESS_DENIED: result = L"权限不足,无法访问目标进程"; break; case ERROR_INVALID_HANDLE: result = L"无效的进程或文件句柄"; break; case ERROR_NOT_ENOUGH_MEMORY: result = L"系统内存不足,无法生成转储"; break; default: result = Format(L"未知错误,错误码: %lu", lastError); break; } ErrorMsg( result ); } } catch( ... ) { // 记录异常信息 } } }
关键修复说明
- 移除无效的
EXCEPTION_POINTERS:非异常场景下,直接将exceptionInfo.ExceptionPointers设为NULL,避免读取未初始化内存。 - 修正
ThreadId赋值:若生成当前进程转储,用GetCurrentThreadId();若需目标进程线程,需通过CreateToolhelp32Snapshot枚举其线程列表获取有效ID。 - 补充进程权限:添加
PROCESS_VM_OPERATION和PROCESS_DUP_HANDLE,满足全内存转储的权限要求。 - 改用
GetLastError()获取错误:MiniDumpWriteDump失败时,操作系统会设置错误码,调用GetLastError()才能拿到真实的失败原因。 - 简化转储类型:保留
MiniDumpWithFullMemory和MiniDumpWithDataSegs即可,减少冗余配置引发的兼容性问题。
内容的提问来源于stack exchange,提问作者TheKilAnt
相关产品推荐
相关产品推荐

