⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 excepthandler.cpp

📁 CExceptionLogger
💻 CPP
📖 第 1 页 / 共 3 页
字号:
/*  
Module : EXCEPTHANDLER.CPP
Purpose: Implementation for a class which intercepts and logs details about unhandled exceptions
         such as access violations, stack overflows and division by zero.
         The class is based upon the February 2002 MSDN article called "Under the Hood: Improved
         Error Reporting with DBGHELP 5.1 by Matt Pietrek. 

         To compile the class you need the November 2002 Platform SDK or later installed.
         You can add the ExceptHandler.cpp/h files directly to your C++ project or put the exception
         handler class in a DLL and arrange for your app / dll to get it installed via a LoadLibrary
         call.

         A binary version of the ExceptHandler is provided in the XCPTHLR.DLL which is included
         in this distribution.

         To get this code to work on client machines you will need to arrange for the distribution
         of the DBGHELP 5.1 dll which can be obtained from November 2002 Platform SDK or later. Select
         "Install Debugging Tools for Windows" to get the DLL installed on your machine. Also
         note that the DBGHELP dll is under Windows System File protection on recent versions of Windows, 
         so put the DBGHELP dll in the same directory as your application.

         To provide symbols for your code in release mode, you should modify your projects settings as 
         follows:

         1. Project Settings -> Link -> Debug (Category) , Enable "Debug Info" and pick "Microsoft Format".
         2. Same Place, pick "Separate Types".
         3. In the Project Options edit box on the same page, add "/OPT:REF". This will ensure that 
         unreferrenced functions are removed from the resultant binary.
         4. Project Settings -> C++ -> General (Category) Debug Info Combo -> "Program Database"
         5. Sample Place, you may need to set "Optimizations" to "Disable (Debug)" if your are getting
         call stacks which do not make sense.

         Also remember to ship the resultant pdb files with your code (or arrange for them to be somehow
         available) so that CExceptionLogger can give the source and line information in the resultant
         log.

         By default the CExceptionLogger class will generate a log file with the name "name of exe.exception".
         In this file (which is ASCII if the CExceptionLogger is build as ASCII and UNICODE if the 
         CExceptionLogger is build as a UNICODE) is the following information:

         1.  Date and Time when the exception occurred.
         2.  Exception Code
         3.  Details about exception if an access violation
         4.  Details about exception address including linear address, section, offset and module path
         5.  Full path of the process
         6.  Current Win32 working directory
         7.  Command line for the process
         8.  Process ID
         9.  Thread ID where the exception occurred.
         10. Enumeration of all the threads in the process (assuming Toolhelp32 is available) including
              i)   Thread ID
              ii)  Priority and Delta priority
              iii) References
              iv)  Creation Time
              v)   Kernel and User Time
         11. Enumeration of the Modules in the process (again only if Toolhelp32 is available) including
              i)   Name and full path
              ii)  Global and per Process reference count
              iii) module handle
              iv)  size
              v)   All symbols for that module fully expanded (see details later)
         12. All the x86 registers
         13. Call Stack where exception occured, including section, offset, module, function and line 
             information. 
         14. At each stack frame as well as for all modules, all variables and parameters all also logged. 
             All basic types such as voids, chars, shorts, words, ints, DWORDS, floats, doubles and longs 
             are logged. UDT's including structs, unions and classes are also fully recursed into to 
             display their members. Info for each type includes its name, address, type and value. 
             In addition if the variable is a array, each value in the array is fully logged.

         Please note that due to all the symbols which even a very small appp has, each log of an exception
         can generate upwards of 100K of data or more. My opinion on this is that disk space is cheap while 
         developers time looking for an intermittent release only bug is not!!!!!.             

         References:

         Bugslayer,      MSJ,  August 1998 by John Robbins, http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0898/bugslayer0898.htm
         Under the Hood, MSDN, March 2002 by Matt Pietrek,  http://msdn.microsoft.com/msdnmag/issues/02/03/Hood/Hood0203.asp



Created: PJN / 14-03-2002
History: None


Copyright (c) 2002 by PJ Naughter.  (Web: www.naughter.com, Email: pjna@naughter.com)

All rights reserved.

Copyright / Usage Details:

You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 
when your product is released in binary form. You are allowed to modify the source code in any way you want 
except you cannot modify the copyright details at the top of each module. If you want to distribute source 
code with your application, then you are only allowed to distribute versions released by the author. This is 
to maintain a single distribution point for the source code. 

*/



