1. 程式人生 > >編寫的windows程式,崩潰時產生crash dump檔案的辦法

編寫的windows程式,崩潰時產生crash dump檔案的辦法

一、引言

dump檔案是C++程式發生異常時,儲存當時程式執行狀態的檔案,是除錯異常程式重要的方法,所以程式崩潰時,除了日誌檔案,dump檔案便成了我們查詢錯誤的最後一根救命的稻草。windows程式產生dump檔案和linux程式產生dump檔案的方式不一樣,linux預設是不讓產生core dump檔案,只要在使用者自己的~/.bash_profile檔案中增加

ulimit -S -c unlimited > /dev/null 2>&1

這樣程式崩潰就可以產生可除錯的core dump檔案了。但是windows環境就得寫程式碼才能實現了。

二、原理

windows程式當遇到異常,沒有try-catch或者try-catch也無法捕獲到的異常時,程式就會自動退出,如果這時候沒有dump檔案的話,我們是沒有得到任何程式退出的資訊。在windows程式異常退出之前,會預先呼叫一個在程式中註冊的異常處理回撥函式(預設是沒有設定),只要我們在這個回撥函式中呼叫MiniDumpWriteDump函式就可以產生我們想要的dump檔案。

三、實現

1.呼叫SetUnhandledExceptionFilter註冊一個自定義的異常處理回撥函式

SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);

異常處理回撥函式的原型

LONG __stdcall MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);

2.CreateFile建立dump檔案,呼叫MiniDumpWriteDump函式往dump檔案寫異常資訊

inline void CreateMiniDump(PEXCEPTION_POINTERS pep, LPCTSTR strFileName)
{
	HANDLE hFile = CreateFile(strFileName, 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, MiniDumpNormal, (pep != 0) ? &mdei : 0, NULL, &mci);

		CloseHandle(hFile);
	}
}

CreateMiniDump函式是在異常處理回撥函式MyUnhandledExceptionFilter中呼叫的
LONG __stdcall MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
{
	CreateMiniDump(pExceptionInfo, "core.dmp");

	return EXCEPTION_EXECUTE_HANDLER;
}

3.將SetUnhandledExceptionFilter失效

vs2005中,編譯的過程中,編譯器會自動給你的程式加上一句SetUnhandledExceptionFilter(NULL),這就會導致你之前自定義的

SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);

無效,就有可能不會產生dump檔案,因此我們必須在自定義的SetUnhandledExceptionFilter之後,讓之後呼叫的SetUnhandledExceptionFilter無效。增加以下程式碼:

// 此函式一旦成功呼叫,之後對 SetUnhandledExceptionFilter 的呼叫將無效
void DisableSetUnhandledExceptionFilter()
{
	void* addr = (void*)GetProcAddress(LoadLibrary("kernel32.dll"),
		"SetUnhandledExceptionFilter");

	if (addr)
	{
		unsigned char code[16];
		int size = 0;

		code[size++] = 0x33;
		code[size++] = 0xC0;
		code[size++] = 0xC2;
		code[size++] = 0x04;
		code[size++] = 0x00;

		DWORD dwOldFlag, dwTempFlag;
		VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
		WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
		VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
	}
}

最終程式碼整理:

//minidump.h

#pragma once
#include <windows.h>
#include <DbgHelp.h>
#include <stdlib.h>
#pragma comment(lib, "dbghelp.lib")

#ifndef _M_IX86
#error "The following code only works for x86!"
#endif

inline BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{
	if(pModuleName == 0)
	{
		return FALSE;
	}

	WCHAR szFileName[_MAX_FNAME] = L"";
	_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);

	if(wcsicmp(szFileName, L"ntdll") == 0)
		return TRUE;

	return FALSE;
}

inline 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;
}

inline void CreateMiniDump(PEXCEPTION_POINTERS pep, LPCTSTR strFileName)
{
	HANDLE hFile = CreateFile(strFileName, 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, MiniDumpNormal, (pep != 0) ? &mdei : 0, NULL, &mci);

		CloseHandle(hFile);
	}
}

