#include "stdafx.h" #include "MiniDumper.h" #include #ifndef Assert #include #define Assert assert #define LogToFile (void)(0); #endif #define ASIZE(A) (sizeof(A)/sizeof((A)[0])) cMiniDump::DumpLevel cMiniDump::ms_DumpLevel = cMiniDump::DUMP_LEVEL_LIGHT; bool cMiniDump::ms_AddTimeStamp = true; TCHAR cMiniDump::ms_AppName[_MAX_PATH] = {0,}; TCHAR cMiniDump::ms_CallStack[8192] = {0,}; TCHAR cMiniDump::ms_Modules[8192] = {0,}; LPCTSTR cMiniDump::ms_DialogTemplate = NULL; DLGPROC cMiniDump::ms_DialogProc = NULL; namespace { /// \brief ¿¹¿ÜÀÇ ¿øÀο¡ ´ëÇÑ ¹®ÀÚ¿­À» ¹ÝȯÇÑ´Ù. LPCTSTR GetFaultReason(PEXCEPTION_POINTERS exPtrs); /// \brief »ç¿ëÀÚ Á¤º¸¸¦ ¹ÝȯÇÑ´Ù. LPCTSTR GetUserInfo(); /// \brief À©µµ¿ìÁî ¹öÀüÀ» ¹ÝȯÇÑ´Ù. LPCTSTR GetOSInfo(); /// \brief CPU Á¤º¸¸¦ ¹ÝȯÇÑ´Ù. LPCTSTR GetCpuInfo(); /// \brief ¸Þ¸ð¸® Á¤º¸¸¦ ¹ÝȯÇÑ´Ù. LPCTSTR GetMemoryInfo(); /// \brief À©µµ¿ìÁî ¹öÀüÀ» ¾Ë¾Æ³½´Ù. bool GetWinVersion(LPTSTR pszVersion, int *nVersion, LPTSTR pszMajorMinorBuild); /// \brief strrchr TCHAR ¹öÀü TCHAR* lstrrchr(TCHAR* str, TCHAR ch); } //////////////////////////////////////////////////////////////////////////////// /// \brief ¹Ì´Ï ´ýÇÁ ±â´ÉÀ» ÃʱâÈ­ÇÑ´Ù. /// \param dumpLevel ´ýÇÁ ·¹º§ /// \param addTimeStamp ´ýÇÁ ÆÄÀÏ À̸§¿¡´Ù°¡ ´ýÇÁ ÆÄÀÏÀÌ »ý¼ºµÈ ³¯Â¥¸¦ /// Áý¾î³Ö´Â°¡ÀÇ ¿©ºÎ. /// \param dialogTemplate ´ëȭâ ÅÛÇø´ /// \param dialogProc ´ëȭâ ÇÁ·Î½ÃÁ® //////////////////////////////////////////////////////////////////////////////// void cMiniDump::Install( DumpLevel dumpLevel, bool addTimeStamp, LPCTSTR dialogTemplate, DLGPROC dialogProc) { Assert(ms_AppName[0] == 0); Assert(dumpLevel >= DUMP_LEVEL_LIGHT); Assert(dumpLevel <= DUMP_LEVEL_HEAVY); ms_DumpLevel = dumpLevel; ms_AddTimeStamp = addTimeStamp; ms_DialogTemplate = dialogTemplate; ms_DialogProc = dialogProc; // ¸ðµâ °æ·Î¸¦ ¾Ë¾Æ³½´Ù. // C:\somewhere\something.exe TCHAR szFileName[_MAX_PATH]; ::GetModuleFileName(NULL, szFileName, _MAX_PATH); // È®ÀåÀÚ¸¦ Á¦°ÅÇÑ ¸ðµâ °æ·Î¸¦ ÁغñÇØµÐ´Ù. // C:\somewhere\something.exe -> C:\somewhere\something TCHAR* dot = lstrrchr(szFileName, '.'); ::lstrcpyn(ms_AppName, szFileName, (int)(dot - szFileName + 1)); dot = ::_tcsrchr(ms_AppName, _T('\\')); ::lstrcpyn(ms_AppName, &dot[1], ::_tcslen(ms_AppName) ); // ¿¹¿Ü ó¸® Çڵ鷯¸¦ ¼³Á¤ÇÑ´Ù. ::SetUnhandledExceptionFilter(WriteDump); } //////////////////////////////////////////////////////////////////////////////// /// \brief ¿¹¿Ü¿¡ ´ëÇÑ Á¤º¸¸¦ ¹Þ¾Æ¼­, ¹Ì´Ï ´ýÇÁ ÆÄÀÏÀ» »ý¼ºÇÑ´Ù. /// /// SetUnhandledExceptionFilter() API¿¡ ÀÇÇØ¼­ ¼³Á¤µÇ°í, ÇÁ·Î¼¼½º ³»ºÎ¿¡¼­ /// Unhandled ExceptionÀÌ ¹ß»ýµÉ °æ¿ì, È£ÃâµÇ°Ô µÈ´Ù. ´Ü µð¹ö°Å°¡ ºÙ¾îÀÖ´Â /// °æ¿ì, Unhandled Exception Filter´Â È£ÃâµÇÁö ¾Ê´Â´Ù. ÀÌ ¸»Àº ÀÌ ÇÔ¼ö /// ³»ºÎ¸¦ µð¹ö±ëÇÒ ¼ö´Â ¾ø´Ù´Â ¸»ÀÌ´Ù. ÀÌ ÇÔ¼ö ³»ºÎ¸¦ µð¹ö±ëÇϱâ À§Çؼ­´Â /// ¸Þ½ÃÁö ¹Ú½º ¶Ç´Â ÆÄÀÏÀ» ÀÌ¿ëÇØ¾ßÇÑ´Ù. /// /// \param exPtrs ¿¹¿Ü Á¤º¸ /// \return LONG ÀÌ ÇÔ¼ö¸¦ ½ÇÇàÇÏ°í ³­ ´ÙÀ½, ÃëÇÒ Çൿ°ª. ÀÚ¼¼ÇÑ °ÍÀº SEH /// ¹®¼­¸¦ Âü°íÇϵµ·Ï. //////////////////////////////////////////////////////////////////////////////// LONG WINAPI cMiniDump::WriteDump(PEXCEPTION_POINTERS exPtrs) { // based on dbghelp.h typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)( HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam ); #ifdef UNICODE fwprintf(stderr, L"=============================================\n" L"unhandled excetpion triggerd! writing dump...\n" L"=============================================\n" ); #else fprintf(stderr, "=============================================\n" "unhandled excetpion triggerd! writing dump...\n" "=============================================\n" ); #endif // ´ëȭâÀÌ ¼³Á¤µÇ¾î ÀÖ´Ù¸é º¸¿©ÁØ´Ù. if (ms_DialogTemplate != NULL && ms_DialogProc != NULL) { if (DialogBox(NULL, ms_DialogTemplate, HWND_DESKTOP, ms_DialogProc) != IDOK) return EXCEPTION_EXECUTE_HANDLER; } HMODULE hDLL = NULL; TCHAR szDbgHelpPath[_MAX_PATH] = {0, }; TCHAR szDumpPath[MAX_PATH * 2] = {0,}; // ´ýÇÁ ÆÄÀÏ À̸§ += ½Ã°£ ¹®ÀÚ¿­ ::lstrcat(szDumpPath, ms_AppName); if (ms_AddTimeStamp) { SYSTEMTIME t; ::GetLocalTime(&t); TCHAR szTail[_MAX_PATH]; #ifdef UNICODE swprintf(szTail, ASIZE(szTail)-1, L" %04d-%02d-%02d %02d-%02d-%02d", t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); #else _snprintf(szTail, ASIZE(szTail)-1, _TRUNCATE, " %04d-%02d-%02d %02d-%02d-%02d", t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); #endif ::lstrcat(szDumpPath, szTail); } ::lstrcat(szDumpPath, _T(".dmp")); // ¸ÕÀú ½ÇÇà ÆÄÀÏÀÌ ÀÖ´Â µð·ºÅ丮¿¡¼­ DBGHELP.DLLÀ» ·ÎµåÇØ º»´Ù. // Windows 2000 ÀÇ System32 µð·ºÅ丮¿¡ ÀÖ´Â DBGHELP.DLL ÆÄÀÏÀº ¹öÀüÀÌ // ¿À·¡µÈ °ÍÀÏ ¼ö Àֱ⠶§¹®ÀÌ´Ù. (ÃÖ¼Ò 5.1.2600.0 ÀÌ»óÀ̾î¾ß ÇÑ´Ù.) if (::GetModuleFileName(NULL, szDbgHelpPath, _MAX_PATH)) { if (LPTSTR slash = ::lstrrchr(szDbgHelpPath, '\\')) { ::lstrcpy(slash + 1, _T("DBGHELP.DLL")); hDLL = ::LoadLibrary(szDbgHelpPath); } } // ÇöÀç µð·ºÅ丮¿¡ ¾ø´Ù¸é, ¾Æ¹« ¹öÀüÀ̳ª ·ÎµåÇÑ´Ù. if (hDLL == NULL) hDLL = ::LoadLibrary(_T("dbghelp.dll")); // DBGHELP.DLLÀ» ãÀ» ¼ö ¾ø´Ù¸é ´õ ÀÌ»ó ÁøÇàÇÒ ¼ö ¾ø´Ù. if (hDLL == NULL) { return EXCEPTION_CONTINUE_SEARCH; } // DLL ³»ºÎ¿¡¼­ MiniDumpWriteDump API¸¦ ã´Â´Ù. MINIDUMPWRITEDUMP pfnMiniDumpWriteDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDLL, "MiniDumpWriteDump"); if (pfnMiniDumpWriteDump == NULL) { return EXCEPTION_CONTINUE_SEARCH; } // ÆÄÀÏÀ» »ý¼ºÇÑ´Ù. HANDLE hFile = ::CreateFile( szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { return EXCEPTION_CONTINUE_SEARCH; } MINIDUMP_EXCEPTION_INFORMATION ExParam; ExParam.ThreadId = ::GetCurrentThreadId(); ExParam.ExceptionPointers = exPtrs; ExParam.ClientPointers = FALSE; MINIDUMP_TYPE dumptype = MiniDumpNormal; switch (ms_DumpLevel) { case DUMP_LEVEL_LIGHT: dumptype = MiniDumpNormal; break; case DUMP_LEVEL_MEDIUM: dumptype = MiniDumpWithDataSegs; break; case DUMP_LEVEL_HEAVY: dumptype = MiniDumpWithFullMemory; break; } // ´ýÇÁ ÆÄÀÏ »ý¼º °á°ú¸¦ ·Î±× ÆÄÀÏ¿¡´Ù ±â·ÏÇÑ´Ù. if (pfnMiniDumpWriteDump( ::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, dumptype, &ExParam, NULL, NULL) == 0) { Assert(false); } ::CloseHandle(hFile); return EXCEPTION_EXECUTE_HANDLER; } namespace { /// \brief ¿¹¿ÜÀÇ ¿øÀο¡ ´ëÇÑ ¹®ÀÚ¿­À» ¹ÝȯÇÑ´Ù. /// \param exPtrs ¿¹¿Ü ±¸Á¶Ã¼ Æ÷ÀÎÅÍ /// \return LPCTSTR ¿øÀÎ ¹®ÀÚ¿­ LPCTSTR GetFaultReason(PEXCEPTION_POINTERS exPtrs) { if (::IsBadReadPtr(exPtrs, sizeof(EXCEPTION_POINTERS))) return _T("bad exception pointers"); // °£´ÜÇÑ ¿¡·¯ ÄÚµå¶ó¸é ±×³É º¯È¯ÇÒ ¼ö ÀÖ´Ù. switch (exPtrs->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: return _T("EXCEPTION_ACCESS_VIOLATION"); case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("EXCEPTION_DATATYPE_MISALIGNMENT"); case EXCEPTION_BREAKPOINT: return _T("EXCEPTION_BREAKPOINT"); case EXCEPTION_SINGLE_STEP: return _T("EXCEPTION_SINGLE_STEP"); case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("EXCEPTION_FLT_DENORMAL_OPERAND"); case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("EXCEPTION_FLT_DIVIDE_BY_ZERO"); case EXCEPTION_FLT_INEXACT_RESULT: return _T("EXCEPTION_FLT_INEXACT_RESULT"); case EXCEPTION_FLT_INVALID_OPERATION: return _T("EXCEPTION_FLT_INVALID_OPERATION"); case EXCEPTION_FLT_OVERFLOW: return _T("EXCEPTION_FLT_OVERFLOW"); case EXCEPTION_FLT_STACK_CHECK: return _T("EXCEPTION_FLT_STACK_CHECK"); case EXCEPTION_FLT_UNDERFLOW: return _T("EXCEPTION_FLT_UNDERFLOW"); case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("EXCEPTION_INT_DIVIDE_BY_ZERO"); case EXCEPTION_INT_OVERFLOW: return _T("EXCEPTION_INT_OVERFLOW"); case EXCEPTION_PRIV_INSTRUCTION: return _T("EXCEPTION_PRIV_INSTRUCTION"); case EXCEPTION_IN_PAGE_ERROR: return _T("EXCEPTION_IN_PAGE_ERROR"); case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("EXCEPTION_ILLEGAL_INSTRUCTION"); case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("EXCEPTION_NONCONTINUABLE_EXCEPTION"); case EXCEPTION_STACK_OVERFLOW: return _T("EXCEPTION_STACK_OVERFLOW"); case EXCEPTION_INVALID_DISPOSITION: return _T("EXCEPTION_INVALID_DISPOSITION"); case EXCEPTION_GUARD_PAGE: return _T("EXCEPTION_GUARD_PAGE"); case EXCEPTION_INVALID_HANDLE: return _T("EXCEPTION_INVALID_HANDLE"); //case EXCEPTION_POSSIBLE_DEADLOCK: return _T("EXCEPTION_POSSIBLE_DEADLOCK"); case CONTROL_C_EXIT: return _T("CONTROL_C_EXIT"); case 0xE06D7363: return _T("Microsoft C++ Exception"); default: break; } // ¹º°¡ Á» ´õ º¹ÀâÇÑ ¿¡·¯¶ó¸é... static TCHAR szFaultReason[2048]; ::lstrcpy(szFaultReason, _T("Unknown")); ::FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, ::GetModuleHandle(_T("ntdll.dll")), exPtrs->ExceptionRecord->ExceptionCode, 0, szFaultReason, 0, NULL); return szFaultReason; } /// \brief »ç¿ëÀÚ Á¤º¸¸¦ ¹ÝȯÇÑ´Ù. /// \return LPCTSTR »ç¿ëÀÚ À̸§ LPCTSTR GetUserInfo() { static TCHAR szUserName[200] = {0,}; ZeroMemory(szUserName, sizeof(szUserName)); DWORD UserNameSize = ASIZE(szUserName) - 1; if (!::GetUserName(szUserName, &UserNameSize)) ::lstrcpy(szUserName, _T("Unknown")); return szUserName; } /// \brief À©µµ¿ìÁî ¹öÀüÀ» ¹ÝȯÇÑ´Ù. /// \return LPCTSTR À©µµ¿ìÁî ¹öÀü ¹®ÀÚ¿­ LPCTSTR GetOSInfo() { TCHAR szWinVer[50] = {0,}; TCHAR szMajorMinorBuild[50] = {0,}; int nWinVer = 0; ::GetWinVersion(szWinVer, &nWinVer, szMajorMinorBuild); static TCHAR szOSInfo[512] = {0,}; #ifdef UNICODE swprintf(szOSInfo, ASIZE(szOSInfo)-1, L"%s (%s)", szWinVer, szMajorMinorBuild); #else _snprintf(szOSInfo, ASIZE(szOSInfo)-1, _TRUNCATE, "%s (%s)", szWinVer, szMajorMinorBuild); #endif szOSInfo[ASIZE(szOSInfo)-1] = 0; return szOSInfo; } /// \brief CPU Á¤º¸¸¦ ¹ÝȯÇÑ´Ù. /// \return LPCTSTR CPU Á¤º¸ ¹®ÀÚ¿­ LPCTSTR GetCpuInfo() { // CPU Á¤º¸ ±â·Ï SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); static TCHAR szCpuInfo[512] = {0,}; #ifdef UNICODE swprintf(szCpuInfo, ASIZE(szCpuInfo)-1, L"%d processor(s), type %d", SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType); #else _snprintf(szCpuInfo, ASIZE(szCpuInfo)-1, _TRUNCATE, "%d processor(s), type %d", SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType); #endif return szCpuInfo; } /// \brief ¸Þ¸ð¸® Á¤º¸¸¦ ¹ÝȯÇÑ´Ù. /// \return LPCTSTR ¸Þ¸ð¸® Á¤º¸ ¹®ÀÚ¿­ LPCTSTR GetMemoryInfo() { static const int ONE_K = 1024; static const int ONE_M = ONE_K * ONE_K; static const int ONE_G = ONE_K * ONE_K * ONE_K; MEMORYSTATUS MemInfo; MemInfo.dwLength = sizeof(MemInfo); GlobalMemoryStatus(&MemInfo); static TCHAR szMemoryInfo[2048] = {0,}; #ifdef UNICODE swprintf(szMemoryInfo, ASIZE(szMemoryInfo)-1, L"%d%% of memory in use.\n" L"%d MB physical memory.\n" L"%d MB physical memory free.\n" L"%d MB paging file.\n" L"%d MB paging file free.\n" L"%d MB user address space.\n" L"%d MB user address space free.", MemInfo.dwMemoryLoad, (MemInfo.dwTotalPhys + ONE_M - 1) / ONE_M, (MemInfo.dwAvailPhys + ONE_M - 1) / ONE_M, (MemInfo.dwTotalPageFile + ONE_M - 1) / ONE_M, (MemInfo.dwAvailPageFile + ONE_M - 1) / ONE_M, (MemInfo.dwTotalVirtual + ONE_M - 1) / ONE_M, (MemInfo.dwAvailVirtual + ONE_M - 1) / ONE_M); #else _snprintf(szMemoryInfo, ASIZE(szMemoryInfo)-1, _TRUNCATE, "%d%% of memory in use.\n" "%d MB physical memory.\n" "%d MB physical memory free.\n" "%d MB paging file.\n" "%d MB paging file free.\n" "%d MB user address space.\n" "%d MB user address space free.", MemInfo.dwMemoryLoad, (MemInfo.dwTotalPhys + ONE_M - 1) / ONE_M, (MemInfo.dwAvailPhys + ONE_M - 1) / ONE_M, (MemInfo.dwTotalPageFile + ONE_M - 1) / ONE_M, (MemInfo.dwAvailPageFile + ONE_M - 1) / ONE_M, (MemInfo.dwTotalVirtual + ONE_M - 1) / ONE_M, (MemInfo.dwAvailVirtual + ONE_M - 1) / ONE_M); #endif return szMemoryInfo; } /// \brief À©µµ¿ìÁî ¹öÀüÀ» ¾Ë¾Æ³½´Ù. /// /// This table has been assembled from Usenet postings, personal observations, /// and reading other people's code. Please feel free to add to it or correct /// it. /// ///
	/// dwPlatFormID  dwMajorVersion  dwMinorVersion  dwBuildNumber
	/// 95            1               4                 0            950
	/// 95 SP1        1               4                 0            >950 && <=1080
	/// 95 OSR2       1               4               <10            >1080
	/// 98            1               4                10            1998
	/// 98 SP1        1               4                10            >1998 && <2183
	/// 98 SE         1               4                10            >=2183
	/// ME            1               4                90            3000
	///
	/// NT 3.51       2               3                51
	/// NT 4          2               4                 0            1381
	/// 2000          2               5                 0            2195
	/// XP            2               5                 1            2600
	/// 2003 Server   2               5                 2            3790
	///
	/// CE            3
	/// 