/////////////////////// Includes //////////////////////////////
#include "stdafx.h"
#include "ExceptHandler.h"
#ifndef _INC_TCHAR
#pragma message("To avoid this message, put tchar.h in your PCH")
#include <tchar.h>
#endif
#ifndef _INC_TOOLHELP32
#pragma message("To avoid this message, put tlhelp32.h in your PCH")
#include <tlhelp32.h>
#endif
#ifndef _INC_STDIO
#pragma message("To avoid this message, put stdio.h in your PCH")
#include <stdio.h>
#endif



/////////////////////// Macros / Defines  / Locals //////////////////////

#pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "oleaut32.lib")

//Class which looks after pulling in ToolHelp32 and OpenThread dynamically. 
//That way the CExceptionlogger class will work on NT 4 (without of course the Thread
//and module lists) and Win95 which does not have OpenThread (but does
//have Toolhelp32!!!!)
class _EXCEPTION_LOGGER_DATA
{
public:
//Constructors /Destructors
  _EXCEPTION_LOGGER_DATA();

  typedef HANDLE (WINAPI CREATETOOLHELP32SNAPSHOT)(DWORD, DWORD);
  typedef CREATETOOLHELP32SNAPSHOT* LPCREATETOOLHELP32SNAPSHOT;

  typedef BOOL (WINAPI THREAD32FIRST)(HANDLE, LPTHREADENTRY32);
  typedef THREAD32FIRST* LPTHREAD32FIRST;

  typedef BOOL (WINAPI THREAD32NEXT)(HANDLE, LPTHREADENTRY32);
  typedef THREAD32NEXT* LPTHREAD32NEXT;

  typedef BOOL (WINAPI MODULE32FIRST)(HANDLE, LPMODULEENTRY32);
  typedef MODULE32FIRST* LPMODULE32FIRST;

  typedef BOOL (WINAPI MODULE32NEXT)(HANDLE, LPMODULEENTRY32);
  typedef MODULE32NEXT* LPMODULE32NEXT;

  typedef HANDLE (WINAPI OPENTHREAD)(DWORD, BOOL, DWORD);
  typedef OPENTHREAD* LPOPENTHREAD;

  LPCREATETOOLHELP32SNAPSHOT m_lpfnCreateToolhelp32Snapshot;
  LPTHREAD32FIRST            m_lpfnThread32First;
  LPTHREAD32NEXT             m_lpfnThread32Next;
  LPMODULE32FIRST            m_lpfnModule32First;
  LPMODULE32NEXT             m_lpfnModule32Next;
  LPOPENTHREAD               m_lpfnOpenThread;
  HMODULE                    m_hKernel32;

  void ResetFunctionPointers();
};

_EXCEPTION_LOGGER_DATA::_EXCEPTION_LOGGER_DATA()
{
  m_hKernel32 = LoadLibrary(_T("KERNEL32.DLL"));
  if (m_hKernel32)
  {
    m_lpfnOpenThread               = (LPOPENTHREAD) GetProcAddress(m_hKernel32, "OpenThread");
    m_lpfnCreateToolhelp32Snapshot = (LPCREATETOOLHELP32SNAPSHOT) GetProcAddress(m_hKernel32, "CreateToolhelp32Snapshot");
    m_lpfnThread32First            = (LPTHREAD32FIRST) GetProcAddress(m_hKernel32, "Thread32First");
    m_lpfnThread32Next             = (LPTHREAD32NEXT) GetProcAddress(m_hKernel32, "Thread32Next");
#ifdef _UNICODE
    m_lpfnModule32First = (LPMODULE32FIRST) GetProcAddress(m_hKernel32, "Module32FirstW");
    m_lpfnModule32Next  = (LPMODULE32NEXT) GetProcAddress(m_hKernel32, "Module32NextW");
#else
    m_lpfnModule32First = (LPMODULE32FIRST) GetProcAddress(m_hKernel32, "Module32First");
    m_lpfnModule32Next  = (LPMODULE32NEXT) GetProcAddress(m_hKernel32, "Module32Next");
#endif
  }
  
  //Any function pointer NULL, then set them all to NULL. Helps simplify the code which 
  //uses the function pointers
  if (m_lpfnCreateToolhelp32Snapshot == NULL || m_lpfnThread32First == NULL || 
      m_lpfnThread32Next == NULL || m_lpfnModule32First == NULL || m_lpfnModule32Next == NULL)
    ResetFunctionPointers();
}

