您的位置:首页 > 其它

调试技巧 —— 如何利用windbg + dump + map分析程序异常

2013-01-04 00:20 561 查看
之前碰到论坛里有几个好友,说程序不时的崩溃,什么xxoo不能read的! 如果光要是这个内存地址,估计你会疯掉~~
所以分享一下基本的调试技巧,需要准备的工具有WinDbg + VC6.0,

下面是自己整理的一份自动生成DUMP文件的源代码,只需要添加到工程即可,源代码如下:

MiniDump.h

#include <windows.h>
#include <tlhelp32.h>

//#include "dbghelp.h"
//#define DEBUG_DPRINTF 1 //allow d()
//#include "wfun.h"

#pragma optimize("y", off) //generate stack frame pointers for all functions - same as /Oy- in the project
#pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union
#pragma warning(disable: 4100) //unreferenced formal parameter

/*BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr);
int WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str);
int WINAPI Get_Version_Str(PCHAR Str);
PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException);
void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag);*/

// In case you don't have dbghelp.h.
#ifndef _DBGHELP_

typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
DWORD ThreadId;
PEXCEPTION_POINTERS ExceptionPointers;
BOOL ClientPointers;
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;

typedef enum _MINIDUMP_TYPE {
MiniDumpNormal = 0x00000000,
MiniDumpWithDataSegs = 0x00000001,
} MINIDUMP_TYPE;

typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)(
IN HANDLE hProcess,
IN DWORD ProcessId,
IN HANDLE hFile,
IN MINIDUMP_TYPE DumpType,
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
IN PVOID UserStreamParam, OPTIONAL
IN PVOID CallbackParam OPTIONAL
);

#else

typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)(
IN HANDLE hProcess,
IN DWORD ProcessId,
IN HANDLE hFile,
IN MINIDUMP_TYPE DumpType,
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
IN PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
IN PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
);
#endif //#ifndef _DBGHELP_

// Tool Help functions.
typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);
typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);

extern void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag);

extern HMODULE hDbgHelp;
extern MINIDUMP_WRITE_DUMP MiniDumpWriteDump_;

extern CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;
extern MODULE32_FIRST Module32First_;
extern MODULE32_NEST Module32Next_;

[cpp]
view plaincopy

#include <windows.h>
#include <tlhelp32.h>

//#include "dbghelp.h"
//#define DEBUG_DPRINTF 1 //allow d()
//#include "wfun.h"

#pragma optimize("y", off) //generate stack frame pointers for all functions - same as /Oy- in the project
#pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union
#pragma warning(disable: 4100) //unreferenced formal parameter

/*BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr);
int WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str);
int WINAPI Get_Version_Str(PCHAR Str);
PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException);
void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag);*/

// In case you don't have dbghelp.h.

#ifndef _DBGHELP_

typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
DWORD ThreadId;
PEXCEPTION_POINTERS ExceptionPointers;
BOOL ClientPointers;
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;

typedef enum _MINIDUMP_TYPE {
MiniDumpNormal = 0x00000000,
MiniDumpWithDataSegs = 0x00000001,
} MINIDUMP_TYPE;

typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)(
IN HANDLE hProcess,
IN DWORD ProcessId,
IN HANDLE hFile,
IN MINIDUMP_TYPE DumpType,
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
IN PVOID UserStreamParam, OPTIONAL
IN PVOID CallbackParam OPTIONAL
);

#else

typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)(
IN HANDLE hProcess,
IN DWORD ProcessId,
IN HANDLE hFile,
IN MINIDUMP_TYPE DumpType,
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
IN PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
IN PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
);
#endif //#ifndef _DBGHELP_

// Tool Help functions.

typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);
typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);

extern void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag);

extern HMODULE hDbgHelp;
extern MINIDUMP_WRITE_DUMP MiniDumpWriteDump_;

extern CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;
extern MODULE32_FIRST Module32First_;
extern MODULE32_NEST Module32Next_;

[cpp]
view plaincopy

#include <windows.h>
#include <tlhelp32.h>

//#include "dbghelp.h"
//#define DEBUG_DPRINTF 1 //allow d()
//#include "wfun.h"

#pragma optimize("y", off) //generate stack frame pointers for all functions - same as /Oy- in the project
#pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union
#pragma warning(disable: 4100) //unreferenced formal parameter

/*BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr);
int WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str);
int WINAPI Get_Version_Str(PCHAR Str);
PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException);
void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag);*/

// In case you don't have dbghelp.h.
#ifndef _DBGHELP_

typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
DWORD ThreadId;
PEXCEPTION_POINTERS ExceptionPointers;
BOOL ClientPointers;
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;

typedef enum _MINIDUMP_TYPE {
MiniDumpNormal = 0x00000000,
MiniDumpWithDataSegs = 0x00000001,
} MINIDUMP_TYPE;

typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)(
IN HANDLE hProcess,
IN DWORD ProcessId,
IN HANDLE hFile,
IN MINIDUMP_TYPE DumpType,
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
IN PVOID UserStreamParam, OPTIONAL
IN PVOID CallbackParam OPTIONAL
);

#else

typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)(
IN HANDLE hProcess,
IN DWORD ProcessId,
IN HANDLE hFile,
IN MINIDUMP_TYPE DumpType,
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
IN PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
IN PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
);
#endif //#ifndef _DBGHELP_

// Tool Help functions.
typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);
typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);

extern void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag);

extern HMODULE hDbgHelp;
extern MINIDUMP_WRITE_DUMP MiniDumpWriteDump_;

extern CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;
extern MODULE32_FIRST Module32First_;
extern MODULE32_NEST Module32Next_;

MiniDump.cpp

view plaincopy
to clipboard

/*
Author: Vladimir Sedach.

Purpose: demo of Call Stack creation by our own means,
and with MiniDumpWriteDump() function of DbgHelp.dll.
*/

#include "StdAfx.h"
#include "MiniDump.h"
#include <Shlwapi.h>

#pragma comment(lib,"shlwapi.lib")

HMODULE hDbgHelp;
MINIDUMP_WRITE_DUMP MiniDumpWriteDump_;

CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;
MODULE32_FIRST Module32First_;
MODULE32_NEST Module32Next_;

#define DUMP_SIZE_MAX 8000 //max size of our dump
#define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls
#define NL "\r\n" //new line

extern CString GetExePath();

//****************************************************************************************
BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr)
//****************************************************************************************
// Find module by Ret_Addr (address in the module).

// Return Module_Name (full path) and Module_Addr (start address).
// Return TRUE if found.
{
MODULEENTRY32 M = {sizeof(M)};
HANDLE hSnapshot;

Module_Name[0] = 0;

if (CreateToolhelp32Snapshot_)
{
hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0);

if ((hSnapshot != INVALID_HANDLE_VALUE) &&
Module32First_(hSnapshot, &M))
{
do
{
if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)
{
lstrcpyn(Module_Name, M.szExePath, MAX_PATH);
Module_Addr = M.modBaseAddr;
break;
}
} while (Module32Next_(hSnapshot, &M));
}

CloseHandle(hSnapshot);
}

return !!Module_Name[0];
} //Get_Module_By_Ret_Addr

//******************************************************************
int WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str)
//******************************************************************
// Fill Str with call stack info.
// pException can be either GetExceptionInformation() or NULL.
// If pException = NULL - get current call stack.

{
CHAR Module_Name[MAX_PATH];
PBYTE Module_Addr = 0;
PBYTE Module_Addr_1;
int Str_Len;

typedef struct STACK
{
STACK * Ebp;
PBYTE Ret_Addr;
DWORD Param[0];
} STACK, * PSTACK;

STACK Stack = {0, 0};
PSTACK Ebp;

if (pException) //fake frame for exception address
{
Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp;
Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress;
Ebp = &Stack;
}
else
{
Ebp = (PSTACK)&pException - 1; //frame addr of Get_Call_Stack()

// Skip frame of Get_Call_Stack().
if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
Ebp = Ebp->Ebp; //caller ebp
}

Str[0] = 0;
Str_Len = 0;

// Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX.
// Break trace on wrong stack frame.

for (int Ret_Addr_I = 0;
(Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
Ret_Addr_I++, Ebp = Ebp->Ebp)
{
// If module with Ebp->Ret_Addr found.
if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr_1))
{
if (Module_Addr_1 != Module_Addr) //new module
{
// Save module's address and full path.
Module_Addr = Module_Addr_1;
Str_Len += wsprintf(Str + Str_Len, NL "%08X %s", Module_Addr, Module_Name);
}

// Save call offset.
Str_Len += wsprintf(Str + Str_Len,
NL " +%08X", Ebp->Ret_Addr - Module_Addr);

// Save 5 params of the call. We don't know the real number of params.
if (pException && !Ret_Addr_I) //fake frame for exception address
Str_Len += wsprintf(Str + Str_Len, " Exception Offset");
else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))
{
Str_Len += wsprintf(Str + Str_Len, " (%X, %X, %X, %X, %X)",
Ebp->Param[0], Ebp->Param[1], Ebp->Param[2], Ebp->Param[3], Ebp->Param[4]);
}
}
else
Str_Len += wsprintf(Str + Str_Len, NL "%08X", Ebp->Ret_Addr);
}

return Str_Len;
} //Get_Call_Stack

//***********************************

int WINAPI Get_Version_Str(PCHAR Str)
//***********************************

// Fill Str with Windows version.
{
OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)}; //EX for NT 5.0 and later

if (!GetVersionEx((POSVERSIONINFO)&V))
{
ZeroMemory(&V, sizeof(V));
V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx((POSVERSIONINFO)&V);
}

if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)
V.dwBuildNumber = LOWORD(V.dwBuildNumber); //for 9x HIWORD(dwBuildNumber) = 0x04xx

