前面介紹如如何用WinDBG 產生crash dump 《WinDBG 技巧:如何產生Dump 檔案(.dump 命令) 》,但是使用者機器上通常不安裝WinDBG, 而且多數使用者也不知道怎麼使用WinDBG。 所以最好是自己程式裡面能夠捕捉exception/crash,並且產生crash dump,然後通過網路傳回到自己伺服器。
捕捉exception 可以用API 函數 SetUnhandledExceptionFilter 。產生crash dump 可以用DbgHelp.dll 裡面的MiniDumpWriteDump 函數。
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter( __in LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter );
BOOL WINAPI MiniDumpWriteDump( __in HANDLE hProcess, __in DWORD ProcessId, __in HANDLE hFile, __in MINIDUMP_TYPE DumpType, __in PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, __in PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, __in PMINIDUMP_CALLBACK_INFORMATION CallbackParam );
程式碼範例:
#include <dbghelp.h><br />#include <shellapi.h><br />#include <shlobj.h></p><p>// 自訂的exectpion filter<br />LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pExceptionPointers)<br />{</p><p> SetErrorMode( SEM_NOGPFAULTERRORBOX );</p><p> //收集資訊<br /> CStringW strBuild;<br /> strBuild.Format(L"Build: %s %s", __DATE__, __TIME__);<br /> CStringW strError;<br /> HMODULE hModule;<br /> WCHAR szModuleName[MAX_PATH] = L"";<br /> GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)pExceptionPointers->ExceptionRecord->ExceptionAddress, &hModule);<br /> GetModuleFileName(hModule, szModuleName, ARRAYSIZE(szModuleName));<br /> strError.AppenedFormat(L"%s %d , %d ,%d.", szModuleName,pExceptionPointers->ExceptionRecord->ExceptionCode, pExceptionPointers->ExceptionRecord->ExceptionFlags, pExceptionPointers->ExceptionRecord->ExceptionAddress);</p><p> //產生 mini crash dump<br /> BOOL bMiniDumpSuccessful;<br /> WCHAR szPath[MAX_PATH];<br /> WCHAR szFileName[MAX_PATH];<br /> WCHAR* szAppName = L"AppName";<br /> WCHAR* szVersion = L"v1.0";<br /> DWORD dwBufferSize = MAX_PATH;<br /> HANDLE hDumpFile;<br /> SYSTEMTIME stLocalTime;<br /> MINIDUMP_EXCEPTION_INFORMATION ExpParam;<br /> GetLocalTime( &stLocalTime );<br /> GetTempPath( dwBufferSize, szPath );<br /> StringCchPrintf( szFileName, MAX_PATH, L"%s%s", szPath, szAppName );<br /> CreateDirectory( szFileName, NULL );<br /> StringCchPrintf( szFileName, MAX_PATH, L"%s%s//%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",<br /> szPath, szAppName, szVersion,<br /> stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,<br /> stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,<br /> GetCurrentProcessId(), GetCurrentThreadId());<br /> hDumpFile = CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE,<br /> FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);</p><p> MINIDUMP_USER_STREAM UserStream[2];<br /> MINIDUMP_USER_STREAM_INFORMATION UserInfo;<br /> UserInfo.UserStreamCount = 1;<br /> UserInfo.UserStreamArray = UserStream;<br /> UserStream[0].Type = CommentStreamW;<br /> UserStream[0].BufferSize = strBuild.GetLength()*sizeof(WCHAR);<br /> UserStream[0].Buffer = strBuild.GetBuffer();<br /> UserStream[1].Type = CommentStreamW;<br /> UserStream[1].BufferSize = strError.GetLength()*sizeof(WCHAR);<br /> UserStream[1].Buffer = strError.GetBuffer();</p><p> ExpParam.ThreadId = GetCurrentThreadId();<br /> ExpParam.ExceptionPointers = pExceptionPointers;<br /> ExpParam.ClientPointers = TRUE;</p><p> MINIDUMP_TYPE MiniDumpWithDataSegs = MiniDumpNormal<br /> | MiniDumpWithHandleData<br /> | MiniDumpWithUnloadedModules<br /> | MiniDumpWithIndirectlyReferencedMemory<br /> | MiniDumpScanMemory<br /> | MiniDumpWithProcessThreadData<br /> | MiniDumpWithThreadInfo;<br /> bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),<br /> hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);<br /> // 上傳mini dump 到自己伺服器(略)<br /> ...</p><p> return EXCEPTION_CONTINUE_SEARCH; //或者 EXCEPTION_EXECUTE_HANDLER 關閉程式<br />}</p><p>int _tmain()<br />{<br /> // 設定 execption filter<br /> SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);<br /> ....<br /> return 0;<br />}<br />