SEH转化为C++异常<三>

 
//==============================================================================
//
// 项目名 :
// 文件名 :XDBExceptionReport.cpp
// 作  者 :
// 用  途 :异常报告输出实现类。
// 
//==============================================================================
// 版本记录	
//==============================================================================
//
// V0.1	 2005-5-19 9:49:44
// V0.9	 2005-6-20 8:54:52 RC1
//	
// 
//==============================================================================
#include "stdafx.h"
#include <malloc.h>
#include "KDSExceptionReport.h"
#include "KDSOsUtils.h"
#include "KingDataServer/KDSCommonPublic.h"
//==============================================================================
// 需要DBGHELP.LIB文件
//==============================================================================
#pragma comment( linker, "/defaultlib:dbghelp.lib")
#pragma comment( lib,"version.lib" )

using namespace KING_DATA_SERVER;

//==============================================================================
// 静态成员
//==============================================================================
WCHAR XDBExceptionReport::m_szReportFileName[MAX_PATH] = {  0 };
LPTOP_LEVEL_EXCEPTION_FILTER XDBExceptionReport::m_previousFilter = NULL;
HANDLE XDBExceptionReport::m_hReportFile = INVALID_HANDLE_VALUE;
HANDLE XDBExceptionReport::m_hProcess	 = NULL;
CRITICAL_SECTION XDBExceptionReport::m_csDbgHelp;

#define XDB_ASSERT	KDS_ASSERT

//==============================================================================
// 全局唯一实例
//==============================================================================
static XDBExceptionReport _kdsExceptionReport;

//==============================================================================
// UNICODE字节序标志(BOM:Byte Order Mark)
//==============================================================================
#define UNICODE_BOM	"\xFF\xFE"

//========================================================= 
// 嵌套层次深度
//========================================================= 
#define MAX_NESTING_LEVEL		2

//=============================================================
// 最多处理的子节点数目
//=============================================================
#define MAX_CHILDREN_NUM		16

//=============================================================
// 最多处理的数组元素个数
//=============================================================
#define MAX_ELEMENT_NUM			16

//=============================================================
// 异常输出文件的最大尺寸
//=============================================================
// 注: 只在每次处理异常之前检查尺寸,因此文件可能暂时出现尺寸
//		大于该值的现象,不过下次再发生异常时,文件尺寸将被收缩。
//=============================================================
#define MAX_REPORT_FILE_SIZE	((DWORD)16*1024*1024) // 16M

//========================================================= 
// 字符输出缓冲区大小
//========================================================= 
#define MAX_OUT_BUFFER	32767


//==============================================================================
// 
// 类成员实现
// 
//==============================================================================

// 枚举符号信息的回调参数结构
struct EnumerateSymbolsCallbackPara
{
	LPSTACKFRAME64	stackFrame;		// 调用栈信息
	HANDLE			reportFile;		// 输出文件句柄
};

/// <summary> 
/// 构造函数。
/// </summary> 
XDBExceptionReport::XDBExceptionReport( )
{
	// 生成默认的异常报告输出文件
	// 默认的文件路径与可执行文件路径相同,文件后缀为RPT
	::GetModuleFileName( 0, m_szReportFileName, MAX_PATH );

	// 得到进程ID
	DWORD ProcessId = GetCurrentProcessId();


	// 生成合适的后缀
	LPWSTR pszDot = wcsrchr( m_szReportFileName, WSTR('.') );
    if ( pszDot != NULL ) *pszDot = WSTR('\0');

	wchar_t wStr[ 32] = { 0 };
	swprintf( wStr,32, WSTR("_%u.RPT"),ProcessId );

	size_t MaxCount = MAX_PATH - wcslen( m_szReportFileName ) - 1;

	if( MaxCount > 0 )  wcsncat( m_szReportFileName,wStr,MaxCount );

	// 获得进程句柄
    m_hProcess = GetCurrentProcess();

	// 初始化临界区
	::InitializeCriticalSection( &m_csDbgHelp );

	// 安装未处理异常过滤函数
	m_previousFilter = ::SetUnhandledExceptionFilter( 
		XDBUnhandledExceptionFilter );
}

/// <summary> 
/// 析构函数。
/// </summary> 
XDBExceptionReport::~XDBExceptionReport( )
{
	// 恢复默认的未处理异常过滤函数
    SetUnhandledExceptionFilter( m_previousFilter );

	// 删除临界区
	::DeleteCriticalSection( &m_csDbgHelp );
}

/// <summary> 
///		设置异常报告输出文件的路径。
/// </summary> 
/// <param name="pszReportFileName">
///		文件路径。
/// </param>
void XDBExceptionReport::SetReportFileName( LPWSTR pszReportFileName )
{
	XDB_ASSERT( pszReportFileName != NULL );
	if( pszReportFileName != NULL )
		wcscpy( m_szReportFileName, pszReportFileName );
}

/// <summary> 
///		获得异常报告输出文件的路径。
/// </summary> 
/// <returns>
///     返回文件路径。
/// </returns>
WCHAR*  XDBExceptionReport::GetReportFileName( )
{
	return m_szReportFileName;
}


/// <summary> 
///		未处理异常入口函数。	
/// </summary> 
/// <param name="pExceptionInfo">
///		异常信息。
/// </param>
/// <returns>
///		返回值为EXCEPTION_CONTINUE_SEARCH或EXCEPTION_EXECUTE_HANDLER。
/// </returns>
LONG WINAPI XDBExceptionReport::XDBUnhandledExceptionFilter(
		PEXCEPTION_POINTERS pExceptionInfo )
{
	LONG	Result	= EXCEPTION_CONTINUE_SEARCH;
	DWORD	FileSize= 0;
	__try
	{
		// 进入临界区(DBGHELP库只能被单线程访问)
		::EnterCriticalSection( &m_csDbgHelp );
		
		// 打开文件
		m_hReportFile = ::CreateFile( m_szReportFileName,
									GENERIC_WRITE|GENERIC_READ,
									FILE_SHARE_READ,
									NULL,
									OPEN_ALWAYS,
									FILE_ATTRIBUTE_NORMAL,
									NULL );

		// 检查是否成功打开文件
		if ( m_hReportFile != INVALID_HANDLE_VALUE )
		{

			// 检查文件大小
			FileSize = GetFileSize( m_hReportFile,NULL );
		//==============================================================================
		#ifdef _UNICODE 
		//==============================================================================
			if( FileSize == 0 )
			{
				DWORD Bytes = 0;
				::WriteFile( m_hReportFile,UNICODE_BOM,2,&Bytes,NULL );	
			}
		//==============================================================================
		#endif // _UNICODE
		//==============================================================================
			// 检查是否需要收缩文件尺寸
			if( FileSize > MAX_REPORT_FILE_SIZE ) 
			{
				// 收缩文件
				if( !ShrinkReportFile( m_hReportFile, m_szReportFileName ) )
				{
					XDB_ASSERT( m_hReportFile == INVALID_HANDLE_VALUE );
					__leave; // 跳出__try块
				}
			}

			// 不覆盖原有内容,移动到文件的最后
			SetFilePointer( m_hReportFile, 0, 0, FILE_END );
			
			// 生成异常报告
			GenerateExceptionReport( m_hReportFile, pExceptionInfo,false );

			// 关闭文件句柄
			CloseHandle( m_hReportFile );
			m_hReportFile = INVALID_HANDLE_VALUE;
		}

		// 检查之前是否存在过滤函数
		if ( m_previousFilter )
			Result = m_previousFilter( pExceptionInfo );
	} // __try
	__finally
	{
		// 释放临界区
		::LeaveCriticalSection( &m_csDbgHelp );
	}

	// 返回
	return Result;
}