return wsprintf(Str,
NL "Windows: %d.%d.%d, SP %d.%d, Product Type %d", //SP - service pack, Product Type - VER_NT_WORKSTATION,...
V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor/*, V.wProductType*/);
} //Get_Version_Str

//*************************************************************
PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)
//*************************************************************
// Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str.
{
PCHAR Str;
int Str_Len;
int i;
CHAR Module_Name[MAX_PATH];
PBYTE Module_Addr;
HANDLE hFile;
FILETIME Last_Write_Time;
FILETIME Local_File_Time;
SYSTEMTIME T;

Str = new CHAR[DUMP_SIZE_MAX];

if (!Str)
return NULL;

Str_Len = 0;
Str_Len += Get_Version_Str(Str + Str_Len);

Str_Len += wsprintf(Str + Str_Len, NL "Process: ");
GetModuleFileName(NULL, Str + Str_Len, MAX_PATH);
Str_Len = lstrlen(Str);

// If exception occurred.
if (pException)
{
EXCEPTION_RECORD & E = *pException->ExceptionRecord;
CONTEXT & C = *pException->ContextRecord;

// If module with E.ExceptionAddress found - save its path and date.
if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))
{
Str_Len += wsprintf(Str + Str_Len,
NL "Module: %s", Module_Name);

if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
{
if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time))
{
FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time);
FileTimeToSystemTime(&Local_File_Time, &T);

Str_Len += wsprintf(Str + Str_Len,
NL "Date Modified: %02d/%02d/%d",
T.wMonth, T.wDay, T.wYear);
}
CloseHandle(hFile);
}
}
else
{
Str_Len += wsprintf(Str + Str_Len,
NL "Exception Addr: %08X", E.ExceptionAddress);
}

Str_Len += wsprintf(Str + Str_Len,
NL "Exception Code: %08X", E.ExceptionCode);

if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
// Access violation type - Write/Read.
Str_Len += wsprintf(Str + Str_Len,
NL "%s Address: %08X",
(E.ExceptionInformation[0]) ? "Write" : "Read", E.ExceptionInformation[1]);
}

// Save instruction that caused exception.
Str_Len += wsprintf(Str + Str_Len, NL "Instruction: ");
for (i = 0; i < 16; i++)
Str_Len += wsprintf(Str + Str_Len, " %02X", PBYTE(E.ExceptionAddress)[i]);

// Save registers at exception.
Str_Len += wsprintf(Str + Str_Len, NL "Registers:");
Str_Len += wsprintf(Str + Str_Len, NL "EAX: %08X EBX: %08X ECX: %08X EDX: %08X", C.Eax, C.Ebx, C.Ecx, C.Edx);
Str_Len += wsprintf(Str + Str_Len, NL "ESI: %08X EDI: %08X ESP: %08X EBP: %08X", C.Esi, C.Edi, C.Esp, C.Ebp);
Str_Len += wsprintf(Str + Str_Len, NL "EIP: %08X EFlags: %08X", C.Eip, C.EFlags);
} //if (pException)

// Save call stack info.
Str_Len += wsprintf(Str + Str_Len, NL "Call Stack:");
Get_Call_Stack(pException, Str + Str_Len);

if (Str[0] == NL[0])
lstrcpy(Str, Str + sizeof(NL) - 1);

return Str;
} //Get_Exception_Info