/// /// \param pszVersion ¹öÀü ¹®ÀÚ¿­À» Áý¾î³ÖÀ» Æ÷ÀÎÅÍ /// \param nVersion ¹öÀü ¼ýÀÚ°ªÀ» Áý¾î³ÖÀ» Æ÷ÀÎÅÍ /// \param pszMajorMinorBuild ºôµå ¹®ÀÚ¿­À» Áý¾î³ÖÀ» Æ÷ÀÎÅÍ /// \return bool ¹«»çÈ÷ ½ÇÇàÇÑ °æ¿ì¿¡´Â true, ¹º°¡ ¿¡·¯°¡ »ý±ä °æ¿ì¿¡´Â false bool GetWinVersion(LPTSTR pszVersion, int *nVersion, LPTSTR pszMajorMinorBuild) { // from winbase.h #ifndef VER_PLATFORM_WIN32s #define VER_PLATFORM_WIN32s 0 #endif #ifndef VER_PLATFORM_WIN32_WINDOWS #define VER_PLATFORM_WIN32_WINDOWS 1 #endif #ifndef VER_PLATFORM_WIN32_NT #define VER_PLATFORM_WIN32_NT 2 #endif #ifndef VER_PLATFORM_WIN32_CE #define VER_PLATFORM_WIN32_CE 3 #endif static LPCTSTR WUNKNOWNSTR = _T("Unknown Windows Version"); static LPCTSTR W95STR = _T("Windows 95"); static LPCTSTR W95SP1STR = _T("Windows 95 SP1"); static LPCTSTR W95OSR2STR = _T("Windows 95 OSR2"); static LPCTSTR W98STR = _T("Windows 98"); static LPCTSTR W98SP1STR = _T("Windows 98 SP1"); static LPCTSTR W98SESTR = _T("Windows 98 SE"); static LPCTSTR WMESTR = _T("Windows ME"); static LPCTSTR WNT351STR = _T("Windows NT 3.51"); static LPCTSTR WNT4STR = _T("Windows NT 4"); static LPCTSTR W2KSTR = _T("Windows 2000"); static LPCTSTR WXPSTR = _T("Windows XP"); static LPCTSTR W2003SERVERSTR = _T("Windows 2003 Server"); static LPCTSTR WCESTR = _T("Windows CE"); static const int WUNKNOWN = 0; static const int W9XFIRST = 1; static const int W95 = 1; static const int W95SP1 = 2; static const int W95OSR2 = 3; static const int W98 = 4; static const int W98SP1 = 5; static const int W98SE = 6; static const int WME = 7; static const int W9XLAST = 99; static const int WNTFIRST = 101; static const int WNT351 = 101; static const int WNT4 = 102; static const int W2K = 103; static const int WXP = 104; static const int W2003SERVER = 105; static const int WNTLAST = 199; static const int WCEFIRST = 201; static const int WCE = 201; static const int WCELAST = 299; if (!pszVersion || !nVersion || !pszMajorMinorBuild) return false; ::lstrcpy(pszVersion, WUNKNOWNSTR); *nVersion = WUNKNOWN; OSVERSIONINFO osinfo; osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&osinfo)) return false; DWORD dwPlatformId = osinfo.dwPlatformId; DWORD dwMinorVersion = osinfo.dwMinorVersion; DWORD dwMajorVersion = osinfo.dwMajorVersion; DWORD dwBuildNumber = osinfo.dwBuildNumber & 0xFFFF; // Win 95 needs this TCHAR buf[50] = {0, }; #ifdef UNICODE swprintf(buf, ASIZE(buf), L"%u.%u.%u", dwMajorVersion, dwMinorVersion, dwBuildNumber); #else _snprintf(buf, ASIZE(buf), _TRUNCATE, "%u.%u.%u", dwMajorVersion, dwMinorVersion, dwBuildNumber); #endif ::lstrcpy(pszMajorMinorBuild, buf); if ((dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && (dwMajorVersion == 4)) { if ((dwMinorVersion < 10) && (dwBuildNumber == 950)) { ::lstrcpy(pszVersion, W95STR); *nVersion = W95; } else if ((dwMinorVersion < 10) && ((dwBuildNumber > 950) && (dwBuildNumber <= 1080))) { ::lstrcpy(pszVersion, W95SP1STR); *nVersion = W95SP1; } else if ((dwMinorVersion < 10) && (dwBuildNumber > 1080)) { ::lstrcpy(pszVersion, W95OSR2STR); *nVersion = W95OSR2; } else if ((dwMinorVersion == 10) && (dwBuildNumber == 1998)) { ::lstrcpy(pszVersion, W98STR); *nVersion = W98; } else if ((dwMinorVersion == 10) && ((dwBuildNumber > 1998) && (dwBuildNumber < 2183))) { ::lstrcpy(pszVersion, W98SP1STR); *nVersion = W98SP1; } else if ((dwMinorVersion == 10) && (dwBuildNumber >= 2183)) { ::lstrcpy(pszVersion, W98SESTR); *nVersion = W98SE; } else if (dwMinorVersion == 90) { ::lstrcpy(pszVersion, WMESTR); *nVersion = WME; } } else if (dwPlatformId == VER_PLATFORM_WIN32_NT) { if ((dwMajorVersion == 3) && (dwMinorVersion == 51)) { ::lstrcpy(pszVersion, WNT351STR); *nVersion = WNT351; } else if ((dwMajorVersion == 4) && (dwMinorVersion == 0)) { ::lstrcpy(pszVersion, WNT4STR); *nVersion = WNT4; } else if ((dwMajorVersion == 5) && (dwMinorVersion == 0)) { ::lstrcpy(pszVersion, W2KSTR); *nVersion = W2K; } else if ((dwMajorVersion == 5) && (dwMinorVersion == 1)) { ::lstrcpy(pszVersion, WXPSTR); *nVersion = WXP; } else if ((dwMajorVersion == 5) && (dwMinorVersion == 2)) { ::lstrcpy(pszVersion, W2003SERVERSTR); *nVersion = W2003SERVER; } } else if (dwPlatformId == VER_PLATFORM_WIN32_CE) { ::lstrcpy(pszVersion, WCESTR); *nVersion = WCE; } return true; #undef VER_PLATFORM_WIN32s #undef VER_PLATFORM_WIN32_WINDOWS #undef VER_PLATFORM_WIN32_NT #undef VER_PLATFORM_WIN32_CE } /// \brief strrchr TCHAR ¹öÀü /// \param str °Ë»öÇÒ ¹®ÀÚ¿­ /// \param ch ã°íÀÚ ÇÏ´Â ±ÛÀÚ /// \return TCHAR* ÁÖ¾îÁø ±ÛÀÚ¸¦ ãÀº °æ¿ì ÇØ´ç À§Ä¡ Æ÷ÀÎÅ͸¦ ¹ÝȯÇϰí, /// ãÁö ¸øÇÑ °æ¿ì¿¡´Â NULLÀ» ¹ÝȯÇÑ´Ù. TCHAR* lstrrchr(TCHAR* str, TCHAR ch) { TCHAR* start = str; while (*str++) ; while (--str != start && *str != ch) ; return *str == ch ? str : NULL; } } /* #include "StdAfx.h" #include "MiniDumper.h" #include #define chDIMOF(Array) (sizeof(Array) / sizeof(Array[0])) // based on dbghelp.h typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)( HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam ); cMiniDumper::DumpLevel cMiniDumper::ms_eDumpLevel = cMiniDumper::DUMP_LEVEL_0; BOOL cMiniDumper::ms_bAddTimeStamp = TRUE; TCHAR cMiniDumper::ms_acAppName[_MAX_PATH] = {0,}; TCHAR cMiniDumper::ms_acFaultReason[2048] = {0,}; ////////////////////////////////////////////////////////////////////////////// /// \brief »ý¼ºÀÚ /// \param DL ´ýÇÁ ·¹º§ /// \param bAddTimeStamp ´ýÇÁ ÆÄÀÏ À̸§¿¡´Ù°¡ ´ýÇÁ ÆÄÀÏÀÌ »ý¼ºµÈ ³¯Â¥¸¦ /// Áý¾î³Ö´Â°¡ÀÇ ¿©ºÎ. ////////////////////////////////////////////////////////////////////////////// cMiniDumper::cMiniDumper(DumpLevel DL, BOOL bAddTimeStamp) { // Assert(s_szAppName[0] == 0); // Assert(DL >= 0); // Assert(DL <= DUMP_LEVEL_2); ms_eDumpLevel = DL; ms_bAddTimeStamp = bAddTimeStamp; // ¸ðµâ °æ·Î¸¦ ¾Ë¾Æ³½´Ù. TCHAR filename[_MAX_PATH]; ::GetModuleFileName(NULL, filename, _MAX_PATH); // È®ÀåÀÚ¸¦ Á¦°ÅÇÑ ¸ðµâ °æ·Î¸¦ ÁغñÇØµÎ°í... TCHAR* dot = ::_tcsrchr(filename, _T('.')); ::lstrcpyn(ms_acAppName, filename, (int)(dot - filename + 1)); dot = ::_tcsrchr(ms_acAppName, _T('\\')); ::lstrcpyn(ms_acAppName, &dot[1], ::_tcslen(ms_acAppName) ); // ¿¹¿Ü ó¸® Çڵ鷯¸¦ ¼³Á¤ÇÑ´Ù. ::SetUnhandledExceptionFilter(TopLevelFilter); } ////////////////////////////////////////////////////////////////////////////// /// \brief ¼Ò¸êÀÚ ////////////////////////////////////////////////////////////////////////////// cMiniDumper::~cMiniDumper() { } ////////////////////////////////////////////////////////////////////////////// /// \brief ¿¹¿Ü¿¡ ´ëÇÑ Á¤º¸¸¦ ¹Þ¾Æ¼­, ¹Ì´Ï ´ýÇÁ ÆÄÀÏÀ» »ý¼ºÇÑ´Ù. /// /// SetUnhandledExceptionFilter() API¿¡ ÀÇÇØ¼­ ¼³Á¤µÇ°í, ÇÁ·Î¼¼½º ³»ºÎ¿¡¼­ /// Unhandled ExceptionÀÌ ¹ß»ýµÉ °æ¿ì, È£ÃâµÇ°Ô µÈ´Ù. ´Ü µð¹ö°Å°¡ ºÙ¾îÀÖ´Â /// °æ¿ì, Unhandled Exception Filter´Â È£ÃâµÇÁö ¾Ê´Â´Ù. ÀÌ ¸»Àº ÀÌ ÇÔ¼ö /// ³»ºÎ¸¦ µð¹ö±ëÇÒ ¼ö´Â ¾ø´Ù´Â ¸»ÀÌ´Ù. ÀÌ ÇÔ¼ö ³»ºÎ¸¦ µð¹ö±ëÇϱâ À§Çؼ­´Â /// ¸Þ½ÃÁö ¹Ú½º ¶Ç´Â ÆÄÀÏÀ» ÀÌ¿ëÇØ¾ßÇÑ´Ù. /// /// \param pExceptionInfo ¿¹¿Ü Á¤º¸ /// \return LONG ÀÌ ÇÔ¼ö¸¦ ½ÇÇàÇÏ°í ³­ ´ÙÀ½, ÃëÇÒ Çൿ°ª. ÀÚ¼¼ÇÑ °ÍÀº SEH /// ¹®¼­¸¦ Âü°íÇϵµ·Ï. ////////////////////////////////////////////////////////////////////////////// LONG WINAPI cMiniDumper::TopLevelFilter(struct _EXCEPTION_POINTERS* pExPtr) { LONG retval = EXCEPTION_CONTINUE_SEARCH; HMODULE hDLL = NULL; TCHAR szDbgHelpPath[_MAX_PATH] = {0, }; TCHAR szDumpPath[MAX_PATH * 2] = {0,}; // ¸ÕÀú ½ÇÇà ÆÄÀÏÀÌ ÀÖ´Â µð·ºÅ丮¿¡¼­ DBGHELP.DLLÀ» ·ÎµåÇØ º»´Ù. // Windows 2000 ÀÇ System32 µð·ºÅ丮¿¡ ÀÖ´Â DBGHELP.DLL ÆÄÀÏÀº ¹öÀüÀÌ // ¿À·¡µÈ °ÍÀÏ ¼ö Àֱ⠶§¹®ÀÌ´Ù. (ÃÖ¼Ò 5.1.2600.0 ÀÌ»óÀ̾î¾ß ÇÑ´Ù.) if (::GetModuleFileName(NULL, szDbgHelpPath, _MAX_PATH)) { LPTSTR pSlash = ::_tcsrchr(szDbgHelpPath, _T('\\')); if (pSlash) { ::lstrcpy(pSlash + 1, _T("DBGHELP.DLL")); hDLL = ::LoadLibrary(szDbgHelpPath); } } // ÇöÀç µð·ºÅ丮¿¡ ¾ø´Ù¸é, ¾Æ¹« ¹öÀüÀ̳ª ·ÎµåÇÑ´Ù. if (hDLL == NULL) hDLL = ::LoadLibrary(_T("DBGHELP.DLL")); // DBGHELP.DLLÀ» ãÀ» ¼ö ¾ø´Ù¸é ´õ ÀÌ»ó ÁøÇàÇÒ ¼ö ¾ø´Ù. if (hDLL == NULL) { OutputDebugString(_T("DBGHELP.DLL not found")); return retval; } // DLL ³»ºÎ¿¡¼­ MiniDumpWriteDump API¸¦ ã´Â´Ù. MINIDUMPWRITEDUMP pfnMiniDumpWriteDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDLL, "MiniDumpWriteDump"); // ¹Ì´Ï´ýÇÁ ÇÔ¼ö¸¦ ãÀ» ¼ö ¾ø´Ù¸é ´õ ÀÌ»ó ÁøÇàÇÒ ¼ö ¾ø´Ù. if (pfnMiniDumpWriteDump == NULL) { OutputDebugString(_T("DBGHELP.DLL too old")); return retval; } if (ms_bAddTimeStamp) { // ÇöÀç ½Ã°£À» ¾ò¾î¿Â´Ù. SYSTEMTIME t; ::GetLocalTime(&t); // ½Ã°£ ¹®ÀÚ¿­À» ÁغñÇÑ´Ù. TCHAR szTail[_MAX_PATH]; ::_sntprintf(szTail, chDIMOF(szTail)-1, _T("_%04d_%02d_%02d_%02d_%02d_%02d"), t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); // ´ýÇÁ ÆÄÀÏ À̸§ += ½Ã°£ ¹®ÀÚ¿­ // ::lstrcat(szDumpPath, _T("./Log/")); ::lstrcat(szDumpPath, ms_acAppName); ::lstrcat(szDumpPath, szTail); } // ´ýÇÁ ÆÄÀÏ À̸§ += È®ÀåÀÚ ::lstrcat(szDumpPath, _T(".dmp")); // ÆÄÀÏÀ» »ý¼ºÇÑ´Ù. HANDLE hFile = ::CreateFile( szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // ÆÄÀÏÀ» »ý¼ºÇÒ ¼ö ¾ø´Ù¸é ´õ ÀÌ»ó ÁøÇàÇÒ ¼ö ¾ø´Ù. if (hFile == INVALID_HANDLE_VALUE) { // filelog(NULL, "Failed to create dump file '%s' (error %s)", // szDumpPath, GetLastErrorString().c_str()); return retval; } MINIDUMP_EXCEPTION_INFORMATION ExceptionParam; ExceptionParam.ThreadId = ::GetCurrentThreadId(); ExceptionParam.ExceptionPointers = pExPtr; ExceptionParam.ClientPointers = FALSE; // ¿É¼Ç¿¡ µû¶ó ´ýÇÁ ÆÄÀÏÀ» »ý¼ºÇÑ´Ù. BOOL bResult = FALSE; switch (ms_eDumpLevel) { case DUMP_LEVEL_0: // MiniDumpNormal bResult = pfnMiniDumpWriteDump( ::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, &ExceptionParam, NULL, NULL); break; case DUMP_LEVEL_1: // MiniDumpWithDataSegs bResult = pfnMiniDumpWriteDump( ::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpWithDataSegs, &ExceptionParam, NULL, NULL); break; case DUMP_LEVEL_2: // MiniDumpWithFullMemory bResult = pfnMiniDumpWriteDump( ::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpWithFullMemory, &ExceptionParam, NULL, NULL); break; default: bResult = pfnMiniDumpWriteDump( ::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, &ExceptionParam, NULL, NULL); break; } // ´ýÇÁ ÆÄÀÏ »ý¼º °á°ú¸¦ ·Î±× ÆÄÀÏ¿¡´Ù ±â·ÏÇÑ´Ù. if (bResult) { TCHAR szMessage[8192] = {0,}; lstrcat(szMessage, _T("Saved dump file to '")); lstrcat(szMessage, szDumpPath); lstrcat(szMessage, _T("'.\nFault Reason : ")); lstrcat(szMessage, GetFaultReason(pExPtr)); // filelog(NULL, szMessage); // retval = EXCEPTION_EXECUTE_HANDLER; } else { // filelog(NULL, "Failed to save dump file to '%s' (error %d,%s)", // szDumpPath, ::GetLastError(), GetLastErrorString().c_str()); // Assert(false); } ::CloseHandle(hFile); return retval; } ////////////////////////////////////////////////////////////////////////////// /// \brief /// /// \param pExPtrs /// \return LPCTSTR ////////////////////////////////////////////////////////////////////////////// LPCTSTR cMiniDumper::GetFaultReason(struct _EXCEPTION_POINTERS* pExPtrs) { if (::IsBadReadPtr(pExPtrs, sizeof(EXCEPTION_POINTERS))) return _T("BAD EXCEPTION POINTERS"); // °£´ÜÇÑ ¿¡·¯ ÄÚµå¶ó¸é ±×³É º¯È¯ÇÒ ¼ö ÀÖ´Ù. switch (pExPtrs->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: return _T("EXCEPTION_ACCESS_VIOLATION"); case EXCEPTION_DATATYPE_MISALIGNMENT: return _T("EXCEPTION_DATATYPE_MISALIGNMENT"); case EXCEPTION_BREAKPOINT: return _T("EXCEPTION_BREAKPOINT"); case EXCEPTION_SINGLE_STEP: return _T("EXCEPTION_SINGLE_STEP"); case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return _T("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); case EXCEPTION_FLT_DENORMAL_OPERAND: return _T("EXCEPTION_FLT_DENORMAL_OPERAND"); case EXCEPTION_FLT_DIVIDE_BY_ZERO: return _T("EXCEPTION_FLT_DIVIDE_BY_ZERO"); case EXCEPTION_FLT_INEXACT_RESULT: return _T("EXCEPTION_FLT_INEXACT_RESULT"); case EXCEPTION_FLT_INVALID_OPERATION: return _T("EXCEPTION_FLT_INVALID_OPERATION"); case EXCEPTION_FLT_OVERFLOW: return _T("EXCEPTION_FLT_OVERFLOW"); case EXCEPTION_FLT_STACK_CHECK: return _T("EXCEPTION_FLT_STACK_CHECK"); case EXCEPTION_FLT_UNDERFLOW: return _T("EXCEPTION_FLT_UNDERFLOW"); case EXCEPTION_INT_DIVIDE_BY_ZERO: return _T("EXCEPTION_INT_DIVIDE_BY_ZERO"); case EXCEPTION_INT_OVERFLOW: return _T("EXCEPTION_INT_OVERFLOW"); case EXCEPTION_PRIV_INSTRUCTION: return _T("EXCEPTION_PRIV_INSTRUCTION"); case EXCEPTION_IN_PAGE_ERROR: return _T("EXCEPTION_IN_PAGE_ERROR"); case EXCEPTION_ILLEGAL_INSTRUCTION: return _T("EXCEPTION_ILLEGAL_INSTRUCTION"); case EXCEPTION_NONCONTINUABLE_EXCEPTION: return _T("EXCEPTION_NONCONTINUABLE_EXCEPTION"); case EXCEPTION_STACK_OVERFLOW: return _T("EXCEPTION_STACK_OVERFLOW"); case EXCEPTION_INVALID_DISPOSITION: return _T("EXCEPTION_INVALID_DISPOSITION"); case EXCEPTION_GUARD_PAGE: return _T("EXCEPTION_GUARD_PAGE"); case EXCEPTION_INVALID_HANDLE: return _T("EXCEPTION_INVALID_HANDLE"); case 0xE06D7363: return _T("Microsoft C++ Exception"); default: break; } // ¹º°¡ Á» ´õ º¹ÀâÇÑ ¿¡·¯¶ó¸é... ::lstrcpy(ms_acFaultReason, _T("Unknown")); ::FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, ::GetModuleHandle(_T("NTDLL.DLL")), pExPtrs->ExceptionRecord->ExceptionCode, 0, ms_acFaultReason, 0, NULL); return ms_acFaultReason; } */