/// <summary> 
///		异常过滤函数。
/// </summary> 
/// <param name="pszReportFileName">
///		异常报告输出的文件。
/// </param>
/// <param name="pExceptionInfo">
///		异常信息。
/// </param>
/// <param name="bWriteVariables">
///		是否输出参数信息。
/// </param>
/// <returns>
///		返回值为EXCEPTION_EXECUTE_HANDLER。
/// </returns>
LONG WINAPI XDBExceptionReport::XDBHandledExceptionFilter(
			LPCWSTR				pszReportFileName,
			PEXCEPTION_POINTERS pExceptionInfo,
			bool				bWriteVariables /*=false*/)
{

// Release版不输出参数信息
#ifndef _DEBUG
	bWriteVariables = false;
#endif 


	// 检查参数
	if( pszReportFileName == NULL ) return EXCEPTION_EXECUTE_HANDLER;
	HANDLE	pReportFile = INVALID_HANDLE_VALUE;
	DWORD	FileSize	= 0;
	__try
	{
		// 进入临界区(DBGHELP库只能被单线程访问)
		::EnterCriticalSection( &m_csDbgHelp );

		// 打开文件
		pReportFile = ::CreateFile( 
			pszReportFileName,
			GENERIC_WRITE|GENERIC_READ,
			FILE_SHARE_READ,
			NULL,
			OPEN_ALWAYS,
			FILE_ATTRIBUTE_NORMAL,
			NULL );
		if( pReportFile != INVALID_HANDLE_VALUE )
		{

			// 检查文件大小
			FileSize = GetFileSize( pReportFile,NULL );
		//==============================================================================
		#ifdef _UNICODE 
		//==============================================================================
			if( FileSize == 0 )
			{
				DWORD Bytes = 0;
				::WriteFile( pReportFile,UNICODE_BOM,2,&Bytes,NULL );	
			}
		//==============================================================================
		#endif // _UNICODE
		//==============================================================================
			// 检查是否需要收缩文件尺寸
			if(  FileSize > MAX_REPORT_FILE_SIZE )
			{
				// 收缩文件
				if( !ShrinkReportFile( pReportFile, pszReportFileName ) )
				{
					XDB_ASSERT( pReportFile == INVALID_HANDLE_VALUE );
					__leave; // 跳出__try块
				}
			}

			// 不覆盖原有内容,移动到文件的最后
			SetFilePointer( pReportFile, 0, 0, FILE_END );

			// 生成异常报告
			GenerateExceptionReport( pReportFile, pExceptionInfo,bWriteVariables);

			// 关闭文件句柄
			CloseHandle( pReportFile );
		}  // pReportFile != INVALID_HANDLE_VALUE
	}// __try
	__finally
	{
		// 释放临界区
		::LeaveCriticalSection( &m_csDbgHelp );
	}
 
	// 返回异常已经处理标志
	return EXCEPTION_EXECUTE_HANDLER;
}


/// <summary> 
///		当文件尺寸过大时,收缩异常报告输出文件。
/// </summary> 
/// <param name="pReportFile">
///		异常报告输出文件句柄。
/// </param>
/// <param name="pszReportFileName">
///		异常报告输出文件路径。
/// </param>
/// <returns>
///		指明操作是否成功。
/// </returns>
BOOL XDBExceptionReport::ShrinkReportFile( 
		HANDLE&	pReportFile,
		LPCWSTR	pszReportFileName )
{
	XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
	XDB_ASSERT( pszReportFileName != NULL );
    
	// 关闭文件
	::CloseHandle( pReportFile );

	// 删除文件
	::DeleteFile( pszReportFileName );
    
	// 重新创建文件
	pReportFile = ::CreateFile( 
		pszReportFileName,
		GENERIC_WRITE|GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL );
 
	// 写入字节序标志
	if( pReportFile != INVALID_HANDLE_VALUE )
	{
		DWORD Bytes = 0;
		::WriteFile( pReportFile,UNICODE_BOM,2,&Bytes,NULL );
	}
	
	// 返回
	return ( pReportFile != INVALID_HANDLE_VALUE );
}

