李sir_Blog

博客园 首页 联系 订阅 管理

MiniDumpWriteDump是MS DbgHelp.dll 中一个API, 用于导出当前运行的程序的Dump. 这个dll程序系统中就有, 但是很多软件, 都在自己的安装目录下保存了这个.dll的最新的版本.

为了测试这个API, 参考网上一些资料, 写了一个简单的C++ 程序. 目的是当有异常发生的时候, 自动生成Dump文件供之后的分析. 有了Dump文件, 我们就可以使用WinDBG等调试器来分析异常发生时的情况. 其实这个功能很多软件都有, 比如QQ, 魔兽世界, 等等. 它们在出现了异常的时候会弹出一个对话框, 让用户输入异常发生时的情况, 然后把异常的dump文件用email发回, 供开发者们分析修改bug.

不过有一点, 这里需要程序的调试符号文件(pdb文件). 对于Debug版来说, 是生成的, 但是Release版来说默认是不生成的. 可以设置VC的编译器, 让它在Release版的时候也生成调试信息. 这带来一个新的问题, 因为.pdb里面是保存了源文件的信息的, 为了避免泄密, 可以采用VS中的CVPack工具, 从中去除敏感的信息.

程序需要使用Dbghelp.hDbghelp.lib . 它们可以从MSDN找到.

 

//最主要的函数, 生成Dump
static void DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo)
{
if (excpInfo == NULL) //如果没有传入异常, 比如是在程序里面调用的, 生成一个异常
{
// Generate exception to get proper context in dump
__try
{
OutputDebugString(_T("raising exception\r\n"));
RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
}
__except(DumpMiniDump(hFile, GetExceptionInformation()),
EXCEPTION_CONTINUE_EXECUTION)
{
}
}
else
{
OutputDebugString(_T("writing minidump\r\n"));
MINIDUMP_EXCEPTION_INFORMATION eInfo;
eInfo.ThreadId = GetCurrentThreadId(); //把需要的信息添进去
eInfo.ExceptionPointers = excpInfo;
eInfo.ClientPointers = FALSE;

    // 调用, 生成Dump. 98不支持
// Dump的类型是小型的, 节省空间. 可以参考MSDN生成更详细的Dump
.
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpNormal,
excpInfo ? &eInfo : NULL,
NULL,
NULL);
}
}

下面的是程序部分:

int _tmain(int argc, _TCHAR* argv[])
{
// 创建一个Dump文件
HANDLE hFile = CreateFile( _T("MiniDump.dmp"), GENERIC_READ | GENERIC_WRITE,
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
int code;
__try
{   
// 把自己实现的main函数包装一下, 放在try .. except 块中. 这样出现了异常可以自动生成dump
main_wrapper(argc, argv);
}
__except( code=GetExceptionCode(), DumpMiniDump(hFile, GetExceptionInformation() ),       EXCEPTION_EXECUTE_HANDLER ) //出现了异常, 记录异常的code, 生成dump!!
{
printf("%x\n", code);
wchar_t msg[512];
wsprintf(msg, L"Exception happened. Exception code is %x", code);
MessageBox(NULL, msg, L"Exception", MB_OK); //显示消息给用户
}
CloseHandle( hFile ); //关闭Dump文件
getchar();
return 0;
}

最下面是两个测试的函数, main_wrapper函数将调用test1, test1将会生成一个异常(非法内存写)

void test1() {
int *p;
p = (int*)0x100;
*p = 0; //写0x100地址, 这个是非法的
}

void main_wrapper(int argc, _TCHAR* argv[]) {
test1();
}

运行, 异常被捕获了:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

同时, dump文件也生成了:


用WinDBG打开Dump文件, 可以清楚的看出异常出现的情况:




从中可以比较清楚的看到异常发生的情况(Exception code), 异常出现的地址(test1函数, 偏移0x28). 因为这次测试的是Debug版, 有保存了源代码的.pdb文件, 所以WinDbg把源代码也列出来了. 这样可以非常容易的发现问题.

============================================

摘录自msdn

/****************************** Module Header ******************************\
Module Name:  CppExceptionHandling.cpp
Project:      CppExceptionHandling
Copyright (c) Microsoft Corporation.

- to be finished.

This source is subject to the Microsoft Public License.
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
All other rights reserved.

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/

#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <stdio.h>
#include <strsafe.h>
#include <shlwapi.h>
#include <dbghelp.h>
#pragma comment(lib, "Dbghelp.lib")
#pragma comment(lib, "shlwapi.lib")


// This method is to write mini dump of current process to
// CppExceptionHandling.dmp file.
LONG WINAPI WriteMiniDump(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
 // EXCEPTION_CONTINUE_SEARCH means it continue to
 // execute subsequent exception handlers.
 LONG rc = EXCEPTION_CONTINUE_SEARCH;

 HMODULE hDll = NULL;
 TCHAR szDumpFile[MAX_PATH];
 StringCchPrintf(szDumpFile, MAX_PATH, _T("CppExceptionHandling.dmp"));

 HANDLE hDumpFile;
 hDumpFile = CreateFile(szDumpFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

 if (hDumpFile != INVALID_HANDLE_VALUE)
 {
  MINIDUMP_EXCEPTION_INFORMATION ExInfo;

  ExInfo.ThreadId = GetCurrentThreadId();
  ExInfo.ExceptionPointers = ExceptionInfo;
  ExInfo.ClientPointers = TRUE;

  // Write the information into the dump
  if (MiniDumpWriteDump(
   GetCurrentProcess(), // Handle of process
   GetCurrentProcessId(), // Process Id
   hDumpFile,    // Handle of dump file
   MiniDumpNormal,   // Dump Level: Mini
   &ExInfo,    // Exception information
   NULL,     // User stream parameter
   NULL))     // Callback Parameter
  {
   rc = EXCEPTION_CONTINUE_SEARCH;
  }
  else
  {
   _tprintf(_T("MiniDumpWriteDump failed w/err 0x%08lx\n"), GetLastError());
  }

  CloseHandle(hDumpFile);
 }
 else
 {
  _tprintf(_T("CreateFile failed w/err 0x%08lx\n"), GetLastError());
 }

 return rc;
}


#pragma region Structured Exception

void ThrowStructuredException()
{
}

void ThrowAndHandleStructuredException()
{
}

#pragma endregion


#pragma region Vectored Exception

// The registered exception handler
LONG WINAPI VectoredExceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
{   
 WriteMiniDump (ExceptionInfo);
 printf("This vectored handler is always called first\n");
 return EXCEPTION_CONTINUE_SEARCH;
}

void ThrowAndHandleVectoredException()
{
 // Registers a vectored exception handler
 PVOID handler = AddVectoredExceptionHandler(0, VectoredExceptionHandler);

 // Raise a fake exception for demo
 __try
 {
  printf("RaiseException: Raise an exception\n");
  RaiseException(1,0,0,NULL);
 } 
 __except(EXCEPTION_EXECUTE_HANDLER)
 {
  printf("This is a exception handler\n");
 }

 // Remove the vectored exception handler
 RemoveVectoredExceptionHandler(handler);
}

#pragma endregion


int _tmain(int argc, _TCHAR* argv[])
{
 ThrowAndHandleVectoredException();

 return 0;
}

posted on 2010-12-01 17:11  李sir  阅读(12590)  评论(0编辑  收藏  举报