LONG __stdcall MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
{
	CreateMiniDump(pExceptionInfo, "core.dmp");

	return EXCEPTION_EXECUTE_HANDLER;
}

// 此函式一旦成功呼叫,之後對 SetUnhandledExceptionFilter 的呼叫將無效
void DisableSetUnhandledExceptionFilter()
{
	void* addr = (void*)GetProcAddress(LoadLibrary("kernel32.dll"),
		"SetUnhandledExceptionFilter");

	if (addr)
	{
		unsigned char code[16];
		int size = 0;

		code[size++] = 0x33;
		code[size++] = 0xC0;
		code[size++] = 0xC2;
		code[size++] = 0x04;
		code[size++] = 0x00;

		DWORD dwOldFlag, dwTempFlag;
		VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
		WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
		VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
	}
}

void InitMinDump()
{
	//註冊異常處理函式
	SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);

	//使SetUnhandledExceptionFilter
	DisableSetUnhandledExceptionFilter();
}

4.測試程式碼

//test.cpp

#include <iostream>
#include "minidump.h"
void test()
{
	std::string s = "abcd";

	try{
		s[100] = 'b';
	}
	catch(std::exception& e)
	{
		std::cout << "with exception:[" << e.what() << "]" << std::endl;
	}
	catch(...)
	{
		std::cout << "with unknown exception" << std::endl;
	}
}

void main()
{
	InitMinDump();

	test();

	system("pause");
}


相關推薦

編寫windows程式崩潰產生crash dump檔案辦法

一、引言 dump檔案是C++程式發生異常時,儲存當時程式執行狀態的檔案,是除錯異常程式重要的方法,所以程式崩潰時,除了日誌檔案,dump檔案便成了我們查詢錯誤的最後一根救命的稻草。windows程式產生dump檔案和linux程式產生dump檔案的方式不一樣,linux預

編寫windows程式崩潰產生crash dump檔案辦法 .

一、引言 dump檔案是C++程式發生異常時,儲存當時程式執行狀態的檔案,是除錯異常程式重要的方法,所以程式崩潰時,除了日誌檔案,dump檔案便成了我們查詢錯誤的最後一根救命的稻草。windows程式產生dump檔案和linux程式產生dump檔案的方式不一樣,linux預

如何在程式異常或者崩潰產生一個dump檔案

核心API是: CreateFile() MinDumpWriteDump() 需要包含標頭檔案: #include <DbgHelp.h> #pragma comment(lib, "d