/// <summary> 
///		生成异常报告。
/// </summary> 
/// <param name="pReportFile">
///		异常报告输出的文件句柄。
/// </param>
/// <param name="pExceptionInfo">
///		异常信息。
/// </param>
/// <param name="bWriteVariables">
///		是否输出参数信息。
/// </param>
void XDBExceptionReport::GenerateExceptionReport( 
		HANDLE				pReportFile,
		PEXCEPTION_POINTERS pExceptionInfo,
		bool				bWriteVariables)
{
	// 检查参数
	if( pReportFile			== INVALID_HANDLE_VALUE ||
		pExceptionInfo		== NULL )
		return; 

// Release版不输出参数信息
#ifndef _DEBUG
	bWriteVariables = false;
#endif 


	WCHAR szBuffer[4096] = { 0 };
	//==============================================================================
	// 生成文件头,记录系统信息
	//==============================================================================
	PrintOut( pReportFile , WSTR("Exception report created by %s\r\n\r\n"), GetApplicationInfo( szBuffer ,4096) );
	PrintOut( pReportFile , WSTR("=====================================================\r\n") );
	PrintOut( pReportFile , WSTR("System details:\r\n") );
	PrintOut( pReportFile , WSTR("=====================================================\r\n") );
	PrintOut( pReportFile , WSTR("%s\r\n"), GetOperationSystemInfo( szBuffer,4096 )  );
	PrintOut( pReportFile , WSTR("%s\r\n"), GetProcessorInfo( szBuffer,4096 ) );
	PrintOut( pReportFile , WSTR("%s\r\n"), GetMemoryInfo( szBuffer,4096 ) );

	//==============================================================================
	// 输出异常信息
	//==============================================================================
	PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
	PrintOut( pReportFile , WSTR("\r\n=====================================================\r\n") );
	PrintOut( pReportFile , WSTR("Exception Details:\r\n" ) );
	PrintOut( pReportFile , WSTR("=====================================================\r\n") );
	PrintOut( pReportFile , WSTR("Exception code: %08X %s\r\n"),
							pExceptionRecord->ExceptionCode,
							GetExceptionString(pExceptionRecord->ExceptionCode) );

	//==============================================================================
	// 设置符号引擎属性
	//==============================================================================
	DWORD dwOptions = SymGetOptions() ;

	// 需要装载行号信息
	SymSetOptions(dwOptions | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);

	// 初始化DBGHELP库
	if ( !SymInitialize( m_hProcess, NULL , TRUE ) )
		return;
	
	//==============================================================================
	// 输出引起异常的地址信息
	//==============================================================================
	WCHAR szFaultingModule[MAX_PATH] = { 0 };
    DWORD_PTR section =0, offset = 0;

	// 获得地址
    if( GetLogicalAddress(
			pExceptionRecord->ExceptionAddress,
		    szFaultingModule,
			sizeof( szFaultingModule ),
			section, 
			offset ) )
	{
		// 输出地址信息
		PrintOut(	pReportFile , 
					WSTR("Fault address:  %p %IX:%IX %s\r\n"),
					pExceptionRecord->ExceptionAddress,
					section, offset, szFaultingModule );
	}
	else 
	{
		PrintOut(	pReportFile ,
					WSTR( "Failed to get module info,Fault address: %p\r\n" ),
					pExceptionRecord->ExceptionAddress );
		// 返回
		return;
	}

    
	//==============================================================================
	// 输出寄存器信息
	//==============================================================================
	PCONTEXT pCtx = pExceptionInfo->ContextRecord;
	XDB_ASSERT( pCtx != NULL );
	PrintOut( pReportFile , WSTR("\r\n=====================================================\r\n") );
    PrintOut( pReportFile ,WSTR("Registers:\r\n") );
	PrintOut( pReportFile , WSTR("=====================================================\r\n") );

#ifdef _M_IX86
    PrintOut( pReportFile , WSTR("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n"),
							pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
							pCtx->Esi, pCtx->Edi );
    PrintOut( pReportFile , WSTR("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip );
    PrintOut( pReportFile , WSTR("SS:ESP:%04X:%08X  EBP:%08X\r\n"),
							pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
    PrintOut( pReportFile , WSTR("DS:%04X  ES:%04X  FS:%04X  GS:%04X\r\n"),
			                pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
    PrintOut( pReportFile , WSTR("Flags:%08X\r\n"), pCtx->EFlags );
#endif // TODO: other platform

	//==============================================================================
	// 输出调用堆栈
	//==============================================================================
    CONTEXT trashableContext = *pCtx;
    WriteStackDetails( pReportFile, &trashableContext, false );

	//==============================================================================
	// 输出参数信息
	//==============================================================================
	if( bWriteVariables )
	{
		// 输出局部变量和函数参数
		PrintOut( pReportFile , WSTR("\r\n=====================================================\r\n") );
		PrintOut( pReportFile , WSTR("Local Variables And Parameters:\r\n") );
		PrintOut( pReportFile , WSTR("=====================================================\r\n") );
		trashableContext = *pCtx;
		WriteStackDetails( pReportFile,&trashableContext, true );

		// 输出全局变量
	#ifdef DBGHELP_PRINTOUT_GLOBAL
		{
			PrintOut( pReportFile , WSTR("\r\n=====================================================\r\n") );
			PrintOut( pReportFile , WSTR("Global Variables:\r\n") );
			PrintOut( pReportFile , WSTR("=====================================================\r\n") );
			EnumerateSymbolsCallbackPara stParam = { NULL, pReportFile };
			SymEnumSymbols(	m_hProcess,
							(DWORD64)GetModuleHandle(szFaultingModule),
							NULL, EnumerateSymbolsCallback, &stParam );
		}
	#endif // DBGHELP_PRINTOUT_GLOBAL 
	}
    
	// 释放资源
    SymCleanup( m_hProcess );

	// 结束输出
    PrintOut( pReportFile ,WSTR("\r\n") );
}


/// <summary> 
///		获得运行程序的版本信息。
/// </summary> 
/// <param name="szBuffer">
///		用于保存信息的缓冲区(必须足够大)。
/// </param>
/// <returns>
///		返回程序信息。
/// </returns>
WCHAR* XDBExceptionReport::GetApplicationInfo(  WCHAR* szBuffer,size_t szLength ) 
{
	XDB_ASSERT( szBuffer != NULL );

	// 获得全路径
	WCHAR szModuleName[MAX_PATH]= {  0 };
	WCHAR szAppName[MAX_PATH]	= {  0 };
	::GetModuleFileName( NULL,szModuleName,MAX_PATH );

	// 获得程序名称
	WCHAR* p = wcsrchr( szModuleName, WSTR('\\') );
	if( p == NULL )
		wcscpy( szAppName, szModuleName );
	else
		wcscpy( szAppName, p+1 );

	// 获得版本信息
	WCHAR szVersion[256] = {  0	};
	GetFileVersion( szModuleName,WSTR("FileVersion"),szVersion );

	// 获得时间信息
	SYSTEMTIME localTime = { 0 };
	GetLocalTime( &localTime );
	WCHAR szTime[128] ={  0 };
	swprintf( szTime , 128,L"%04d-%02d-%02d %02d:%02d:%02d.%03d", localTime.wYear , localTime.wMonth , localTime.wDay , localTime.wHour , localTime.wMinute, localTime.wSecond, localTime.wMilliseconds );

	// 构造返回信息
	swprintf( szBuffer,szLength,WSTR("%s %s [%s]"),szAppName,szVersion ,szTime );
	return szBuffer;
}



/// <summary> 
///		获得文件版本信息。
/// </summary> 
/// <param name="lpszFileName">
///		文件名。
/// </param>
/// <param name="lpszVersionName">
///		要获取的版本信息。
/// </param>
/// <param name="lpszVersion">
///		用于返回版本信息,必须具有足够的空间。
/// </param>
/// <returns>
///		是否成功。
/// </returns>
BOOL XDBExceptionReport::GetFileVersion( 
	LPCWSTR lpszFileName, 
	LPCWSTR lpszVersionName, 
	LPWSTR	lpszVersion )
{
	// 检查参数
	if( lpszFileName == NULL || lpszVersionName == NULL || lpszVersion == NULL )
		return FALSE;

	// 局部变量
	DWORD   dwVerInfoSize	= 0;	  // Size of version information block
	DWORD   dwVerHnd		= 0;	  // An 'ignored' parameter, always '0'
	BOOL	bRet			= FALSE;

	// 获取该文件的版本信息资源的大小
	dwVerInfoSize = ::GetFileVersionInfoSize( (LPWSTR)lpszFileName, &dwVerHnd );
	if(dwVerInfoSize == 0)
		return FALSE;

	//获取该文件的版本信息资源
	WCHAR *szVersionInfo = new WCHAR[dwVerInfoSize];
	XDB_ASSERT( szVersionInfo != NULL );
	ZeroMemory( szVersionInfo,dwVerInfoSize );
	bRet = ::GetFileVersionInfo( (LPWSTR)lpszFileName, dwVerHnd, dwVerInfoSize, szVersionInfo );
	if (!bRet)
	{
		delete []szVersionInfo;
		return FALSE;
	}

	// 获取该文件的版本信息资源中的语言代号
	DWORD	*lpLanguageCode = NULL;
	UINT	nLen = 0;
	bRet = ::VerQueryValue(
		szVersionInfo, 
		WSTR("\\VarFileInfo\\Translation"), 
		(LPVOID *)&lpLanguageCode, 
		&nLen);
	if (!bRet)
	{
		delete []szVersionInfo;
		return FALSE;
	}

	// 生成版本信息字符串
	WCHAR szVersionName[64] = { 0 };
	swprintf( szVersionName,64, WSTR("\\StringFileInfo\\%04hX%04hX\\%s"), LOWORD(*lpLanguageCode), HIWORD(*lpLanguageCode), lpszVersionName) ;

	// 获取该文件的版本信息资源中指定的某项版本信息
	LPWSTR szVersion = NULL;	// String pointer to 'version' text
	bRet = ::VerQueryValue( 
		szVersionInfo, 
		szVersionName, 
		(LPVOID *)&szVersion, 
		&nLen );
	if (!bRet)
	{
		delete []szVersionInfo;
		return FALSE;
	}

	// 拷贝版本信息
	wcscpy( lpszVersion,szVersion );
	if ( wcsstr( lpszVersionName, WSTR("Version")) != NULL )
	{
		// 如果指定的版本信息名称中包含"Version"的话,则将版本号中的","更换为"."
		for ( WCHAR *p = lpszVersion; *p != WSTR('\0'); p++ )
		{
			if( *p == WSTR(',') )
				*p = WSTR('.');
		}
	}

	// 释放内存
	delete []szVersionInfo;
	return TRUE;
}


/// <summary> 
///		获得异常代码的字符串描述。
/// </summary> 
/// <param name="dwCode">
///		异常代码。
/// </param>
/// <returns>
///		异常代码的字符串描述。
/// </returns>
LPWSTR XDBExceptionReport::GetExceptionString( DWORD dwCode )
{
    #define EXCEPTION( x ) case EXCEPTION_##x: return WSTR(#x);
    switch ( dwCode )
    {
        EXCEPTION( ACCESS_VIOLATION )
        EXCEPTION( DATATYPE_MISALIGNMENT )
        EXCEPTION( BREAKPOINT )
        EXCEPTION( SINGLE_STEP )
        EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
        EXCEPTION( FLT_DENORMAL_OPERAND )
        EXCEPTION( FLT_DIVIDE_BY_ZERO )
        EXCEPTION( FLT_INEXACT_RESULT )
        EXCEPTION( FLT_INVALID_OPERATION )
        EXCEPTION( FLT_OVERFLOW )
        EXCEPTION( FLT_STACK_CHECK )
        EXCEPTION( FLT_UNDERFLOW )
        EXCEPTION( INT_DIVIDE_BY_ZERO )
        EXCEPTION( INT_OVERFLOW )
        EXCEPTION( PRIV_INSTRUCTION )
        EXCEPTION( IN_PAGE_ERROR )
        EXCEPTION( ILLEGAL_INSTRUCTION )
        EXCEPTION( NONCONTINUABLE_EXCEPTION )
        EXCEPTION( STACK_OVERFLOW )
        EXCEPTION( INVALID_DISPOSITION )
        EXCEPTION( GUARD_PAGE )
        EXCEPTION( INVALID_HANDLE )
    }

    // If not one of the "known" exceptions, try to get the string
    // from NTDLL.DLL's message table.
    static WCHAR szBuffer[512] = { 0 };
	::FormatMessage( 
		FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
		GetModuleHandle( WSTR("NTDLL.DLL") ),
        dwCode, 0, szBuffer, sizeof( szBuffer ), 0 );
    return szBuffer;
}



/// <summary> 
///		获得基本类型的字符串描述。
/// </summary> 
/// <param name="dwBasicType">
///		基本类型。
/// </param>
/// <returns>
///		基本类型的字符串表示。
/// </returns>
WCHAR* XDBExceptionReport::GetBasicTypeString( 
	DWORD dwBasicType )
{
	#define BASE_TYPE_CASE(x) case bt##x : return WSTR(#x);break;
	switch( dwBasicType )
	{
		BASE_TYPE_CASE( NoType )
		BASE_TYPE_CASE( Void )
		BASE_TYPE_CASE( Char )
		BASE_TYPE_CASE( WChar )
		BASE_TYPE_CASE( Int )
		BASE_TYPE_CASE( UInt )
		BASE_TYPE_CASE( Float )
		BASE_TYPE_CASE( BCD )
		BASE_TYPE_CASE( Bool )
		BASE_TYPE_CASE( Long )
		BASE_TYPE_CASE( ULong )
		BASE_TYPE_CASE( Currency )
		BASE_TYPE_CASE( Date )
		BASE_TYPE_CASE( Variant )
		BASE_TYPE_CASE( Complex )
		BASE_TYPE_CASE( Bit )
		BASE_TYPE_CASE( BSTR )
		BASE_TYPE_CASE( Hresult )
	}
	return WSTR("(UnknownType)");
}

/// <summary> 
///		获得符号类型的字符串描述。
/// </summary> 
/// <param name="dwSymbolTag">
///		符号类型。
/// </param>
/// <returns>
///		符号类型的字符串描述。
/// </returns>
WCHAR* XDBExceptionReport::GetSymbolTagString(
	DWORD dwSymbolTag )
{
	#define SYM_TAG_CASE(x) case SymTag##x : return WSTR(#x);break;
	switch( dwSymbolTag )
	{
		SYM_TAG_CASE( Null )
		SYM_TAG_CASE( Exe )
		SYM_TAG_CASE( Compiland )
		SYM_TAG_CASE( CompilandDetails )
		SYM_TAG_CASE( CompilandEnv )
		SYM_TAG_CASE( Function )
		SYM_TAG_CASE( Block )
		SYM_TAG_CASE( Data )
		SYM_TAG_CASE( Annotation )
		SYM_TAG_CASE( Label )
		SYM_TAG_CASE( PublicSymbol )
		SYM_TAG_CASE( UDT )
		SYM_TAG_CASE( Enum )
		SYM_TAG_CASE( FunctionType )
		SYM_TAG_CASE( PointerType )
		SYM_TAG_CASE( ArrayType )
		SYM_TAG_CASE( BaseType )
		SYM_TAG_CASE( Typedef )
		SYM_TAG_CASE( BaseClass )
		SYM_TAG_CASE( Friend )
		SYM_TAG_CASE( FunctionArgType )
		SYM_TAG_CASE( FuncDebugStart )
		SYM_TAG_CASE( FuncDebugEnd )
		SYM_TAG_CASE( UsingNamespace )
		SYM_TAG_CASE( VTableShape )
		SYM_TAG_CASE( VTable )
		SYM_TAG_CASE( Custom )
		SYM_TAG_CASE( Thunk )
		SYM_TAG_CASE( CustomType )
		SYM_TAG_CASE( ManagedType )
		SYM_TAG_CASE( Dimension )
	}
	return WSTR("(UnknownTag)");
}


/// <summary> 
///		获得引起异常的地址信息。
/// </summary> 
/// <param name="addr">
///		地址信息。
/// </param>
/// <param name="szModule">
///		用于保存模块名的缓冲。
/// </param>
/// <param name="len">
///		缓冲区大小。
/// </param>
/// <param name="section">
///		地址所位于的节。
/// </param>
/// <param name="offset">
///		地址偏移。
/// </param>
/// <returns>
///		成功返回TRUE,否则返回FALSE。
/// </returns>
BOOL XDBExceptionReport::GetLogicalAddress(
		PVOID addr, PWSTR szModule, DWORD len, 
		DWORD_PTR& section, DWORD_PTR& offset )
{
//	XDB_ASSERT( addr != NULL );
	XDB_ASSERT( szModule != NULL );
	XDB_ASSERT( len > 0 );
	section = 0;
	offset  = 0;
	HMODULE hMod = NULL;
    hMod = (HMODULE)SymGetModuleBase64( m_hProcess, (DWORD64)addr );
	if( hMod == NULL )
	{
		// 尝试另外的方法
		MEMORY_BASIC_INFORMATION mbi = { 0 };
		if( 0 == ::VirtualQuery( addr, &mbi, sizeof(MEMORY_BASIC_INFORMATION) ) )
			return FALSE;
		
		// 模块地址就是AllocationBase
		hMod = (HMODULE) mbi.AllocationBase;
	}

	// 获得模块名称
	if ( !GetModuleFileName( hMod, szModule, len ) )
		return FALSE;

	// 获得PE Header
	PIMAGE_NT_HEADERS pNtHdr = ImageNtHeader( hMod );
	if( pNtHdr == NULL ) return FALSE;

	// 获得SECTION
	DWORD rva = ( DWORD )( (BYTE*)addr - (BYTE*)hMod ); 
	PIMAGE_SECTION_HEADER pFirst   = IMAGE_FIRST_SECTION( pNtHdr );
    PIMAGE_SECTION_HEADER pSection = ImageRvaToSection( pNtHdr,hMod,rva );
	if( pSection == NULL ) return FALSE;
	
	// 计算节号和偏移
	section = pSection - pFirst + 1;
	offset  = rva - pSection->VirtualAddress;
	return TRUE;
}



/// <summary> 
///		输出调用堆栈信息。
/// </summary> 
/// <param name="pReportFile">
///		异常报告输出的文件句柄。
/// </param>
/// <param name="pContext">
///		上下文环境句柄。
/// </param>
/// <param name="bWriteVariables">
///		是否输出参数信息。
/// </param>
void XDBExceptionReport::WriteStackDetails( 
	HANDLE	 pReportFile,
	PCONTEXT pContext, 
	bool	bWriteVariables )
{
    DWORD dwMachineType = 0;				// CPU类型
	STACKFRAME64 sf = { 0 } ;				// 调用堆栈帧
	WCHAR szModule[MAX_PATH] = WSTR("");		// 模块路径
	WCHAR szError[MAX_TRACE_BUFFER_LEN] = { 0 };// 错误信息
	DWORD_PTR section = 0, offset = 0;			// 逻辑地址
	DWORD64 symDisplacement = 0;			// 相对于符号起始位置的偏移
	BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ] = { 0 };// 用于保存符号名的缓冲区
	IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };	// 源文件及行号信息
	DWORD dwLineDisplacement = 0;							// 相对偏移

	// 输出分隔头
	PrintOut( pReportFile , WSTR("\r\n=====================================================\r\n") );
	PrintOut( pReportFile , WSTR("Call stack:\r\n") );
	PrintOut( pReportFile , WSTR("=====================================================\r\n") );
	PrintOut( pReportFile , WSTR("Address   Frame     Function            SourceFile\r\n") );

	//=============================================================
	//
	// 对于Intel CPU,需要在首次调用StackWalk64函数之前初始化
	// STACKFRAME64成员的AddrPC和AddrFrame成员
	//
	//=============================================================
#if defined(_M_IX86)	// 32bit
    sf.AddrPC.Offset		= pContext->Eip;
    sf.AddrPC.Mode			= AddrModeFlat;
    sf.AddrStack.Offset		= pContext->Esp;
    sf.AddrStack.Mode		= AddrModeFlat;
    sf.AddrFrame.Offset		= pContext->Ebp;
    sf.AddrFrame.Mode		= AddrModeFlat;
    dwMachineType			= IMAGE_FILE_MACHINE_I386;
#elif defined( _M_IA64 ) // Itanium Processor Family
	sf.AddrPC.Offset		= pContext->StIIP;
	sf.AddrPC.Mode			= AddrModeFlat;
	sf.AddrStack.Offset		= pContext->IntSp;
	sf.AddrStack.Mode		= AddrModeFlat;
	sf.AddrFrame.Offset		= pContext->IntSp;
	sf.AddrFrame.Mode		= AddrModeFlat;
	sf.AddrBStore.Offset	= pContext->RsBSP;
	sf.AddrBStore.Mode		= AddrModeFlat;
	dwMachineType			= IMAGE_FILE_MACHINE_IA64;
#elif defined( _M_X64 )	// X64 
	sf.AddrPC.Offset		= pContext->Rip;
	sf.AddrPC.Mode			= AddrModeFlat;
	sf.AddrStack.Offset		= pContext->Rsp;
	sf.AddrStack.Mode		= AddrModeFlat;
	sf.AddrFrame.Offset		= pContext->Rsp;
	sf.AddrFrame.Mode		= AddrModeFlat;
	dwMachineType			= IMAGE_FILE_MACHINE_AMD64;
#else 
#error "OS not supported!"
#endif 


	// 遍历调用堆栈
    while ( true )
    {
        // 获取下一个调用帧
        if ( !StackWalk64(dwMachineType,
                          m_hProcess,
                          GetCurrentThread(),
                          &sf,
                          pContext,
                          NULL,
                          SymFunctionTableAccess64,
                          SymGetModuleBase64,
                          NULL ) )
            break;// 结束循环

		// 检查该帧是否正常( EBP != 0 )
        if ( 0 == sf.AddrFrame.Offset ) 
            break;// 结束循环

		// 输出EIP及EBP
        PrintOut( pReportFile ,   WSTR("%I64X  %I64X  "), 
				  sf.AddrPC.Offset, 
				  sf.AddrFrame.Offset );

		// 获取该调用帧对应的函数名
		PSYMBOL_INFO pSymbol	= (PSYMBOL_INFO)symbolBuffer;
        pSymbol->SizeOfStruct	= sizeof(SYMBOL_INFO); // !! sizeof(symbolBuffer);
        pSymbol->MaxNameLen		= 1024;
        DWORD64 symDisplacement = 0;    
        if ( SymFromAddr(m_hProcess,sf.AddrPC.Offset,&symDisplacement,pSymbol))
        {
			// 如果找到了符号,则输出其名称及偏移
#ifdef DBGHELP_TRANSLATE_TCHAR
			PrintOut( pReportFile ,   WSTR("%ls+%I64X"), pSymbol->Name, symDisplacement );
#else
            PrintOut( pReportFile ,   WSTR("%hs+%I64X"), pSymbol->Name, symDisplacement );
#endif
        }
        else    // 如果无法找到符号,则返回其对应的逻辑(虚拟)地址
        {
			// 当没有相应的PDB文件时,就会走到这儿
            GetLogicalAddress(  (PVOID)sf.AddrPC.Offset,
                                szModule, sizeof(szModule), section, offset );
            PrintOut( pReportFile ,   WSTR("%IX:%IX %s\r\n"), section, offset, szModule );

			// 继续处理下一个堆栈帧
			continue;
        }

        // 获取该调用帧对应的源文件及行号信息
        if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset,
                                   &dwLineDisplacement, &lineInfo ) )
        {
#ifdef DBGHELP_TRANSLATE_TCHAR
			PrintOut( pReportFile ,  WSTR("  %ls line %u"), lineInfo.FileName,lineInfo.LineNumber); 
#else
		    PrintOut( pReportFile ,  WSTR("  %hs line %u"), lineInfo.FileName,lineInfo.LineNumber); 
#endif 
        }
		else // 如果不存在相应的PDB文件,则输出模块信息
		{
			// 打印模块信息
			GetLogicalAddress(  (PVOID)sf.AddrPC.Offset,
								szModule, sizeof(szModule), section, offset );
			PrintOut( pReportFile ,WSTR( "  %s %IX:%IX \r\n"), szModule, section, offset );

			// 继续处理下一个调用帧
			continue;
		}
        PrintOut( pReportFile ,   WSTR("\r\n") );

        // 检查是否需要输出局部变量及函数参数信息
        if ( bWriteVariables )
        {
			// 设置局部变量对应的上下文
			IMAGEHLP_STACK_FRAME imagehlpStackFrame = { 0 };
            imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
            if( SymSetContext( m_hProcess, &imagehlpStackFrame, NULL ) )
			{
				// 枚举局部变量及函数参数
				EnumerateSymbolsCallbackPara stParam = { &sf, pReportFile };
				SymEnumSymbols( m_hProcess, 0, NULL, EnumerateSymbolsCallback, &stParam ) ;
				PrintOut( pReportFile ,   WSTR("\r\n") );
			}
			else // 如果没有对应的PDB文件,则无法获得局部变量及函数参数信息
			{
				// 输出错误信息
				::FormatMessage( 
					FORMAT_MESSAGE_FROM_SYSTEM,
					NULL,
					GetLastError(),
					0,szError,MAX_TRACE_BUFFER_LEN-1,NULL );
				PrintOut( pReportFile ,   WSTR("SymSetContext Failed: %X : %s\r\n"),GetLastError(),szError );
			}
        }// bWriteVariables

    }// while loop

}

