#include "CrashHandler.h" #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include #ifdef _MSC_VER #include #pragma comment(lib, "dbghelp.lib") #endif #endif QString CrashHandler::dumpPath; void CrashHandler::registerCrashHandler() { signal(SIGSEGV, crashHandlerFunction); // 段错误 signal(SIGABRT, crashHandlerFunction); // abort调用 signal(SIGFPE, crashHandlerFunction); // 浮点异常 signal(SIGILL, crashHandlerFunction); // 非法指令 #ifdef Q_OS_WIN SetUnhandledExceptionFilter(windowsCrashHandler); #endif } void CrashHandler::setDumpPath(const QString& path) { dumpPath = path; QDir dir(dumpPath); if (!dir.exists()) { dir.mkpath(dumpPath); } } QString CrashHandler::getDumpPath() { if (dumpPath.isEmpty()) { dumpPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/crash_dumps"; QDir dir(dumpPath); if (!dir.exists()) { dir.mkpath(dumpPath); } } return dumpPath; } #ifdef Q_OS_WIN LONG WINAPI CrashHandler::windowsCrashHandler(struct _EXCEPTION_POINTERS* exceptionInfo) { // 使用 C 函数生成时间戳,避免依赖 Qt(可能已损坏) time_t now = time(NULL); struct tm timeinfo; localtime_s(&timeinfo, &now); char timestamp[64]; strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &timeinfo); // 获取dump路径 QString dumpDir = CrashHandler::getDumpPath(); // 创建目录(如果不存在) CreateDirectoryW((WCHAR*)dumpDir.utf16(), NULL); // 构建文件路径 QString dumpFilePath = dumpDir + "/crash_dump_" + QString(timestamp) + ".dmp"; QString logFilePath = dumpDir + "/crash_log_" + QString(timestamp) + ".txt"; // 先写日志文件 FILE* logFile = _wfopen((WCHAR*)logFilePath.utf16(), L"w"); if (logFile) { fprintf(logFile, "Application crashed at: %s\n", timestamp); fprintf(logFile, "Exception Code: 0x%08X\n", exceptionInfo->ExceptionRecord->ExceptionCode); fprintf(logFile, "Exception Address: 0x%p\n", exceptionInfo->ExceptionRecord->ExceptionAddress); fprintf(logFile, "Dump file: %s\n", dumpFilePath.toLocal8Bit().constData()); fclose(logFile); } #ifdef _MSC_VER // 创建 dump 文件 HANDLE hFile = CreateFileW((WCHAR*)dumpFilePath.utf16(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { MINIDUMP_EXCEPTION_INFORMATION exceptionInfoEx; exceptionInfoEx.ThreadId = GetCurrentThreadId(); exceptionInfoEx.ExceptionPointers = exceptionInfo; exceptionInfoEx.ClientPointers = FALSE; // 使用更详细的 dump 类型 MINIDUMP_TYPE dumpType = (MINIDUMP_TYPE)( MiniDumpWithDataSegs | MiniDumpWithHandleData | MiniDumpWithThreadInfo); BOOL success = MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, dumpType, &exceptionInfoEx, NULL, NULL); CloseHandle(hFile); if (success) { // 成功写入,显示消息框 wchar_t msg[512]; swprintf_s(msg, 512, L"程序崩溃!\n\nDump文件已保存至:\n%s\n\n请联系技术支持。", (WCHAR*)dumpFilePath.utf16()); MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST); } else { DWORD error = GetLastError(); wchar_t msg[256]; swprintf_s(msg, 256, L"程序崩溃!\n\n无法创建dump文件(错误代码: %d)\n路径: %s", error, (WCHAR*)dumpFilePath.utf16()); MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST); } } else { DWORD error = GetLastError(); wchar_t msg[256]; swprintf_s(msg, 256, L"程序崩溃!\n\n无法创建dump文件(文件创建失败,错误代码: %d)\n路径: %s", error, (WCHAR*)dumpFilePath.utf16()); MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST); } #else // 非 MSVC 编译器,只显示崩溃信息 wchar_t msg[256]; swprintf_s(msg, 256, L"程序崩溃!\n\n日志文件: %s", (WCHAR*)logFilePath.utf16()); MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST); #endif return EXCEPTION_EXECUTE_HANDLER; } #endif void CrashHandler::crashHandlerFunction(int sig) { // 使用 C 函数生成时间戳,避免依赖可能已损坏的 Qt time_t now = time(NULL); struct tm timeinfo; #ifdef Q_OS_WIN localtime_s(&timeinfo, &now); #else localtime_r(&now, &timeinfo); #endif char timestamp[64]; strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &timeinfo); QString dumpDir = getDumpPath(); QString dumpFilePath = dumpDir + "/crash_signal_" + QString(timestamp) + ".txt"; FILE* file = fopen(dumpFilePath.toLocal8Bit().constData(), "w"); if (file) { fprintf(file, "Crash occurred at: %s\n", timestamp); fprintf(file, "Signal: %d (", sig); switch(sig) { case SIGSEGV: fprintf(file, "SIGSEGV - Segmentation fault"); break; case SIGABRT: fprintf(file, "SIGABRT - Abort signal"); break; case SIGFPE: fprintf(file, "SIGFPE - Floating point exception"); break; case SIGILL: fprintf(file, "SIGILL - Illegal instruction"); break; default: fprintf(file, "Unknown signal"); break; } fprintf(file, ")\n"); if (qApp) { fprintf(file, "Application: %s\n", qApp->applicationName().toLocal8Bit().constData()); fprintf(file, "Version: %s\n", qApp->applicationVersion().toLocal8Bit().constData()); } fclose(file); #ifdef Q_OS_WIN wchar_t msg[512]; swprintf_s(msg, 512, L"程序崩溃(信号 %d)!\n\n日志文件已保存至:\n%s", sig, (WCHAR*)dumpFilePath.utf16()); MessageBoxW(NULL, msg, L"程序崩溃", MB_OK | MB_ICONERROR | MB_TOPMOST); #endif } // 调用默认的信号处理函数来终止程序 signal(sig, SIG_DFL); raise(sig); }