windows程式崩潰自動生成dump檔案方法

  /****************第一步新增createdump.h********************************* 新增一個頭檔案:createdump.h #pragma once #include <windows.h> #inclu

利用Windows自帶的功能當程式崩潰產生崩潰轉儲檔案(dmp)

 何志丹 以管理員身份 執行 :OpenDump.bat 其本質是寫登錄檔。 執行後: 任何程式崩潰都會在C:\CrashDump 產生dmp檔案(比較大,約50到200M)。 至少在Win7、Win10的電腦,Win10的平板上執行正確。 OpenDump.bat @e

windows下idea編寫WordCount程式並打jar包上傳到hadoop叢集執行(傻瓜版)

通常會在IDE中編制程式,然後打成jar包,然後提交到叢集,最常用的是建立一個Maven專案,利用Maven來管理jar包的依賴。 一、生成WordCount的jar包 1. 開啟IDEA,File→New→Project→Maven→Next→填寫Groupld和Artifactld→Ne

c# 編寫windows 服務並制作安裝包

階段 其他 編譯 code 工程 component partial 控制臺程序 自定義   對服務的認識有很多個階段。   第一階段:當時還在用c++,知道在一個進程裏while(True){},然後裏面做很多很多事情,這就叫做服務了,界面可能當時還用Console控

P68——編寫一個程式讀取一個浮點數先列印成再列印成如果系統......

#include <stdio.h> int main(void) { float a; printf("enter a floating-point value:"); scanf("%f",&a); printf("fixed-point notation:

P68——編寫一個程式發出一聲警報然後列印下面的文字

要列印的文字內容: Startled by the sudden sound,sally shouted, "By the great pumkin,wahat was that!" 程式程式碼如下: #include <stdio.h> int main(void) {

P68——編寫一個程式要求提示輸入一個ASCII碼值(如66)然後列印輸入的字元

錯誤程式碼如下: 錯在定義了錯誤的輸入物件型別,使用者輸入時也弄錯了輸入型別, 更改後正確程式碼如下: #include <stdio.h> int main(void) { int a; printf("please enter a ASCII ma:");

山科java實驗4-1 編寫一個程式,使用者可以從鍵盤錄入若干個學生的姓名和分數(程式每次提示使用者輸入“Y”或“N”決定是否繼續錄入學生資訊如果使用者輸入“N”則使用者輸入完畢。輸入的“Y”、“N”不區分

編寫一個程式,使用者可以從鍵盤錄入若干個學生的姓名和分數(程式每次提示使用者輸入“Y”或“N”決定是否繼續錄入學生資訊,如果使用者輸入“N”則使用者輸入完畢。輸入的“Y”、“N”不區分大小寫)。使用者錄入完畢後,程式按成績由高到低的順序輸出學生的姓名和分數(姓名和分數之間用一個空格分割)。【說明

c語言 編寫一個程式輸入a b c三個值輸出其中最大者

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

用指標方法編寫一個程式輸入3個整數將它們按由小到大的順序輸出

#include <stdio.h> void swap(int *pa,int *pb) { int temp; temp = *pa; *pa = *pb; *pb = temp; } void main() { int

編寫一個程式指定一個資料夾能自動計算出其總容量

package filetest;import java.io.File;import java.io.IOException; public class FileEdit { double size=0.0; //計算檔案或資料夾的大小,單位MB public double getSize(File fi

編寫一個程式啟動三個執行緒三個執行緒的名稱分別是 ABC; 每個執行緒將自己的名稱在螢幕上列印5遍列印順序是ABCABC...

設定標誌位flag 當flag==1時,列印A 當flag==2時,列印B 當flag==3時,列印C 用count控制列印的次數,題目要求列印5遍,即15個字元 這裡的用notifyAll()的原因:是要把其餘兩個全都喚醒,因為如果用notify

編寫一個程式它從標準輸入讀取C原始碼並驗證所有的花括號都正確的成對出現。

編寫一個程式,它從標準輸入讀取C原始碼,並驗證所有的花括號都正確的成對出現。 (VS  ctrl+z退出  回車即可) int Is_judge_both() { char ch = 0; int count = 0; while((ch=getchar()) !=

編寫一個程式對一個整型陣列的元素求和結果使用全域性變數sum儲存另外對陣列元素中的奇數求和結果使用區域性變數sum儲存在主程式將兩個結果輸出。

#include<iostream> using namespace std; int sum_1(int a[]);//定義計算奇數的和; int main() { int a[]={1,2,3,4,5,6,7,8,9,10};//定義陣列並附初值; int i,sum=0,s

IDA附加除錯apk程式並修改記憶體編寫IDA指令碼程式把修改後的dex檔案dump到本地

我的測試環境:模擬器 Android 4.2 armeavi-v7a   1.IDA附加除錯apk程式 找到IDA所在目錄,在dbgsrv資料夾下找到程式android_server 在這裡按住“Shift +滑鼠右鍵”,開啟控制檯 把程式android_server

.編寫一個程式可以一直接收鍵盤字元 如果是小寫字元就輸出對應的大寫字元 如果接收的是大寫字元就輸出對應的小寫字元 如果是數字不輸出。

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include<stdlib.h> int main() { int i = 0; printf("請輸入一個數字:"); scanf("%d", &i);

python3 自己編寫一個程式把大寫字母轉換為小寫字母(即自程式設計實現了 字串的lower()方法)

 #定義一個函式,可以把列表輸入的帶有大寫字母的列表,轉換為全小寫字母 #比如:輸入列表['Hello','worlD','HAHA']  ====> ['hello','world','haha'] #程式碼如下: #定義一個函式,可以把列表輸入的帶有大寫字母的列表