/// <summary> 
///		枚举符号信息的回调函数。
/// </summary> 
/// <param name="pSymInfo">
///		符号信息。
/// </param>
/// <param name="SymbolSize">
///		符号尺寸。
/// </param>
/// <param name="UserContext">
///		用户参数。
/// </param>
/// <returns>
///		返回TRUE时,继续枚举;否则,终止枚举过程。
/// </returns>
BOOL CALLBACK XDBExceptionReport::EnumerateSymbolsCallback(
    PSYMBOL_INFO  pSymInfo,
    ULONG         SymbolSize,
    PVOID         UserContext )
{
	XDB_ASSERT( pSymInfo != NULL );
	BOOL ContinueEnum  = TRUE;
	EnumerateSymbolsCallbackPara* pParam = ( EnumerateSymbolsCallbackPara * )UserContext;

	// 过滤掉不准备处理的类型
	if( ( pSymInfo->Tag != SymTagUDT		) &&  // 11:自定义类型
		( pSymInfo->Tag != SymTagData		) &&  // 07:数据
		( pSymInfo->Tag != SymTagEnum		) &&  // 12:枚举
		( pSymInfo->Tag != SymTagPointerType) &&  // 14:指针
		( pSymInfo->Tag != SymTagArrayType	) &&  // 15:数组
		( pSymInfo->Tag != SymTagBaseType	) &&  // 16:基本类型
		( pSymInfo->Tag != SymTagBaseClass	) )	  // 18:自定义类型的基类
		return TRUE;
	
	// 查询符号信息
	__try
    {
		// (调试)输出符号信息
		// DumpSymbolInfo( pParam->reportFile , pSymInfo );

		// 尝试获得变量的值
		FormatSymbolValue( pSymInfo, pParam->stackFrame, pParam->reportFile );
		PrintOut( pParam->reportFile, WSTR("\r\n" ) );
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
		PrintOut( pParam->reportFile ,WSTR("\tError on symbol:\'%hs\'.\r\n"), pSymInfo->Name );
		ContinueEnum = FALSE;
    }
    return ContinueEnum;
}