void _EXCEPTION_LOGGER_DATA::ResetFunctionPointers()
{
  m_lpfnCreateToolhelp32Snapshot = NULL;
  m_lpfnThread32First = NULL;
  m_lpfnThread32Next = NULL;
  m_lpfnModule32First = NULL;
  m_lpfnModule32Next = NULL;
  if (m_hKernel32)
  {
    FreeLibrary(m_hKernel32);
    m_hKernel32 = NULL;
  }
}

//Statics used by the CExceptionLogger class
TCHAR                        CExceptionLogger::m_pszLogFilename[_MAX_PATH];
LPTOP_LEVEL_EXCEPTION_FILTER CExceptionLogger::m_pPreviousFilter = NULL;
HANDLE                       CExceptionLogger::m_hLogFile = INVALID_HANDLE_VALUE;
TCHAR                        CExceptionLogger::m_szTempLogLine[4096];
TCHAR                        CExceptionLogger::m_szTempFileName[_MAX_PATH];
BYTE                         CExceptionLogger::m_bySymbolInfo[sizeof(SYMBOL_INFO) + 1024];


//The one and only instance of the crash handler
CExceptionLogger g_CrashHandlerLogger;

//The local variable which handle the function pointers
_EXCEPTION_LOGGER_DATA _ExceptionLoggerData;



/////////////////////// Implementation ////////////////////////

CExceptionLogger::CExceptionLogger()
{
  //Install our exception handler
  m_pPreviousFilter = SetUnhandledExceptionFilter(UnHandledExceptionFilter);

  //The name of the Crash handler log file will by default
  //be "name of exe.crash.log"
  GetModuleFileName(NULL, m_pszLogFilename, _MAX_PATH);
  
  //Note we use the C Runtime here as the exception has not occured yet, so we
  //can be confident that the CRT is ok at this time
  _tcscat(m_pszLogFilename, _T(".exception"));
}

CExceptionLogger::~CExceptionLogger()
{
  //Restore the old exception handler
  SetUnhandledExceptionFilter(m_pPreviousFilter);
}

long CExceptionLogger::UnHandledExceptionFilter(_EXCEPTION_POINTERS* pExceptionInfo)
{
  //Open up the log file, Notice we use FILE_FLAG_WRITE_THROUGHT to 
  //avoid caching of writes. That way if our code goes belly up, we will
  //at least have all the data up to then safely written to disk
  m_hLogFile = CreateFile(m_pszLogFilename, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0);
  if (m_hLogFile != INVALID_HANDLE_VALUE)
  {
    //Seek to the end of the Log file so that we always append to the crash log file
    SetFilePointer(m_hLogFile, 0, 0, FILE_END);

    //Let the helper function do its job
    GenerateCrashLog(pExceptionInfo);

    //Close the file now that we are finished with it
    CloseHandle(m_hLogFile);
    m_hLogFile = INVALID_HANDLE_VALUE;
  }

  //Chain to the previous exception if there is one
  if (m_pPreviousFilter)
    return m_pPreviousFilter(pExceptionInfo);
  else
    return EXCEPTION_CONTINUE_SEARCH;
}

void CExceptionLogger::GenerateCrashLog(_EXCEPTION_POINTERS* pExceptionInfo)
{
  //Write out a banner first to separate multiple log entries
  Log(_T("-----------------------------------------------------\r\n"));

  //get the current time so that it goes into the file
  SYSTEMTIME st;
  GetLocalTime(&st);
  Log(_T("Time of Exception: %02d:%02d:%02d.%03d %d/%d/%d (D/M/Y)\r\n"), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, st.wDay, st.wMonth, st.wYear);

  //Log the type of unhandled exception
  Log(_T("Exception Code: 0x%08x\r\n"), pExceptionInfo->ExceptionRecord->ExceptionCode);

  //Display extra info if an Access Violation
  if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
  {
    if (pExceptionInfo->ExceptionRecord->NumberParameters > 0)
    {
      if (pExceptionInfo->ExceptionRecord->ExceptionInformation[0] == 0)
        Log(_T("Access Violation Exception: Due to the thread attempting to read from an inaccessible address\r\n"));
      else
        Log(_T("Access Violation Exception: Due to the thread attempting to write to an inaccessible address\r\n"));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -