之前碰到论坛里有几个好友,说程序不时的崩溃,什么xxoo不能read的! 如果光要是这个内存地址,估计你会疯掉~~
所以分享一下基本的调试技巧,需要准备的工具有WinDbg + VS2022,
下面是自己整理的一份自动生成DUMP文件的源代码,只需要添加到工程即可,源代码如下:
MTCrashDump.h
#include <windows.h>
#include <tlhelp32.h>
#include "dbghelp.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
// 异常崩溃消息 by MT
#define WM_MSG_RESTART (WM_USER + 1000)
extern void setMainWindow(HWND hWnd);
extern void initDump();
MTCrashDump.cpp
#include "pch.h"
#include "MTCrashDump.h"
#include <Shlwapi.h>
#pragma comment(lib,"shlwapi.lib")
#pragma comment(lib, "Dbghelp.lib")
HWND g_hWnd = NULL;
// 主界面异常崩溃消息 by MT
void setMainWindow(HWND hWnd)
{
g_hWnd = hWnd;
}
CString GetExePath()
{
CString strPath;
// 获取路径
TCHAR pszFilePath[MAX_PATH] = { 0x00 };
memset(pszFilePath, 0 , sizeof(pszFilePath) * sizeof(char));
GetModuleFileName(NULL, pszFilePath, MAX_PATH);
// 去除末尾的"\xxx.exe"
TCHAR *pPos = NULL;
pPos = _tcsrchr(pszFilePath, '\\');
*pPos = NULL;
// 给末尾添加上"\"
if (pszFilePath[_tcslen(pszFilePath)-1] != '\\')
_tcscat(pszFilePath, _T("\\"));
strPath.Format(_T("%s"), pszFilePath);
return strPath;
}
static BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{
if (pModuleName == 0)
{
return FALSE;
}
WCHAR szFileName[_MAX_FNAME] = L"";
//_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
_wsplitpath_s(pModuleName, NULL, 0, NULL, 0, szFileName, _MAX_FNAME, NULL, 0);
//if (wcsicmp(szFileName, L"ntdll") == 0)
if (_wcsicmp(szFileName, L"ntdll") == 0)
return TRUE;
return FALSE;
}
static BOOL CALLBACK MiniDumpCallback(PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput)
{
if (pInput == 0 || pOutput == 0)
return FALSE;
switch (pInput->CallbackType)
{
case ModuleCallback:
if (pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
if (!IsDataSectionNeeded(pInput->Module.FullPath))
pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
case IncludeModuleCallback:
case IncludeThreadCallback:
case ThreadCallback:
case ThreadExCallback:
return TRUE;
default:;
}
return FALSE;
}
static void CreateMiniDump(PEXCEPTION_POINTERS pep)
{
CString strDir, strPath;
CTime tm = CTime::GetCurrentTime();
HANDLE hFile = INVALID_HANDLE_VALUE;
strDir.Format(_T("%s\\log"), GetExePath());
if (!PathFileExists(strDir))
CreateDirectory(strDir, NULL);
strPath.Format(_T("%s\\log\\%04d-%02d-%02d %02d%02d%02d.dmp"), GetExePath(), tm.GetYear(), tm.GetMonth(), tm.GetDay(), tm.GetHour(), tm.GetMinute(), tm.GetSecond());
hFile = CreateFile(strPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
{
MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = pep;
mdei.ClientPointers = NULL;
//MINIDUMP_CALLBACK_INFORMATION mci;
//mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
//mci.CallbackParam = 0;
//::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpWithFullMemory, (pep != 0) ? &mdei : 0, NULL, &mci);
::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpWithFullMemory, (pep != 0) ? &mdei : 0, NULL, NULL);
FlushFileBuffers(hFile);
CloseHandle(hFile);
}
}
/*
异常处理模块
*/
LONG WINAPI CrashReportEx(LPEXCEPTION_POINTERS ExceptionInfo)
{
TCHAR szFileName[MAX_PATH] = { 0x00 };
UINT nRet = 0;
// 创建DUMP文件
CreateMiniDump(ExceptionInfo);
if (g_hWnd)
SendMessage(g_hWnd, WM_MSG_RESTART, NULL, NULL);
return EXCEPTION_EXECUTE_HANDLER;
}
void initDump()
{
SetUnhandledExceptionFilter(CrashReportEx);
}
具体参考方法如下:
1、在CXXDlg::OnInitDialog()中添加这样一段:
#include "Dump/MTCrashDump.h"
// 主界面异常崩溃消息 by MT
LRESULT CDumpMsgDlg::OnMsgRestart(WPARAM wParam, LPARAM lParam)
{
TCHAR szPath[MAX_PATH] = { 0x00 };
TCHAR szDir[MAX_PATH] = { 0x00 };
TCHAR szCmdLine[MAX_PATH] = { 0x00 };
CString strExe = GetExePath();
STARTUPINFO si;
PROCESS_INFORMATION pi;
BOOL bResult = FALSE;
// 在这个回调函数做主界面初始化操作的,释放内存啥的
// ...
// 设置进程信息
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
GetModuleFileName(NULL, szPath, MAX_PATH);
wsprintf(szDir, _T("%s"), strExe);
// 启动参数 111.exe -a -b
wsprintf(szCmdLine, _T(" -a -b"));
// 启动进程
bResult = CreateProcess(szPath, szCmdLine, NULL, NULL, FALSE, NULL, NULL, szDir, &si, &pi);
if (!bResult)
{
//AfxMessageBox(_T("启动失败!"));
return 0;
}
//AfxMessageBox(_T("启动成功!"));
return 1L;
}
BOOL CDumpMsgDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// 主界面异常崩溃消息 by MT
setMainWindow(this->GetSafeHwnd());
// 注册异常回调 by MT
initDump();
// 初始化成员量的指针,为后续模拟崩溃操作
m_pData = new UCHAR[10];
for (int i = 0; i < 10; i++)
m_pData[i] = i;
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
// 模拟主界面异常崩溃 by MT
// 工程属性,调试,生成调试信息 (/DEBUG)
void CDumpMsgDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString strMsg;
memset(m_pData, 0x00, 4096);
strMsg.Format(_T("Val = %02x - %02X"), m_pData[11], m_pData[1024]);
MessageBox(strMsg);
}
图(1):
图(2):
图(3):
图(4):
图(5):
图(6):
图(7):
图(8):
图(9):