/// <summary> 
///		格式化符号值。
/// </summary> 
/// <param name="pSym">
///		符号信息。
/// </param>
/// <param name="sf">
///		堆栈帧。
/// </param>
/// <param name="pReportFile">
///		异常报告输出的文件句柄。
/// </param>
/// <returns>
///		格式化结果字符串。
/// </returns>
bool XDBExceptionReport::FormatSymbolValue(
            PSYMBOL_INFO	pSym,
            LPSTACKFRAME64	sf,
			HANDLE			pReportFile )
{
	// 注意:sf可以为NULL
	XDB_ASSERT( pSym != NULL );
	XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );

 	// 确定属性
	if( pSym->Flags & SYMFLAG_PARAMETER )		// 参数
		PrintOut( pReportFile, WSTR("Parameter ") );
    else if ( pSym->Flags & SYMFLAG_LOCAL )		// 局部变量
        PrintOut( pReportFile, WSTR("Local " ) );

    // 显示变量名称
#ifdef DBGHELP_TRANSLATE_TCHAR
    PrintOut( pReportFile, WSTR("\'%ls\'"), pSym->Name );
#else
	PrintOut( pReportFile, WSTR("\'%hs\'"), pSym->Name );
#endif 
	
	// 如果是寄存器变量,则返回(无法得到当时的寄存器值)
	if( pSym->Flags & SYMFLAG_REGISTER )
	{
		PrintOut(  pReportFile, WSTR(" (register variable) ") );
		return false;
	}

	// 确定变量在内存中的地址
    DWORD_PTR pVariable = 0; 
    if ( pSym->Flags & SYMFLAG_REGREL ) // 相对(栈基址寄存器)的偏移(局部变量或参数)
    {
		if( sf == NULL ) // 无效参数
		{
			PrintOut(  pReportFile, WSTR(" (null stack frame) ") );
			return false;  
		}
	    pVariable =  (DWORD_PTR)sf->AddrFrame.Offset;  // EBP
        pVariable += (DWORD_PTR)pSym->Address;
    }
    else // 绝对地址(全局变量才会使用绝对地址)
        pVariable = (DWORD_PTR)pSym->Address;

	// 输出变量信息
	return DumpTypeIndex(pReportFile,pSym->ModBase, pSym->TypeIndex,0, pVariable );
}