//*************************************************************************************
void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag)
//*************************************************************************************
// Create dump.
// pException can be either GetExceptionInformation() or NULL.
// If File_Flag = TRUE - write dump files (.dmz and .dmp) with the name of the current process.
// If Show_Flag = TRUE - show message with Get_Exception_Info() dump.
{
HANDLE hDump_File;
PCHAR Str;
DWORD Bytes;
DWORD nLen = 0;

CString strDir,strTXTFile,strDMPFile;
CString strDate,strTotal;
CTime tm = CTime::GetCurrentTime();

strDir.Format(_T("%s\\Log"),GetExePath());
strTXTFile.Format(_T("%s\\Log\\%04d-%02d-%02d %02d%02d%02d.txt"),GetExePath(),tm.GetYear(),tm.GetMonth(),
tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());
strDMPFile.Format(_T("%s\\Log\\%04d-%02d-%02d %02d%02d%02d.dmp"),GetExePath(),tm.GetYear(),tm.GetMonth(),
tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());

if(!PathFileExists(strDir))
CreateDirectory(strDir,NULL);

Str = Get_Exception_Info(pException);

//if (Show_Flag && Str)
// MessageBox(NULL, Str, "MiniDump", MB_ICONHAND | MB_OK);

if (File_Flag)
{
if (Str)
{
hDump_File = CreateFile(strTXTFile,
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

nLen = lstrlen(Str);
Str[nLen] = '\0';

WriteFile(hDump_File, Str, lstrlen(Str) + 1, &Bytes, NULL);

CloseHandle(hDump_File);
}

// If MiniDumpWriteDump() of DbgHelp.dll available.
if (MiniDumpWriteDump_)
{
MINIDUMP_EXCEPTION_INFORMATION M;

M.ThreadId = GetCurrentThreadId();
M.ExceptionPointers = pException;
M.ClientPointers = 0;

hDump_File = CreateFile(strDMPFile,
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

MiniDumpWriteDump_(GetCurrentProcess(), GetCurrentProcessId(), hDump_File,
MiniDumpNormal, (pException) ? &M : NULL, NULL, NULL);

CloseHandle(hDump_File);
}
} //if (File_Flag)

delete Str;
} //Create_Dump

[cpp]
view plaincopy

/*
Author: Vladimir Sedach.

Purpose: demo of Call Stack creation by our own means,
and with MiniDumpWriteDump() function of DbgHelp.dll.
*/

#include "StdAfx.h"
#include "MiniDump.h"

#include <Shlwapi.h>

#pragma comment(lib,"shlwapi.lib")

HMODULE hDbgHelp;
MINIDUMP_WRITE_DUMP MiniDumpWriteDump_;

CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;
MODULE32_FIRST Module32First_;
MODULE32_NEST Module32Next_;

#define DUMP_SIZE_MAX 8000 //max size of our dump
#define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls
#define NL "\r\n" //new line

extern CString GetExePath();

//****************************************************************************************
BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr)
//****************************************************************************************
// Find module by Ret_Addr (address in the module).
// Return Module_Name (full path) and Module_Addr (start address).
// Return TRUE if found.
{
MODULEENTRY32 M = {sizeof(M)};
HANDLE hSnapshot;

Module_Name[0] = 0;

if (CreateToolhelp32Snapshot_)
{
hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0);

if ((hSnapshot != INVALID_HANDLE_VALUE) &&
Module32First_(hSnapshot, &M))
{
do
{
if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)
{
lstrcpyn(Module_Name, M.szExePath, MAX_PATH);
Module_Addr = M.modBaseAddr;
break;
}
} while (Module32Next_(hSnapshot, &M));
}

CloseHandle(hSnapshot);
}

return !!Module_Name[0];
} //Get_Module_By_Ret_Addr

//******************************************************************
int WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str)
//******************************************************************
// Fill Str with call stanbsp;ck info.

// pException can be either GetExceptionInformation() or NULL.
// If pException = NULL - get current call stack.
{
CHAR Module_Name[MAX_PATH];
PBYTE Module_Addr = 0;
PBYTE Module_Addr_1;
int Str_Len;

typedef struct STACK
{
STACK * Ebp;
PBYTE Ret_Addr;
DWORD Param[0];
} STACK, * PSTACK;

STACK Stack = {0, 0};
PSTACK Ebp;

if (pException) //fake frame for exception address
{
Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp;
Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress;
Ebp = &Stack;
}
else
{
Ebp = (PSTACK)&pException - 1; //frame addr of Get_Call_Stack()

// Skip frame of Get_Call_Stack().
if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
Ebp = Ebp->Ebp; //caller ebp
}

Str[0] = 0;
Str_Len = 0;

// Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX.
// Break trace on wrong stack frame.

for (int Ret_Addr_I = 0;
(Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
Ret_Addr_I++, Ebp = Ebp->Ebp)
{
// If module with Ebp->Ret_Addr found.
if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr_1))
{
if (Module_Addr_1 != Module_Addr) //new module
{
// Save module's address and full path.
Module_Addr = Module_Addr_1;
Str_Len += wsprintf(Str + Str_Len, NL "%08X %s", Module_Addr, Module_Name);
}

// Save call offset.
Str_Len += wsprintf(Str + Str_Len,
NL " +%08X", Ebp->Ret_Addr - Module_Addr);

// Save 5 params of the call. We don't know the real number of params.
if (pException && !Ret_Addr_I) //fake frame for exception address
Str_Len += wsprintf(Str + Str_Len, " Exception Offset");
else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))
{
Str_Len += wsprintf(Str + Str_Len, " (%X, %X, %X, %X, %X)",
Ebp->Param[0], Ebp->Param[1], Ebp->Param[2], Ebp->Param[3], Ebp->Param[4]);
}
}
else
Str_Len += wsprintf(Str + Str_Len, NL "%08X", Ebp->Ret_Addr);
}

return Str_Len;
} //Get_Call_Stack

//***********************************
int WINAPI Get_Version_Str(PCHAR Str)
//***********************************
// Fill Str with Windows version.
{
OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)}; //EX for NT 5.0 and later

if (!GetVersionEx((POSVERSIONINFO)&V))
{
ZeroMemory(&V, sizeof(V));
V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx((POSVERSIONINFO)&V);
}

if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)
V.dwBuildNumber = LOWORD(V.dwBuildNumber); //for 9x HIWORD(dwBuildNumber) = 0x04xx

return wsprintf(Str,
NL "Windows: %d.%d.%d, SP %d.%d, Product Type %d", //SP - service pack, Product Type - VER_NT_WORKSTATION,...
V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor/*, V.wProductType*/);
} //Get_Version_Str

//*************************************************************
PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)
//*************************************************************
// Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str.
{
PCHAR Str;
int Str_Len;
int i;
CHAR Module_Name[MAX_PATH];
PBYTE Module_Addr;
HANDLE hFile;
FILETIME Last_Write_Time;
FILETIME Local_File_Time;
SYSTEMTIME T;

Str = new CHAR[DUMP_SIZE_MAX];

if (!Str)
return NULL;

Str_Len = 0;
Str_Len += Get_Version_Str(Str + Str_Len);

Str_Len += wsprintf(Str + Str_Len, NL "Process: ");
GetModuleFileName(NULL, Str + Str_Len, MAX_PATH);
Str_Len = lstrlen(Str);

// If exception occurred.
if (pException)
{
EXCEPTION_RECORD & E = *pException->ExceptionRecord;
CONTEXT & C = *pException->ContextRecord;

// If module with E.ExceptionAddress found - save its path and date.
if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))
{
Str_Len += wsprintf(Str + Str_Len,
NL "Module: %s", Module_Name);

if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
{
if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time))
{
FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time);
FileTimeToSystemTime(&Local_File_Time, &T);

Str_Len += wsprintf(Str + Str_Len,
NL "Date Modified: %02d/%02d/%d",
T.wMonth, T.wDay, T.wYear);
}
CloseHandle(hFile);
}
}
else
{
Str_Len += wsprintf(Str + Str_Len,
NL "Exception Addr: %08X", E.ExceptionAddress);
}

Str_Len += wsprintf(Str + Str_Len,
NL "Exception Code: %08X", E.ExceptionCode);

if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
// Access violation type - Write/Read.
Str_Len += wsprintf(Str + Str_Len,
NL "%s Address: %08X",
(E.ExceptionInformation[0]) ? "Write" : "Read", E.ExceptionInformation[1]);
}

// Save instruction that caused exception.
Str_Len += wsprintf(Str + Str_Len, NL "Instruction: ");
for (i = 0; i < 16; i++)
Str_Len += wsprintf(Str + Str_Len, " %02X", PBYTE(E.ExceptionAddress)[i]);

// Save registers at exception.
Str_Len += wsprintf(Str + Str_Len, NL "Registers:");
Str_Len += wsprintf(Str + Str_Len, NL "EAX: %08X EBX: %08X ECX: %08X EDX: %08X", C.Eax, C.Ebx, C.Ecx, C.Edx);
Str_Len += wsprintf(Str + Str_Len, NL "ESI: %08X EDI: %08X ESP: %08X EBP: %08X", C.Esi, C.Edi, C.Esp, C.Ebp);
Str_Len += wsprintf(Str + Str_Len, NL "EIP: %08X EFlags: %08X", C.Eip, C.EFlags);
} //if (pException)

// Save call stack info.
Str_Len += wsprintf(Str + Str_Len, NL "Call Stack:");
Get_Call_Stack(pException, Str + Str_Len);

if (Str[0] == NL[0])
lstrcpy(Str, Str + sizeof(NL) - 1);

return Str;
} //Get_Exception_Info

//*************************************************************************************
void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag)
//*************************************************************************************
// Create dump.
// pException can be either GetExceptionInformation() or NULL.
// If File_Flag = TRUE - write dump files (.dmz and .dmp) with the name of the current process.
// If Show_Flag = TRUE - show message with Get_Exception_Info() dump.
{
HANDLE hDump_File;
PCHAR Str;
DWORD Bytes;
DWORD nLen = 0;

CString strDir,strTXTFile,strDMPFile;
CString strDate,strTotal;
CTime tm = CTime::GetCurrentTime();

strDir.Format(_T("%s\\Log"),GetExePath());
strTXTFile.Format(_T("%s\\Log\\%04d-%02d-%02d %02d%02d%02d.txt"),GetExePath(),tm.GetYear(),tm.GetMonth(),
tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());
strDMPFile.Format(_T("%s\\Log\\%04d-%02d-%02d %02d%02d%02d.dmp"),GetExePath(),tm.GetYear(),tm.GetMonth(),
tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());

if(!PathFileExists(strDir))
CreateDirectory(strDir,NULL);

Str = Get_Exception_Info(pException);

//if (Show_Flag && Str)
// MessageBox(NULL, Str, "MiniDump", MB_ICONHAND | MB_OK);

if (File_Flag)
{
if (Str)
{
hDump_File = CreateFile(strTXTFile,
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

nLen = lstrlen(Str);
Str[nLen] = '\0';

WriteFile(hDump_File, Str, lstrlen(Str) + 1, &Bytes, NULL);

CloseHandle(hDump_File);
}

// If MiniDumpWriteDump() of DbgHelp.dll available.
if (MiniDumpWriteDump_)
{
MINIDUMP_EXCEPTION_INFORMATION M;

M.ThreadId = GetCurrentThreadId();
M.ExceptionPointers = pException;
M.ClientPointers = 0;

hDump_File = CreateFile(strDMPFile,
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

MiniDumpWriteDump_(GetCurrentProcess(), GetCurrentProcessId(), hDump_File,
MiniDumpNormal, (pException) ? &M : NULL, NULL, NULL);

CloseHandle(hDump_File);
}
} //if (File_Flag)

delete Str;
} //Create_Dump

[cpp]
view plaincopy

/*
Author: Vladimir Sedach.

Purpose: demo of Call Stack creation by our own means,
and with MiniDumpWriteDump() function of DbgHelp.dll.
*/

#include "StdAfx.h"
#include "MiniDump.h"
#include <Shlwapi.h>

#pragma comment(lib,"shlwapi.lib")

HMODULE hDbgHelp;
MINIDUMP_WRITE_DUMP MiniDumpWriteDump_;

CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;
MODULE32_FIRST Module32First_;
MODULE32_NEST Module32Next_;

#define DUMP_SIZE_MAX 8000 //max size of our dump
#define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls
#define NL "\r\n" //new line

extern CString GetExePath();

//****************************************************************************************
BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr)
//****************************************************************************************
// Find module by Ret_Addr (address in the module).
// Return Module_Name (full path) and Module_Addr (start address).
// Return TRUE if found.
{
MODULEENTRY32 M = {sizeof(M)};
HANDLE hSnapshot;

Module_Name[0] = 0;

if (CreateToolhelp32Snapshot_)
{
hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0);

if ((hSnapshot != INVALID_HANDLE_VALUE) &&
Module32First_(hSnapshot, &M))
{
do
{
if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)
{
lstrcpyn(Module_Name, M.szExePath, MAX_PATH);
Module_Addr = M.modBaseAddr;
break;
}
} while (Module32Next_(hSnapshot, &M));
}

CloseHandle(hSnapshot);
}

return !!Module_Name[0];
} //Get_Module_By_Ret_Addr

//******************************************************************
int WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str)
//******************************************************************
// Fill Str with call stanbsp;ck info.
// pException can be either GetExceptionInformation() or NULL.
// If pException = NULL - get current call stack.
{
CHAR Module_Name[MAX_PATH];
PBYTE Module_Addr = 0;
PBYTE Module_Addr_1;
int Str_Len;

typedef struct STACK
{
STACK * Ebp;
PBYTE Ret_Addr;
DWORD Param[0];
} STACK, * PSTACK;

STACK Stack = {0, 0};
PSTACK Ebp;

if (pException) //fake frame for exception address
{
Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp;
Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress;
Ebp = &Stack;
}
else
{
Ebp = (PSTACK)&pException - 1; //frame addr of Get_Call_Stack()

// Skip frame of Get_Call_Stack().
if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
Ebp = Ebp->Ebp; //caller ebp
}

Str[0] = 0;
Str_Len = 0;

// Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX.
// Break trace on wrong stack frame.
for (int Ret_Addr_I = 0;
(Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
Ret_Addr_I++, Ebp = Ebp->Ebp)
{
// If module with Ebp->Ret_Addr found.
if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr_1))
{
if (Module_Addr_1 != Module_Addr) //new module
{
// Save module's address and full path.
Module_Addr = Module_Addr_1;
Str_Len += wsprintf(Str + Str_Len, NL "%08X %s", Module_Addr, Module_Name);
}

// Save call offset.
Str_Len += wsprintf(Str + Str_Len,
NL " +%08X", Ebp->Ret_Addr - Module_Addr);

// Save 5 params of the call. We don't know the real number of params.
if (pException && !Ret_Addr_I) //fake frame for exception address
Str_Len += wsprintf(Str + Str_Len, " Exception Offset");
else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))
{
Str_Len += wsprintf(Str + Str_Len, " (%X, %X, %X, %X, %X)",
Ebp->Param[0], Ebp->Param[1], Ebp->Param[2], Ebp->Param[3], Ebp->Param[4]);
}
}
else
Str_Len += wsprintf(Str + Str_Len, NL "%08X", Ebp->Ret_Addr);
}

return Str_Len;
} //Get_Call_Stack

//***********************************
int WINAPI Get_Version_Str(PCHAR Str)
//***********************************
// Fill Str with Windows version.
{
OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)}; //EX for NT 5.0 and later

if (!GetVersionEx((POSVERSIONINFO)&V))
{
ZeroMemory(&V, sizeof(V));
V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx((POSVERSIONINFO)&V);
}

if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)
V.dwBuildNumber = LOWORD(V.dwBuildNumber); //for 9x HIWORD(dwBuildNumber) = 0x04xx

return wsprintf(Str,
NL "Windows: %d.%d.%d, SP %d.%d, Product Type %d", //SP - service pack, Product Type - VER_NT_WORKSTATION,...
V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor/*, V.wProductType*/);
} //Get_Version_Str

//*************************************************************
PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)
//*************************************************************
// Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str.
{
PCHAR Str;
int Str_Len;
int i;
CHAR Module_Name[MAX_PATH];
PBYTE Module_Addr;
HANDLE hFile;
FILETIME Last_Write_Time;
FILETIME Local_File_Time;
SYSTEMTIME T;

Str = new CHAR[DUMP_SIZE_MAX];

if (!Str)
return NULL;

Str_Len = 0;
Str_Len += Get_Version_Str(Str + Str_Len);

Str_Len += wsprintf(Str + Str_Len, NL "Process: ");
GetModuleFileName(NULL, Str + Str_Len, MAX_PATH);
Str_Len = lstrlen(Str);

// If exception occurred.
if (pException)
{
EXCEPTION_RECORD & E = *pException->ExceptionRecord;
CONTEXT & C = *pException->ContextRecord;

// If module with E.ExceptionAddress found - save its path and date.
if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))
{
Str_Len += wsprintf(Str + Str_Len,
NL "Module: %s", Module_Name);

if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
{
if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time))
{
FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time);
FileTimeToSystemTime(&Local_File_Time, &T);

Str_Len += wsprintf(Str + Str_Len,
NL "Date Modified: %02d/%02d/%d",
T.wMonth, T.wDay, T.wYear);
}
CloseHandle(hFile);
}
}
else
{
Str_Len += wsprintf(Str + Str_Len,
NL "Exception Addr: %08X", E.ExceptionAddress);
}

Str_Len += wsprintf(Str + Str_Len,
NL "Exception Code: %08X", E.ExceptionCode);

if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
// Access violation type - Write/Read.
Str_Len += wsprintf(Str + Str_Len,
NL "%s Address: %08X",
(E.ExceptionInformation[0]) ? "Write" : "Read", E.ExceptionInformation[1]);
}

// Save instruction that caused exception.
Str_Len += wsprintf(Str + Str_Len, NL "Instruction: ");
for (i = 0; i < 16; i++)
Str_Len += wsprintf(Str + Str_Len, " %02X", PBYTE(E.ExceptionAddress)[i]);

// Save registers at exception.
Str_Len += wsprintf(Str + Str_Len, NL "Registers:");
Str_Len += wsprintf(Str + Str_Len, NL "EAX: %08X EBX: %08X ECX: %08X EDX: %08X", C.Eax, C.Ebx, C.Ecx, C.Edx);
Str_Len += wsprintf(Str + Str_Len, NL "ESI: %08X EDI: %08X ESP: %08X EBP: %08X", C.Esi, C.Edi, C.Esp, C.Ebp);
Str_Len += wsprintf(Str + Str_Len, NL "EIP: %08X EFlags: %08X", C.Eip, C.EFlags);
} //if (pException)

// Save call stack info.
Str_Len += wsprintf(Str + Str_Len, NL "Call Stack:");
Get_Call_Stack(pException, Str + Str_Len);

if (Str[0] == NL[0])
lstrcpy(Str, Str + sizeof(NL) - 1);

return Str;
} //Get_Exception_Info

//*************************************************************************************
void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag)
//*************************************************************************************
// Create dump.
// pException can be either GetExceptionInformation() or NULL.
// If File_Flag = TRUE - write dump files (.dmz and .dmp) with the name of the current process.
// If Show_Flag = TRUE - show message with Get_Exception_Info() dump.
{
HANDLE hDump_File;
PCHAR Str;
DWORD Bytes;
DWORD nLen = 0;

CString strDir,strTXTFile,strDMPFile;
CString strDate,strTotal;
CTime tm = CTime::GetCurrentTime();

strDir.Format(_T("%s\\Log"),GetExePath());
strTXTFile.Format(_T("%s\\Log\\%04d-%02d-%02d %02d%02d%02d.txt"),GetExePath(),tm.GetYear(),tm.GetMonth(),
tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());
strDMPFile.Format(_T("%s\\Log\\%04d-%02d-%02d %02d%02d%02d.dmp"),GetExePath(),tm.GetYear(),tm.GetMonth(),
tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());

if(!PathFileExists(strDir))
CreateDirectory(strDir,NULL);

Str = Get_Exception_Info(pException);

//if (Show_Flag && Str)
// MessageBox(NULL, Str, "MiniDump", MB_ICONHAND | MB_OK);

if (File_Flag)
{
if (Str)
{
hDump_File = CreateFile(strTXTFile,
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

nLen = lstrlen(Str);
Str[nLen] = '\0';

WriteFile(hDump_File, Str, lstrlen(Str) + 1, &Bytes, NULL);

CloseHandle(hDump_File);
}

// If MiniDumpWriteDump() of DbgHelp.dll available.
if (MiniDumpWriteDump_)
{
MINIDUMP_EXCEPTION_INFORMATION M;

M.ThreadId = GetCurrentThreadId();
M.ExceptionPointers = pException;
M.ClientPointers = 0;

hDump_File = CreateFile(strDMPFile,
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

MiniDumpWriteDump_(GetCurrentProcess(), GetCurrentProcessId(), hDump_File,
MiniDumpNormal, (pException) ? &M : NULL, NULL, NULL);

CloseHandle(hDump_File);
}
} //if (File_Flag)

delete Str;
} //Create_Dump

具体参考方法如下:
1、在CXXDlg::OnInitDialog()中添加这样一段:

view plaincopy
to clipboard

SetUnhandledExceptionFilter(CrashReportEx);
HMODULE hKernel32;

// Try to get MiniDumpWriteDump() address.
hDbgHelp = LoadLibrary("DBGHELP.DLL");
MiniDumpWriteDump_ = (MINIDUMP_WRITE_DUMP)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
// d("hDbgHelp=%X, MiniDumpWriteDump_=%X", hDbgHelp, MiniDumpWriteDump_);

// Try to get Tool Help library functions.

hKernel32 = GetModuleHandle("KERNEL32");
CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32First");
Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32Next");

[cpp]
view plaincopy

SetUnhandledExceptionFilter(CrashReportEx);
HMODULE hKernel32;

// Try to get MiniDumpWriteDump() address.
hDbgHelp = LoadLibrary("DBGHELP.DLL");
MiniDumpWriteDump_ = (MINIDUMP_WRITE_DUMP)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
// d("hDbgHelp=%X, MiniDumpWriteDump_=%X", hDbgHelp, MiniDumpWriteDump_);

// Try to get Tool Help library functions.
hKernel32 = GetModuleHandle("KERNEL32");
CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32First");
Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32Next");

[cpp]
view plaincopy

SetUnhandledExceptionFilter(CrashReportEx);
HMODULE hKernel32;

// Try to get MiniDumpWriteDump() address.
hDbgHelp = LoadLibrary("DBGHELP.DLL");
MiniDumpWriteDump_ = (MINIDUMP_WRITE_DUMP)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
// d("hDbgHelp=%X, MiniDumpWriteDump_=%X", hDbgHelp, MiniDumpWriteDump_);

// Try to get Tool Help library functions.
hKernel32 = GetModuleHandle("KERNEL32");
CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32First");
Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32Next");

测试代码如下:

view plaincopy
to clipboard

class CTestDlg : public CDialog
{
// Construction
public:
CTestDlg(CWnd* pParent = NULL); // standard constructor

void Fun1(char *pszBuffer);
void Fun2(char *pszBuffer);
void Fun3(char *pszBuffer);
};

[cpp]
view plaincopy

class CTestDlg : public CDialog
{
// Construction
public:
CTestDlg(CWnd* pParent = NULL); // standard constructor

void Fun1(char *pszBuffer);
void Fun2(char *pszBuffer);
void Fun3(char *pszBuffer);
};

[cpp]
view plaincopy

class CTestDlg : public CDialog
{
// Construction
public:
CTestDlg(CWnd* pParent = NULL); // standard constructor

void Fun1(char *pszBuffer);
void Fun2(char *pszBuffer);
void Fun3(char *pszBuffer);
};

view plaincopy
to clipboard

void CTestDlg::Fun1(char *pszBuffer)
{
Fun2(pszBuffer);
}

void CTestDlg::Fun2(char *pszBuffer)
{
Fun3(pszBuffer);
}

void CTestDlg::Fun3(char *pszBuffer)
{
pszBuffer[1] = 0x00;
}

[cpp]
view plaincopy

void CTestDlg::Fun1(char *pszBuffer)
{
Fun2(pszBuffer);
}

void CTestDlg::Fun2(char *pszBuffer)
{
Fun3(pszBuffer);
}

void CTestDlg::Fun3(char *pszBuffer)
{
pszBuffer[1] = 0x00;
}

[cpp]
view plaincopy

void CTestDlg::Fun1(char *pszBuffer)
{
Fun2(pszBuffer);
}

void CTestDlg::Fun2(char *pszBuffer)
{
Fun3(pszBuffer);
}

void CTestDlg::Fun3(char *pszBuffer)
{
pszBuffer[1] = 0x00;
}

我们在双击确定按钮时的响应代码如下:

view plaincopy
to clipboard

void CTestDlg::OnOK()
{
// TODO: Add extra validation here
Fun1(NULL);
}

[cpp]
view plaincopy

void CTestDlg::OnOK()
{
// TODO: Add extra validation here
Fun1(NULL);
}

[cpp]
view plaincopy

void CTestDlg::OnOK()
{
// TODO: Add extra validation here
Fun1(NULL);
}

2、设置VC编译选项,勾选生成MAP和Debug Info:



在工程选项中加上命令/opt:ref /mapinfo:lines

3、将编译生成的Release目录中的pdb、map文件保存起来,以后调试会用到:



4、运行程序,单击确定按钮出现异常后自动重启,并创建一个Log文件夹,里面生成dump文件:



5、我们打开WinDbg,设置一下pdb路径(File \ Symbol File Path):



6、用WiinDbg打开dump文件(File \ Open Crash Dump)



7、输入命令!analyze -v,等待几秒后会打印出错误信息,函数调用栈如下图:



OK ,这样我们就能在发布版本的程序中,准确的定位到哪个函数出了问题,所以发布程序时,一定要记得生成pdb、map文件,不然客户运行出错的话,你不死也残!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: