#include "stdafx.h" #include "SelfDump.h" #include // SelfDump¸¦ À§ÇÑ ÆÄ¶ó¹ÌÅÍ static TCHAR g_szDumpPreFixName [MAX_PATH] = {0,}; static TCHAR g_szDumpPath [MAX_PATH_SELFDUMP] = {0,}; static DWORD g_dwDumpCapacityBytes = DEFAULT_DUMP_CAPACITY; static MINIDUMP_TYPE g_nMiniDumpType = MiniDumpNormal; static pfnCbOnCrash g_pfnCallback = NULL; static LPVOID g_pUserData = NULL; CSelfDump::~CSelfDump() { } CSelfDump::CSelfDump(IN LPCTSTR lpszDumpPreFixName/*=NULL*/, IN MINIDUMP_TYPE nMiniDumpType/*=MiniDumpNormal*/, IN DWORD dwDumpCapacityBytes/*=DEFAULT_DUMP_CAPACITY*/, IN LPCTSTR lpszDumpPath/*=NULL*/, IN pfnCbOnCrash pfnCallback/*=NULL*/, IN LPVOID lpUserData/*=NULL*/) { RegisterExceptionFilter(lpszDumpPreFixName, nMiniDumpType, dwDumpCapacityBytes, lpszDumpPath, pfnCallback, lpUserData); } LONG CSelfDump::CbTopLevelExceptionFilter(struct _EXCEPTION_POINTERS *pExceptionInfo) { LONG lRtnValue = EXCEPTION_CONTINUE_SEARCH; HANDLE hFile = INVALID_HANDLE_VALUE; BOOL bRtnValue = FALSE; TCHAR szPath[MAX_PATH_SELFDUMP] = {0,}; HMODULE hDll = NULL; LPFN_MinuDumpWriteDump pfn = NULL; SYSTEMTIME stTime = {0,}; _MINIDUMP_EXCEPTION_INFORMATION stExceptInfo = {0,}; // Dump Path°¡ Á¸ÀçÇÏÁö ¾Ê´Â´Ù¸é, Pass if (INVALID_FILE_ATTRIBUTES == ::GetFileAttributes(g_szDumpPath)) { // ·Î±×µµ ³²±âÁö ¾Êµµ·Ï ÇÑ´Ù. lRtnValue = EXCEPTION_CONTINUE_SEARCH; goto FINAL; } // Callback µî·Ï µÇ¾î ÀÖÀ¸¸é ¿ì¼± È£ÃâÇÑ´Ù. if (NULL != g_pfnCallback) { if (FALSE == (*g_pfnCallback)(g_pUserData)) { WriteLogFile(g_szDumpPath, TEXT(""), &stTime, g_szDumpPreFixName, FALSE); lRtnValue = EXCEPTION_CONTINUE_SEARCH; goto FINAL; } } // ¸¸¾à, MiniDumpTypeÀÌ -1 À̶ó¸é,... Á¾·á if ((MINIDUMP_TYPE)-1 == g_nMiniDumpType) { WriteLogFile(g_szDumpPath, TEXT(""), &stTime, g_szDumpPreFixName, FALSE); lRtnValue = EXCEPTION_CONTINUE_SEARCH; goto FINAL; } // DbgHelp.dllÀ» ·ÎµåÇÑ´Ù. hDll = CSelfDump::LoadLibrary_DbgHelp(); if (NULL == hDll) { WriteLogFile(g_szDumpPath, TEXT(""), &stTime, g_szDumpPreFixName, FALSE); lRtnValue = EXCEPTION_CONTINUE_SEARCH; goto FINAL; } // DbgHelp.dll::MinuDumpWriteDump(...)¸¦ ±¸ÇÑ´Ù. pfn = (LPFN_MinuDumpWriteDump)::GetProcAddress(hDll, "MiniDumpWriteDump"); if (NULL == pfn) { WriteLogFile(g_szDumpPath, TEXT(""), &stTime, g_szDumpPreFixName, FALSE); lRtnValue = EXCEPTION_CONTINUE_SEARCH; goto FINAL; } // Dump ÆÄÀÏ À̸§À» ±¸ÇÑ´Ù. if (FALSE == CSelfDump::GetDumpFileFullPathName(g_szDumpPath, g_szDumpPreFixName, szPath, MAX_PATH_SELFDUMP, &stTime)) { ::GetLocalTime(&stTime); WriteLogFile(g_szDumpPath, TEXT(""), &stTime, g_szDumpPreFixName, FALSE); lRtnValue = EXCEPTION_CONTINUE_SEARCH; goto FINAL; } // Dump ÆÄÀÏÀ» Create ÇÑ´Ù. hFile = ::CreateFile(szPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { WriteLogFile(g_szDumpPath, szPath, &stTime, g_szDumpPreFixName, FALSE); lRtnValue = EXCEPTION_CONTINUE_SEARCH; goto FINAL; } // ´ýÇÁ »ý¼ºÀ» À§ÇÑ º¯¼ö ÃʱâÈ­ stExceptInfo.ThreadId = ::GetCurrentThreadId(); stExceptInfo.ExceptionPointers = pExceptionInfo; stExceptInfo.ClientPointers = NULL; // Dump File µéÀ» manageÇÏ¿© ÆÄÀϵîÀ» »èÁ¦ÇÑ´Ù. if (FALSE == CSelfDump::ManageCapacity(g_szDumpPath, g_szDumpPreFixName, g_dwDumpCapacityBytes)) { WriteLogFile(g_szDumpPath, szPath, &stTime, g_szDumpPreFixName, FALSE); lRtnValue = EXCEPTION_CONTINUE_SEARCH; goto FINAL; } // Dump »ý¼º bRtnValue = (*pfn)(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, &stExceptInfo, NULL, NULL); if (TRUE == bRtnValue) { WriteLogFile(g_szDumpPath, szPath, &stTime, g_szDumpPreFixName, TRUE); // EXCEPTION_EXECUTE_HANDLER À¸·Î Çϸé, // drwtsn32.exe¿Í °°Àº µð¹ö°Å¸¦ ÅëÇÑ ´ýÇÁ°¡ ³²Áö ¾Ê´Â´Ù. // lRtnValue = EXCEPTION_EXECUTE_HANDLER; lRtnValue = EXCEPTION_CONTINUE_SEARCH; } else { WriteLogFile(g_szDumpPath, szPath, &stTime, g_szDumpPreFixName, FALSE); lRtnValue = EXCEPTION_CONTINUE_SEARCH; } FINAL: if (INVALID_HANDLE_VALUE != hFile) { ::CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; pfn = NULL; } if (NULL != hDll) { ::FreeLibrary(hDll); hDll = NULL; pfn = NULL; } return lRtnValue; } VOID CSelfDump::SetMiniDumpTypeWhenExist(IN LPCTSTR lpszRegKeyPath, IN LPCTSTR lpszValueName) { HKEY hKey = NULL; LONG lRtnValue = 0; DWORD dwType = 0; DWORD dwCbSize = 0; DWORD dwValue = 0; if ((NULL == lpszRegKeyPath) || (NULL == lpszValueName)) { goto FINAL; } lRtnValue = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpszRegKeyPath, 0, KEY_READ, &hKey); if (ERROR_SUCCESS != lRtnValue) { goto FINAL; } dwType = REG_DWORD; dwCbSize = sizeof(dwValue); lRtnValue = ::RegQueryValueEx(hKey, lpszValueName, NULL, &dwType, (LPBYTE)&dwValue, &dwCbSize); if (ERROR_SUCCESS != lRtnValue) { goto FINAL; } g_nMiniDumpType = (MINIDUMP_TYPE)dwValue; FINAL: if (NULL != hKey) { ::RegCloseKey(hKey); hKey = NULL; } } VOID CSelfDump::WriteLogFile(IN LPCTSTR lpszPath, IN LPCTSTR lpszDumpFileFullPathName, IN PSYSTEMTIME pstTimeOccur, IN LPCTSTR lpszDumpPrefixName, IN BOOL bWriteDumpSuccess) { TCHAR szLogPath[MAX_PATH_SELFDUMP] = {0,}; TCHAR szSection[MAX_PATH] = {0,}; TCHAR szTmp [MAX_PATH] = {0,}; if ((NULL == lpszPath) || (NULL == lpszDumpFileFullPathName) || (NULL == pstTimeOccur) || (NULL == lpszDumpPrefixName)) { return; } StringCchPrintf(szLogPath, MAX_PATH_SELFDUMP, TEXT("%s\\CrashDmp.log"), lpszPath); StringCchPrintf(szSection, MAX_PATH, TEXT("%.4d%.2d%.2d_%.2d%.2d%.2d%.3d"), pstTimeOccur->wYear, pstTimeOccur->wMonth, pstTimeOccur->wDay, pstTimeOccur->wHour, pstTimeOccur->wMinute, pstTimeOccur->wSecond, pstTimeOccur->wMilliseconds); ::WritePrivateProfileString(szSection, TEXT("Prefix"), lpszDumpPrefixName, szLogPath); ::WritePrivateProfileString(szSection, TEXT("DumpPath"), lpszDumpFileFullPathName, szLogPath); StringCchPrintf(szTmp, MAX_PATH, TEXT("%d"), ::GetCurrentProcessId()); ::WritePrivateProfileString(szSection, TEXT("ProcessId"), szTmp, szLogPath); ::GetModuleFileName(NULL, szTmp, MAX_PATH); ::WritePrivateProfileString(szSection, TEXT("CrashModulePath"), szTmp, szLogPath); if (TRUE == bWriteDumpSuccess) { ::WritePrivateProfileString(szSection, TEXT("SuccessWriteDump"), TEXT("yes"), szLogPath); } else { ::WritePrivateProfileString(szSection, TEXT("SuccessWriteDump"), TEXT("no"), szLogPath); } } BOOL CSelfDump::RegisterExceptionFilter(IN LPCTSTR lpszDumpPreFixName/*=NULL*/, IN MINIDUMP_TYPE nMiniDumpType/*=MiniDumpNormal*/, IN DWORD dwDumpCapacityBytes/*=DEFAULT_DUMP_CAPACITY*/, IN LPCTSTR lpszDumpPath/*=NULL*/, IN pfnCbOnCrash pfnCallback/*=NULL*/, IN LPVOID lpUserData/*=NULL*/) { BOOL bRtnValue = TRUE; DWORD dwRtnValue = 0; // ÆÄ¶ó¹ÌÅ͵鸦 º¹»çÇÑ´Ù. if (NULL != lpszDumpPreFixName) { StringCchCopy(g_szDumpPreFixName, MAX_PATH, lpszDumpPreFixName); } else { GetDefaultDumpPreFixName(g_szDumpPreFixName, MAX_PATH); } if (DEFAULT_DUMP_CAPACITY != dwDumpCapacityBytes) { g_dwDumpCapacityBytes = dwDumpCapacityBytes; } if (NULL != lpszDumpPath) { StringCchCopy(g_szDumpPath, MAX_PATH_SELFDUMP, lpszDumpPath); } else { // NULL --> Default Path ( <.exeÀÇ ModulePath>\Temp ) dwRtnValue = CSelfDump::GetDefaultModuleTempPath(g_szDumpPath, MAX_PATH_SELFDUMP); if (ERROR_SUCCESS != dwRtnValue) { bRtnValue = FALSE; goto FINAL; } } g_nMiniDumpType = nMiniDumpType; g_pfnCallback = pfnCallback; g_pUserData = lpUserData; // Exception Filter¸¦ µî·ÏÇÑ´Ù. ::SetUnhandledExceptionFilter(CSelfDump::CbTopLevelExceptionFilter); bRtnValue = TRUE; FINAL: return bRtnValue; } BOOL CSelfDump::GetDumpFileFullPathName(IN LPCTSTR lpszPath, IN LPCTSTR lpszDumpPreFixName, OUT LPTSTR lpszDumpFileFullPathName, IN DWORD dwCchDumpFileFullPathName, OUT PSYSTEMTIME pstTime) { BOOL bRtnValue = TRUE; if ((NULL == lpszPath) || (NULL == lpszDumpPreFixName) || (NULL == lpszDumpFileFullPathName) || (NULL == pstTime)) { bRtnValue = FALSE; goto FINAL; } ::GetLocalTime(pstTime); StringCchPrintf(lpszDumpFileFullPathName, dwCchDumpFileFullPathName, TEXT("%s\\%s_%.4d%.2d%.2d%.2d%.2d%.2d.dmp"), lpszPath, lpszDumpPreFixName, pstTime->wYear, pstTime->wMonth, pstTime->wDay, pstTime->wHour, pstTime->wMinute, pstTime->wSecond); bRtnValue = TRUE; FINAL: return bRtnValue; } DWORD CSelfDump::GetFilesSize(IN LPCTSTR lpszFindFileName, OUT LPTSTR lpszEarlyFileName, IN DWORD dwCchEarlyFileName) { HANDLE hFind = INVALID_HANDLE_VALUE; DWORD dwTotalBytes = 0; WIN32_FIND_DATA FindFileData = {0,}; dwTotalBytes = 0; if ((NULL == lpszFindFileName) || (NULL == lpszEarlyFileName)) { goto FINAL; } ZeroMemory(lpszEarlyFileName, sizeof(TCHAR) * dwCchEarlyFileName); // Enum File ÇÑ´Ù. hFind = ::FindFirstFile(lpszFindFileName, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { dwTotalBytes = 0; goto FINAL; } if (TEXT('.') != FindFileData.cFileName[0]) { dwTotalBytes += FindFileData.nFileSizeLow; StringCchCopy(lpszEarlyFileName, dwCchEarlyFileName, FindFileData.cFileName); } while (FindNextFile(hFind, &FindFileData) != 0) { if (TEXT('.') == FindFileData.cFileName[0]) { continue; } // ÆÄÀÏ Å©±â¸¦ °è¼Ó ´õÇØ ³ª°£´Ù. // (¼³¸¶ nFileHighSize´Â 0À̰ÚÁö?) dwTotalBytes += FindFileData.nFileSizeLow; if (TEXT('\0') == lpszEarlyFileName[0]) { StringCchCopy(lpszEarlyFileName, dwCchEarlyFileName, FindFileData.cFileName); continue; } if (0 < _tcscmp(lpszEarlyFileName, FindFileData.cFileName)) { StringCchCopy(lpszEarlyFileName, dwCchEarlyFileName, FindFileData.cFileName); } } FINAL: if (INVALID_HANDLE_VALUE != hFind) { ::FindClose(hFind); hFind = INVALID_HANDLE_VALUE; } return dwTotalBytes; } BOOL CSelfDump::ManageCapacity(IN LPCTSTR lpszPath, IN LPCTSTR lpszDumpPreFixName, IN DWORD dwDumpCapacityBytes) { INT i = 0; BOOL bRtnValue = FALSE; DWORD dwCbFileSize = 0; TCHAR szFileName[MAX_PATH_SELFDUMP] = {0,}; TCHAR szDelFileName[MAX_PATH_SELFDUMP] = {0,}; TCHAR szEarlyFileName[MAX_PATH_SELFDUMP] = {0,}; if ((NULL == lpszPath) || (NULL == lpszDumpPreFixName)) { bRtnValue = FALSE; goto FINAL; } // Path¸¦ °áÁ¤ÇÑ´Ù. StringCchPrintf(szFileName, MAX_PATH_SELFDUMP, TEXT("%s\\%s_*.dmp"), lpszPath, lpszDumpPreFixName); // ÇѲ¨¹ø¿¡ 32°³ÀÇ ´ýÇÁÆÄÀÏÀ» »èÁ¦ÇÒ ¼ö ÀÖ´Ù. for (i=0; i