/// <summary> 
///		 输出类型的属性信息。
/// </summary> 
/// <param name="pReportFile">
///		 异常报告输出的文件句柄。
/// </param>
/// <param name="modBase">
///		 模块基址。
/// </param>
/// <param name="dwTypeIndex">
///		  类型索引。
/// </param>
/// <param name="dwOffset">
///		  变量地址。
/// </param>
void XDBExceptionReport::DumpTypeInfo(
	HANDLE		pReportFile,
	DWORD64		modBase,
	DWORD		dwTypeIndex ,
	DWORD_PTR	dwOffset )
{
	XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
	DWORD	dwValue		= 0;
	DWORD64 dwValue64	= 0;
	VARIANT	varValue	= {  0	};
	WCHAR*	wszValue	= NULL;

// DWORD类型的数据
#define PRINT_OUT_DWORD( x )	\
	if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,x,&dwValue ) )	\
		PrintOut( pReportFile,WSTR("\t%s = %d\n"),WSTR(#x),dwValue );	

// DWORD64类型的数据
#define PRINT_OUT_DWORD64( x )	\
	if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,x,&dwValue64 ) )	 \
		PrintOut( pReportFile,WSTR("\t%s = %I64X\n"),WSTR(#x),dwValue64 );	

// 字符串类型的数据
#define PRINT_OUT_WSTR( x )		\
	if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,x,&wszValue ) )	 \
	{	\
		PrintOut( pReportFile,WSTR("\t%s = %ls\n"),WSTR(#x),wszValue );		\
		LocalFree( wszValue ); \
		wszValue = NULL;	\
	}

	// 输出头部信息
	PrintOut( pReportFile, WSTR("\r\n\t=========TypeInfo BEGIN==========\r\n" ));
	PrintOut( pReportFile, WSTR("\tTypeIndex = %d\n"), dwTypeIndex );
	PrintOut( pReportFile, WSTR("\tOffset    = %IX\n"), dwOffset  );

	// 输出详细信息
	// 类型(或成员)名称
	PRINT_OUT_WSTR( TI_GET_SYMNAME );

	// 符号类型
	if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_SYMTAG,&dwValue ) )
		PrintOut( pReportFile, WSTR("\tTI_GET_SYMTAG\t = %ls\n"), GetSymbolTagString( dwValue )  );
	
	// 符号基本数据类型
	if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_BASETYPE,&dwValue ) )
		PrintOut( pReportFile, WSTR("\tTI_GET_BASETYPE\t = %ls\n"), GetBasicTypeString(dwValue) );

	// 符号的值(如果存在的话)
	::VariantInit( &varValue );
	if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_VALUE,&varValue) )
	{	
		PrintOut( pReportFile, WSTR("\tTI_GET_VALUE\t"));
		FormatVariantValue( pReportFile,&varValue );
		PrintOut( pReportFile, WSTR("\r\n"));
		::VariantClear( &varValue );
	}
    
	// 其他信息
	PRINT_OUT_DWORD64( TI_GET_LENGTH )
	PRINT_OUT_DWORD64( TI_GET_ADDRESS)
	PRINT_OUT_DWORD( TI_GET_TYPE   )
	PRINT_OUT_DWORD( TI_GET_TYPEID ) 
	PRINT_OUT_DWORD( TI_GET_SYMINDEX )
	PRINT_OUT_DWORD( TI_GET_ARRAYINDEXTYPEID )
	PRINT_OUT_DWORD( TI_GET_DATAKIND )
	PRINT_OUT_DWORD( TI_GET_ADDRESSOFFSET )
	PRINT_OUT_DWORD( TI_GET_OFFSET )
	PRINT_OUT_DWORD( TI_GET_COUNT  )
	PRINT_OUT_DWORD( TI_GET_CHILDRENCOUNT )
	PRINT_OUT_DWORD( TI_GET_BITPOSITION )
	PRINT_OUT_DWORD( TI_GET_VIRTUALBASECLASS )
	PRINT_OUT_DWORD( TI_GET_VIRTUALBASEPOINTEROFFSET )
	PRINT_OUT_DWORD( TI_GET_CLASSPARENTID )
	PRINT_OUT_DWORD( TI_GET_NESTED )
	PRINT_OUT_DWORD( TI_GET_LEXICALPARENT )
	PRINT_OUT_DWORD( TI_GET_THISADJUST )
	PRINT_OUT_DWORD( TI_GET_UDTKIND )
	PRINT_OUT_DWORD( TI_IS_EQUIV_TO )
	PRINT_OUT_DWORD( TI_GET_CALLING_CONVENTION )
	
	// 未输出的信息
   // TI_FINDCHILDREN 
   // TI_GET_VIRTUALTABLESHAPEID 
   // TI_GTIEX_REQS_VALID 
   // TI_IS_CLOSE_EQUIV_TO

   // 打印结束空行
   PrintOut( pReportFile, WSTR("\t=========TypeInfo END============\r\n" ));
}

/// <summary> 
///		输出符号信息。	
/// </summary> 
/// <param name="pReportFile">
///		异常报告输出的文件句柄。
/// </param>
/// <param name="pSymInfo">
///		符号信息结构。
/// </param>
void XDBExceptionReport::DumpSymbolInfo(
	HANDLE			pReportFile,
	PSYMBOL_INFO	pSymInfo )
{
	XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
	XDB_ASSERT( pSymInfo != NULL );
	PrintOut( pReportFile, WSTR("\t=========SYMBOL BEGIN==========\n") );
	PrintOut( pReportFile, WSTR("\tTypeIndex   = %d\n"),pSymInfo->TypeIndex );
//	PrintOut( pReportFile, WSTR("\tIndex       = %d\n"),pSymInfo->info );
	PrintOut( pReportFile, WSTR("\tReserved[0] = %d\n"),pSymInfo->Reserved[0] );
	PrintOut( pReportFile, WSTR("\tReserved[1] = %d\n"),pSymInfo->Reserved[1] );
	PrintOut( pReportFile, WSTR("\tSize        = %d\n"),pSymInfo->Size );
	PrintOut( pReportFile, WSTR("\tFlags       = %08X\n"),pSymInfo->Flags );
	PrintOut( pReportFile, WSTR("\tValue       = %d\n"),pSymInfo->Value );
	PrintOut( pReportFile, WSTR("\tAddress     = %I64X\n"),pSymInfo->Address );	
	PrintOut( pReportFile, WSTR("\tRegister    = %d\n"),pSymInfo->Register);
	PrintOut( pReportFile, WSTR("\tScope       = %d\n"),pSymInfo->Scope );
	PrintOut( pReportFile, WSTR("\tTag         = %d\n"),pSymInfo->Tag );
#ifdef DBGHELP_TRANSLATE_TCHAR
	PrintOut( pReportFile, WSTR("\tName        = %ls\n"),pSymInfo->Name);
#else
	PrintOut( pReportFile, WSTR("\tName        = %hs\n"),pSymInfo->Name);
#endif 
	PrintOut( pReportFile,WSTR("\t=========SYMBOL END============\n") );
}

/// <summary> 
///		输出类型信息。
/// </summary> 
/// <param name="pReportFile">
///		异常报告输出的文件句柄。
/// </param>
/// <param name="modBase">
///		模块基址。
/// </param>
/// <param name="dwTypeIndex">
///		类型索引。
/// </param>
/// <param name="nestingLevel">
///		嵌套层次。
/// </param>
/// <param name="offset">
///		偏移。
/// </param>
/// <returns>
///		成功或失败标志。
/// </returns>
bool XDBExceptionReport::DumpTypeIndex(
		HANDLE pReportFile,
        DWORD64 modBase,
        DWORD dwTypeIndex,
        unsigned nestingLevel,
        DWORD_PTR offset )
{
	XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );

	// 检查嵌套层次
	if( nestingLevel > MAX_NESTING_LEVEL )
		return false;

	// (调试)输出类型信息
	// DumpTypeInfo( pReportFile,modBase,dwTypeIndex,offset );

	// 首先获得符号的类型
	DWORD dwSymbolTag = 0;
	if( !SymGetTypeInfo( m_hProcess, modBase,dwTypeIndex,TI_GET_SYMTAG,&dwSymbolTag ) )
		return false; // 无法获得符号的类型
	
	// 获得长度
	DWORD64 dwLength = 0; // 下列函数有可能失败
	SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_LENGTH,&dwLength );

	// 过滤掉不准备处理的类型
	if( ( dwSymbolTag != SymTagUDT			) &&  // 11:自定义类型
		( dwSymbolTag != SymTagData			) &&  // 07:数据
		( dwSymbolTag != SymTagEnum			) &&  // 12:枚举
		( dwSymbolTag != SymTagPointerType	) &&  // 14:指针
		( dwSymbolTag != SymTagArrayType	) &&  // 15:数组
		( dwSymbolTag != SymTagBaseType		) &&  // 16:基本类型
		( dwSymbolTag != SymTagBaseClass	) )   // 18:自定义类型的基类
        return false;

	// 获得符号类型的名称(类型名称或结构成员,对于基本类型会失败)
	WCHAR * pwszTypeName = NULL;
	if ( SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME,&pwszTypeName ) )
	{
		XDB_ASSERT( pwszTypeName != NULL );
		PrintOut( pReportFile, WSTR(" %ls"), pwszTypeName );
		LocalFree( pwszTypeName );
	}

	// 检查地址是否合法
	if( ::IsBadReadPtr( (LPCVOID) offset, sizeof(DWORD_PTR)) ) 
	{
		PrintOut( pReportFile, WSTR("(E_ADDR:%IX)\n"),offset );
		return false;
	}

	// 根据不同的类型进行处理
	VARIANT varValue		= { 0 };
	DWORD   dwDataKind		= 0;
	DWORD	dwMemberOffset	= 0;
	DWORD	dwChildrenCount = 0;
	DWORD	dwTypeId		= 0;
	DWORD64 dwMemberLength	= 0;
	DWORD	dwBasicType		= btNoType;
	DWORD	dwMemberTag		= 0;
	switch( dwSymbolTag )
	{
	case SymTagUDT: // 自定义类型
		{		
			// 打印空行
			PrintOut( pReportFile, WSTR("\r\n") );

			// 检查嵌套级别
			if( nestingLevel+1 > MAX_NESTING_LEVEL )
				break;

			// 确定子节点的个数
			if( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT,&dwChildrenCount ) )
				break;
			
			// 如果没有子节点,则返回
			if ( 0 == dwChildrenCount )break;
            
			// 以继承方式扩展TI_FINDCHILDREN_PARAMS结构
			struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS
			{
				ULONG   MoreChildIds[MAX_CHILDREN_NUM-1];
				FINDCHILDREN(){ Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]); }
			} children;
			
			// 调整处理的子节点数目
			if( dwChildrenCount > MAX_CHILDREN_NUM )
				dwChildrenCount = MAX_CHILDREN_NUM;
			children.Count = dwChildrenCount;
			children.Start = 0;

			// 获得每个子节点的类型索引
			if ( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,&children ) )
				break;

			// 遍历每个子节点
			for ( unsigned i = 0; i < dwChildrenCount; i++ )
			{
				// 获得成员的偏移
				if( !SymGetTypeInfo( m_hProcess, modBase,children.ChildId[i] ,
									 TI_GET_OFFSET, &dwMemberOffset ) )
				{
					// 对于函数成员,可能没有偏移
					continue;
				}

				// 获得SymTag
				if( !SymGetTypeInfo( m_hProcess,modBase,children.ChildId[i],TI_GET_SYMTAG,&dwMemberTag ) )
				{
					// 无法获得符号类型,继续处理下一个
					continue;
				}

				// 过滤掉不准备处理的类型
				if( ( dwMemberTag != SymTagUDT			) &&  // 11:自定义类型
					( dwMemberTag != SymTagData			) &&  // 07:数据
					( dwMemberTag != SymTagEnum			) &&  // 12:枚举
					( dwMemberTag != SymTagPointerType	) &&  // 14:指针
					( dwMemberTag != SymTagArrayType	) &&  // 15:数组
					( dwMemberTag != SymTagBaseType		) &&  // 16:基本类型
					( dwMemberTag != SymTagBaseClass	) )	  // 18:自定义类型的基类
				{
					// 不是我们想处理的类型,继续下一个
					continue;
				}
		         
				// 根据嵌套级别缩进
				PrintOut( pReportFile,WSTR("%*s"),(nestingLevel+1)*4,WSTR(" ") );

				// 递归处理每个成员,嵌套层次递增
				DumpTypeIndex(	pReportFile,modBase,
								children.ChildId[i],nestingLevel+1,
								offset+dwMemberOffset ) ;
				
                // 输出空行
				PrintOut( pReportFile, WSTR("\r\n" ) );
			} // FOR LOOP

		} // SymTagUDT BLOCK
		break;
	case SymTagEnum: // 枚举类型的值
	case SymTagData: //  数据
	case SymTagBaseClass: // 基类
		if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_TYPEID,&dwTypeId ) )
			DumpTypeIndex( pReportFile,modBase,dwTypeId,nestingLevel,offset );
		break;
	case SymTagBaseType:  // 基本类型
		if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_BASETYPE,&dwBasicType ) )	
			FormatOutputValue( pReportFile,dwBasicType,dwLength,(LPVOID)offset);
		break;
	case SymTagPointerType:	// 指针类型

		// 打印地址及指针类型标识
		PrintOut( pReportFile,WSTR("\t(*[%IX])\t"),offset );

    	// 获得数据的类型
		if( !SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_TYPEID,&dwTypeId ) )
			break;

        // 获得内部的类型(特别处理字符串的问题)
		if( SymGetTypeInfo( m_hProcess,modBase,dwTypeId,TI_GET_BASETYPE,&dwBasicType ) )
		{
			if( dwBasicType == btChar || dwBasicType == btWChar )
			{
				FormatPointerValue( pReportFile,dwBasicType,dwLength,(LPVOID)offset );	
				break;   // 结束处理
			}
		}

		// 获得内部的类型
		if( SymGetTypeInfo( m_hProcess,modBase,dwTypeId,TI_GET_SYMTAG,&dwMemberTag ) )
		{
    		// 过滤掉不准备处理的类型
			if( ( dwMemberTag != SymTagUDT			) &&  // 11:自定义类型
				( dwMemberTag != SymTagData			) &&  // 07:数据
				( dwMemberTag != SymTagEnum			) &&  // 12:枚举
				( dwMemberTag != SymTagPointerType	) &&  // 14:指针
				( dwMemberTag != SymTagArrayType	) &&  // 15:数组
				( dwMemberTag != SymTagBaseType		) &&  // 16:基本类型
				( dwMemberTag != SymTagBaseClass	) )	  // 18:自定义类型的基类
				break; // 结束处理
		}

		// 递归调用
		DumpTypeIndex( pReportFile,modBase,dwTypeId,nestingLevel,*((LPDWORD)offset) );
	    break;
	case SymTagArrayType: // 数组类型
		{
			// 打印空行
			PrintOut( pReportFile, WSTR("\r\n") );

			// 检查嵌套级别
			if( nestingLevel+1 > MAX_NESTING_LEVEL )
				break;

			// 确定子节点的个数,注意是TI_GET_COUNT,而不是TI_GET_CHILDRENCOUNT
			if( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_COUNT ,&dwChildrenCount ) )
				break;

			// 如果没有子节点,则返回
			if ( 0 == dwChildrenCount )	break;
            if( dwChildrenCount > MAX_ELEMENT_NUM )
				dwChildrenCount = MAX_ELEMENT_NUM;

            // 获得元素的类型
			if( !SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_TYPEID,&dwTypeId ) )
				break;

			// 获得内部的类型
			if( SymGetTypeInfo( m_hProcess,modBase,dwTypeId,TI_GET_SYMTAG,&dwMemberTag ) )
			{
				// 过滤掉不准备处理的类型
				if( ( dwMemberTag != SymTagUDT			) &&  // 11:自定义类型
					( dwMemberTag != SymTagData			) &&  // 07:数据
					( dwMemberTag != SymTagEnum			) &&  // 12:枚举
					( dwMemberTag != SymTagPointerType	) &&  // 14:指针
					( dwMemberTag != SymTagArrayType	) &&  // 15:数组
					( dwMemberTag != SymTagBaseType		) &&  // 16:基本类型
					( dwMemberTag != SymTagBaseClass	) )	  // 18:自定义类型的基类
					break; // 结束处理
			}

        	// 获得元素的长度
			if( !SymGetTypeInfo( m_hProcess,modBase,dwTypeId,TI_GET_LENGTH,&dwMemberLength ) )
				break;
         
          	// 遍历每个子节点
			dwMemberOffset = 0;
			for ( DWORD i = 0; i < dwChildrenCount; i++ )
			{
				// 根据嵌套级别缩进
				PrintOut( pReportFile,WSTR("%*s"),(nestingLevel+1)*4,WSTR(" ") );
			
				// 打印索引
				PrintOut( pReportFile,WSTR("[%d]") , i );

				// 输出子节点信息
				DumpTypeIndex(  pReportFile,modBase,
								dwTypeId,nestingLevel+1,
								offset+dwMemberOffset );

				// 输出空行
				PrintOut( pReportFile, WSTR("\r\n" ) );

				// 更新偏移
				dwMemberOffset += (DWORD)dwMemberLength ;
			} // FOR LOOP
		}// BLOCK
		break;
	default:
		return false;
		break;
	}
	
	// 返回
	return true;
}

/// <summary> 
///		输出可变类型的数据。
/// </summary> 
/// <param name="pReportFile">
///		异常报告输出的文件句柄。
/// </param>
/// <param name="pVal">
///		待输出数值。
/// </param>
void XDBExceptionReport::FormatVariantValue(
		HANDLE			pReportFile,
		const VARIANT*	pVal )
{
	XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
	XDB_ASSERT( pVal != NULL );
	switch( V_VT( pVal ) )
	{
	case VT_I8:
		PrintOut( pReportFile, WSTR(" = %ld"), V_I8(pVal) );
		break;
	case VT_UI8:
		PrintOut( pReportFile, WSTR(" = %ld"), V_UI8(pVal) );
		break;
	case VT_I4:
		PrintOut( pReportFile, WSTR(" = %d"), V_I4(pVal) );
		break;
	case VT_UI4:
		PrintOut( pReportFile, WSTR(" = %d"), V_UI4(pVal) );
		break;
	case VT_I2:
		PrintOut( pReportFile, WSTR(" = %d"), V_I2(pVal) );
		break;
	case VT_UI2:
		PrintOut( pReportFile, WSTR(" = %d"), V_UI2(pVal) );
		break;
	case VT_I1:
		PrintOut( pReportFile, WSTR(" = %d"), V_I1(pVal) );
		break;
	case VT_UI1:
		PrintOut( pReportFile, WSTR(" = %d"), V_UI1(pVal) );
		break;
	case VT_INT:
		PrintOut( pReportFile, WSTR(" = %d"), V_INT(pVal) );
		break;
	case VT_UINT:
		PrintOut( pReportFile, WSTR(" = %d"), V_UINT(pVal) );
		break;
	case VT_R4:
		PrintOut( pReportFile, WSTR(" = %f"), V_R4(pVal) );
		break;
	case VT_R8:
		PrintOut( pReportFile, WSTR(" = %lf"), V_R8(pVal) );
		break;
	case VT_BSTR:
		PrintOut( pReportFile, WSTR(" = \"%ls\""),V_BSTR( pVal ) );
		break;
	case VT_BOOL:
		if( V_BOOL( pVal ) == VARIANT_FALSE )
			PrintOut( pReportFile, WSTR(" = VARIANT_FALSE") );
		else
			PrintOut( pReportFile, WSTR(" = VARIANT_TRUE") );
		break;	
	default:
		PrintOut( pReportFile, WSTR(" (TYPE NOT SUPPROTED)") );
		break;
	}
}

/// <summary> 
///		格式化指针参数值。
/// </summary> 
/// <param name="pReportFile">
///		异常报告输出的文件句柄。
/// </param>
/// <param name="basicType">
///		参数类型。
/// </param>
/// <param name="length">
///		值长度。
/// </param>
/// <param name="pAddress">
///		值地址。
/// </param>
void XDBExceptionReport::FormatPointerValue( 
	HANDLE		pReportFile,
	DWORD		basicType, 
	DWORD64		length, 
	PVOID		pAddress )
{   
	XDB_ASSERT( pAddress != NULL );
	// 注意:length长度没有使用,无法区分是单个元素的指针还是数组
	if( basicType == btChar )
	{
		LPSTR pszText =  *( LPSTR*)pAddress;
		// 检查指针是否有效
		// 最多输出64个字符
		if( IsBadStringPtrA( pszText,64 ) )
			PrintOut( pReportFile, WSTR(" = (E_ADDR:%p)"),pAddress );
		else
		{
			if( pszText[0] == '\0' )
				PrintOut( pReportFile, WSTR(" = \"\"") );
			else
				PrintOut( pReportFile, WSTR("	= \"%.63hs\""),pszText );
		}
	}
	else if( basicType == btWChar )
	{
		LPWSTR pwszText =  *( LPWSTR*)pAddress;
		// 检查指针是否有效
		// 最多输出64个字符
		if( IsBadStringPtrW( pwszText,64 ) )
			PrintOut( pReportFile, WSTR(" = (E_ADDR:%p)"),pAddress) ;
		else
		{
			if( pwszText[0] == L'\0' )
				PrintOut( pReportFile, WSTR(" = \"\"") );
			else
				PrintOut( pReportFile, WSTR("	= \"%.63ls\""),pwszText );
		}
	}
	else
		PrintOut( pReportFile, WSTR(" = (PTR:%p)"), pAddress);
}


/// <summary> 
///		格式化参数值。
/// </summary> 
/// <param name="pReportFile">
///		异常报告输出的文件句柄。
/// </param>
/// <param name="basicType">
///		参数类型。
/// </param>
/// <param name="length">
///		值长度。
/// </param>
/// <param name="pAddress">
///		值地址。
/// </param>
void XDBExceptionReport::FormatOutputValue(   
	HANDLE		pReportFile,
    DWORD		basicType,
    DWORD64		length,
    PVOID		pAddress )
{
	XDB_ASSERT( pAddress != NULL );
	XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
	// 检查地址
	if( pAddress == NULL )
	{
		PrintOut( pReportFile,WSTR(" = (E_ADDR:%p)"), pAddress );
		return;
	}
     
	// 根据数据类型进行处理
	CHAR	ch = 0;
	WCHAR	wch= 0;
	switch( basicType )
	{
	case btChar		:	/// ANSI字符
		ch = *(CHAR*)pAddress;
		if( ch != 0 )
			PrintOut( pReportFile, WSTR(" = '%hc'"),ch );
		else
			PrintOut( pReportFile, WSTR(" = ''") );
		break;
	case btWChar	:	/// UNICODE字符
		wch = *(WCHAR*)pAddress;
		if( wch != 0 )
			PrintOut( pReportFile, WSTR(" = '%lc'"),wch );
		else
			PrintOut( pReportFile, WSTR(" = ''") );
		break;
	case btBSTR		:	/// 字符串
		if( !IsBadStringPtrW( *(LPWSTR*)pAddress,64 ) )
			PrintOut( pReportFile, WSTR(" = \"%.63ls\""),*(LPWSTR*)pAddress );
		else
			PrintOut( pReportFile, WSTR(" = [%p]"),pAddress );
		break;
	case btInt		:	/// 有符号整数
	case btUInt		:	/// 无符号整数
	case btLong		:	/// 有符号长整数
	case btULong	:	/// 无符号长整数
	case btHresult	:	/// HRESULT
	case btBool		:	/// 布尔值
		if( length == sizeof(BYTE) )
			PrintOut( pReportFile, WSTR(" = %d"), *(BYTE*)pAddress );
		else if( length == sizeof(SHORT) )
			PrintOut( pReportFile, WSTR(" = %d"), *(WORD*)pAddress );
		else if( length == sizeof( DWORD ) )
			PrintOut( pReportFile, WSTR(" = %d"), *(DWORD*)pAddress );
		else if( length == sizeof( DWORD64 ) )
			PrintOut( pReportFile, WSTR(" = %ld"), *(DWORD64*)pAddress );
		else
			PrintOut( pReportFile, WSTR(" = [%p]"), pAddress);
		break;
	case btFloat	:	/// 浮点数
		if( length == 4 )
			PrintOut( pReportFile, WSTR(" = %f"), *(float*)pAddress);
		else if( length == 8 )
			PrintOut( pReportFile, WSTR(" = %lf"),*(double *)pAddress );
		else
			PrintOut( pReportFile, WSTR(" = [%p]"), pAddress);
		break;
	case btVariant	:	/// 可变数据类型
		{	
			VARIANT* pVal = ( VARIANT * )pAddress ;
			FormatVariantValue( pReportFile,pVal );
		}
		break;
	case btDate		:	/// 日期类型
	case btBCD		:	/// BCD类型
	case btCurrency	:	/// 货币类型
	case btComplex	:	/// 复数
	case btBit		:	/// 二进制
	case btNoType	:	/// 没有数据类型
	default:
		PrintOut( pReportFile, WSTR(" = [%p]"), pAddress);
		break;
	}
}

/// <summary> 
///		输出信息到文件。
/// </summary> 
/// <param name="pReportFile">
///		文件句柄。
/// </param>
/// <param name="format">
///		格式化字符串。
/// </param>
/// <returns>
///		写入的字符个数。
/// </returns>
int __cdecl XDBExceptionReport::PrintOut( HANDLE pReportFile,const WCHAR * format, ...)
{
	WCHAR szBuff[MAX_OUT_BUFFER+1] = { 0 };
    int   retValue = 0;
    DWORD cbWritten = 0;
    va_list argptr;
    va_start( argptr, format );
    retValue = _vsntprintf( szBuff,MAX_OUT_BUFFER,format, argptr );
    va_end( argptr );
	::WriteFile( pReportFile, szBuff, retValue * sizeof(WCHAR), &cbWritten, 0 );
    return retValue;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值