/************************************************************************************* 
  This file is a part of CrashRpt library.

  Copyright (c) 2003, Michael Carruth
  All rights reserved.
 
  Redistribution and use in source and binary forms, with or without modification, 
  are permitted provided that the following conditions are met:
 
   * Redistributions of source code must retain the above copyright notice, this 
     list of conditions and the following disclaimer.
 
   * Redistributions in binary form must reproduce the above copyright notice, 
     this list of conditions and the following disclaimer in the documentation 
     and/or other materials provided with the distribution.
 
   * Neither the name of the author nor the names of its contributors 
     may be used to endorse or promote products derived from this software without 
     specific prior written permission.
 

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
  SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***************************************************************************************/

// File: CrashHandler.cpp
// Description: Exception handling and report generation functionality.
// Authors: mikecarruth, zexspectrum
// Date: 

#include "stdafx.h"
#include "crash_handler.h"
#include "base/utility.h"
#include "resource.h"
#include <sys/stat.h>
#include <psapi.h>
#include "base/strconv.h"
#include <rtcapi.h>
#include <Shellapi.h>

#ifndef _AddressOfReturnAddress

// Taken from: http://msdn.microsoft.com/en-us/library/s975zw7k(VS.71).aspx
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif

// _ReturnAddress and _AddressOfReturnAddress should be prototyped before use 
EXTERNC void * _AddressOfReturnAddress(void);
EXTERNC void * _ReturnAddress(void);

#endif 

extern HANDLE g_hModuleCrashRpt;
CCrashHandler* CCrashHandler::m_pProcessCrashHandler = NULL;

CCrashHandler::CCrashHandler()
{
  m_bInitialized = FALSE;  
  m_dwFlags = 0;
  m_MinidumpType = MiniDumpNormal;
  //m_bAppRestart = FALSE;  
  send_method_ = 0;  
  m_lpfnCallback = NULL;
  m_bAddScreenshot = FALSE;
  m_dwScreenshotFlags = 0;    
  m_nJpegQuality = 95;
  m_hEvent = NULL;  
  m_pCrashDesc = NULL;

  InitPrevExceptionHandlerPointers();
}

CCrashHandler::~CCrashHandler()
{
  Destroy();
}

int CCrashHandler::Init(
  LPCTSTR lpcszAppName,
  LPCTSTR lpcszAppVersion,
  LPCTSTR lpcszCrashSenderPath,
  LPGETLOGFILE lpfnCallback, 
  LPCTSTR lpcszUrl,
  int send_method,
  DWORD dwFlags,
  LPCTSTR lpcszPrivacyPolicyURL,
  LPCTSTR lpcszDebugHelpDLLPath,
  MINIDUMP_TYPE MiniDumpType,
  LPCTSTR lpcszErrorReportSaveDir,
  LPCTSTR lpcszRestartCmdLine,
  LPCTSTR lpcszLangFilePath,
  LPCTSTR lpcszCustomSenderIcon)
{ 
  crSetErrorMsg(_T("Unspecified error."));
  
  m_dwFlags = dwFlags;

  // Save minidump type  
  m_MinidumpType = MiniDumpType;

  // Save user supplied callback
  m_lpfnCallback = lpfnCallback;
    
  // Save application name
  m_sAppName = lpcszAppName;

  // If no app name provided, use the default (EXE name)
  if(m_sAppName.IsEmpty())
  {
    m_sAppName = Utility::getAppName();
  }

  // Save app version
  m_sAppVersion = lpcszAppVersion;

  // If no app version provided, use the default (EXE product version)
  if(m_sAppVersion.IsEmpty())
  {
    // Get EXE image name
    CString sImageName = Utility::GetModuleName(NULL);
    m_sAppVersion = Utility::GetProductVersion(sImageName);
    if(m_sAppVersion.IsEmpty())
    {
      // If product version missing, return error.      
      crSetErrorMsg(_T("Application version is not specified."));
      return 1;
    }
  }

  // Get process image name
  m_sImageName = Utility::GetModuleName(NULL);

  m_sCustomSenderIcon = lpcszCustomSenderIcon;

  if(!m_sCustomSenderIcon.IsEmpty())
  {
    CString sResourceFile;
    CString sIconIndex;
    int nIconIndex = 0;

    int nComma = m_sCustomSenderIcon.ReverseFind(',');    
    if(nComma>=0)
    {
      sResourceFile = m_sCustomSenderIcon.Left(nComma);      
      sIconIndex = m_sCustomSenderIcon.Mid(nComma+1);
      sIconIndex.TrimLeft();
      sIconIndex.TrimRight();
      nIconIndex = _ttoi(sIconIndex);      
    }
    else
    {
      sResourceFile = m_sCustomSenderIcon;
    }

    sResourceFile.TrimRight();        

    if(nIconIndex==-1)
    {
      crSetErrorMsg(_T("Invalid index of custom icon (it should not equal to -1)."));
      return 1;
    }

  }

  // Save URL to send reports via HTTP
  if(lpcszUrl!=NULL)
  {
    m_sUrl = CString(lpcszUrl);
  }

  // Check that we store ZIP archives only when error reports are not being sent.
  BOOL bSendErrorReport = (dwFlags&CR_INST_DONT_SEND_REPORT)?FALSE:TRUE;
  BOOL bStoreZIPArchives = (dwFlags&CR_INST_STORE_ZIP_ARCHIVES)?TRUE:FALSE;
  if(bSendErrorReport && bStoreZIPArchives)
  {
    crSetErrorMsg(_T("The flag CR_INST_STORE_ZIP_ARCHIVES should be used with CR_INST_DONT_SEND_REPORT flag."));
    return 1;
  }
  
  m_sRestartCmdLine = lpcszRestartCmdLine;
  
  // Save report sending priorities
  send_method_ = send_method;

  // Save privacy policy URL (if exists)
  if(lpcszPrivacyPolicyURL!=NULL)
    m_sPrivacyPolicyURL = lpcszPrivacyPolicyURL;

  // Get the name of CrashRpt DLL
  LPTSTR pszCrashRptModule = NULL;

#ifndef CRASHRPT_LIB
  #ifdef _DEBUG
    pszCrashRptModule = _T("crash_reportd.dll");
  #else
    pszCrashRptModule = _T("crash_report.dll");
  #endif //_DEBUG
#else //!CRASHRPT_LIB
  pszCrashRptModule = NULL;
#endif
  
  // Save path to crash_sender.exe
  if(lpcszCrashSenderPath==NULL)
  {
    // By default assume that crash_sender.exe is located in the same dir as crash_report.dll    
    m_sPathToCrashSender = Utility::GetModulePath((HMODULE)g_hModuleCrashRpt);    
  }
  else
  {
    // Save user-specified path    
    m_sPathToCrashSender = CString(lpcszCrashSenderPath);    
  }

  // Get CrashSender EXE name
  CString sCrashSenderName;

#ifdef _DEBUG
  sCrashSenderName = _T("crash_senderd.exe");
#else
  sCrashSenderName = _T("crash_sender.exe");
#endif //_DEBUG

  // Check that crash_sender.exe file exists
  if(m_sPathToCrashSender.Right(1)!='\\')
      m_sPathToCrashSender+="\\";    
    
  HANDLE hFile = CreateFile(m_sPathToCrashSender+sCrashSenderName, FILE_GENERIC_READ, 
      FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);  
  if(hFile==INVALID_HANDLE_VALUE)
  { 
    crSetErrorMsg(_T("crash_sender.exe is not found in the specified path."));
    return 1;
  }
  else
  {
    CloseHandle(hFile);   
  }
  
  // Determine where to look for language file.
  if(lpcszLangFilePath!=NULL)
  {
    // User has provided the custom lang file path.
    m_sLangFileName = lpcszLangFilePath;
  }
  else
  {
    // Look for crashrpt_lang.ini in the same folder as crash_sender.exe.
    m_sLangFileName = m_sPathToCrashSender + _T("crashrpt_lang.ini");
  }
  
  m_sPathToCrashSender += sCrashSenderName;

  CString sLangFileVer = Utility::GetINIString(m_sLangFileName, _T("Settings"), _T("CrashRptVersion"));
  int lang_file_ver = _ttoi(sLangFileVer);
  if(lang_file_ver!=CRASHRPT_VER)
  {    
    crSetErrorMsg(_T("Missing language file or wrong language file version."));
    return 1; // Language INI file has wrong version!
  }
    
  if(lpcszDebugHelpDLLPath==NULL)
  {
    // By default assume that debughlp.dll is located in the same dir as crash_report.dll    
    m_sPathToDebugHelpDll = Utility::GetModulePath((HMODULE)g_hModuleCrashRpt);       
  }
  else
  {
    m_sPathToDebugHelpDll = CString(lpcszDebugHelpDLLPath);        
  }  

  CString sDebugHelpDLL_name = "dbghelp.dll";  

  if(m_sPathToDebugHelpDll.Right(1)!='\\')
    m_sPathToDebugHelpDll+="\\";

  HANDLE hDbgHelpDll = LoadLibrary(m_sPathToDebugHelpDll+sDebugHelpDLL_name);    
    
  if(!hDbgHelpDll)
  {
    //try again ... fallback to dbghelp.dll in path
    m_sPathToDebugHelpDll = _T("");
    hDbgHelpDll = LoadLibrary(sDebugHelpDLL_name);
    if(!hDbgHelpDll)
    {     
      crSetErrorMsg(_T("Couldn't load dbghelp.dll."));      
      return 1;
    }    
  }

  m_sPathToDebugHelpDll += sDebugHelpDLL_name;

  if(hDbgHelpDll!=NULL)
  {
    FreeLibrary((HMODULE)hDbgHelpDll);
    hDbgHelpDll = NULL;
  }

  // Generate unique GUID for this crash report.
  if(0!=Utility::GenerateGUID(m_sCrashGUID))
  {
    ATLASSERT(0);
    crSetErrorMsg(_T("Couldn't generate crash GUID."));
    return 1; 
  }

  // Create event that will be used to synchronize with crash_sender.exe process
  CString sEventName;
  sEventName.Format(_T("Local\\CrashRptEvent_%s"), m_sCrashGUID);
  m_hEvent = CreateEvent(NULL, FALSE, FALSE, sEventName);
  if(m_hEvent==NULL)
  {
    ATLASSERT(m_hEvent!=NULL);
    crSetErrorMsg(_T("Couldn't create synchronization event."));
    return 1; 
  }
  
  if(lpcszErrorReportSaveDir==NULL)
  {
    // Create %LOCAL_APPDATA%\CrashRpt\UnsavedCrashReports\AppName_AppVer folder.
    CString sLocalAppDataFolder;
    DWORD dwCSIDL = CSIDL_LOCAL_APPDATA;
    Utility::GetSpecialFolder(dwCSIDL, sLocalAppDataFolder);
    m_sUnsentCrashReportsFolder.Format(_T("%s\\CrashRpt\\UnsentCrashReports\\%s_%s"), 
      sLocalAppDataFolder, m_sAppName, m_sAppVersion);
  }
  else
  {
    m_sUnsentCrashReportsFolder = lpcszErrorReportSaveDir;
  }

  BOOL bCreateDir = Utility::CreateFolder(m_sUnsentCrashReportsFolder);
  if(!bCreateDir)
  {
    ATLASSERT(0);
    crSetErrorMsg(_T("Couldn't create crash report directory."));
    return 1; 
  }
  
  // Set exception handlers with initial values (NULLs)
  InitPrevExceptionHandlerPointers();
   
  // Set exception handlers that work on per-process basis
  int nSetProcessHandlers = SetProcessExceptionHandlers(dwFlags);   
  if(nSetProcessHandlers!=0)
  {
    ATLASSERT(nSetProcessHandlers==0);
    crSetErrorMsg(_T("Couldn't set C++ exception handlers for current process."));
    return 1;
  }

  // Set exception handlers that work on per-thread basis
  int nSetThreadHandlers = SetThreadExceptionHandlers(dwFlags);
  if(nSetThreadHandlers!=0)
  {
    ATLASSERT(nSetThreadHandlers==0);
    crSetErrorMsg(_T("Couldn't set C++ exception handlers for main execution thread."));
    return 1;
  }
  
  // Associate this handler with the caller process
  m_pProcessCrashHandler =  this;

  // Pack configuration info to shared memory.
  // It will be passed to crash_sender.exe later.
  m_pCrashDesc = PackCrashInfoIntoSharedMem(&m_SharedMem, FALSE);

  // If client wants us to send pending error reports that were queued recently,
  // launch the crash_sender.exe and make it to alert user and send the reports.
  if(dwFlags&CR_INST_SEND_QUEUED_REPORTS)
  {
    // Create temporary shared mem.
    CSharedMem tmpSharedMem;    
    CRASH_DESCRIPTION* pCrashDesc = PackCrashInfoIntoSharedMem(&tmpSharedMem, TRUE);
    pCrashDesc->m_bSendRecentReports = TRUE;
    if(0!=LaunchCrashSender(tmpSharedMem.GetName(), TRUE, NULL))
    {
      crSetErrorMsg(_T("Couldn't launch crash_sender.exe process."));
      return 1;
    }

    m_pTmpCrashDesc = m_pCrashDesc;
    m_pTmpSharedMem = &m_SharedMem;
  }

  // Initialization OK.
  m_bInitialized = TRUE;
  crSetErrorMsg(_T("Success."));
  return 0;
}

// Packs config info to shared mem.
CRASH_DESCRIPTION* CCrashHandler::PackCrashInfoIntoSharedMem(CSharedMem* pSharedMem, BOOL bTempMem)
{
  m_pTmpSharedMem = pSharedMem;

  CString sSharedMemName;
  if(bTempMem)
    sSharedMemName.Format(_T("%s-tmp"), m_sCrashGUID);    
  else 
    sSharedMemName = m_sCrashGUID;

  // Initialize shared memory.
  BOOL bSharedMem = pSharedMem->Init(sSharedMemName, FALSE, SHARED_MEM_MAX_SIZE);
  if(!bSharedMem)
  {
    ATLASSERT(0);
    crSetErrorMsg(_T("Couldn't initialize shared memory."));
    return NULL; 
  }

  // Create memory view.
  m_pTmpCrashDesc = 
    (CRASH_DESCRIPTION*)pSharedMem->CreateView(0, sizeof(CRASH_DESCRIPTION));  
  if(m_pTmpCrashDesc==NULL)
  {
    ATLASSERT(0);
    crSetErrorMsg(_T("Couldn't create shared memory view."));
    return NULL; 
  }

  // Pack config information to shared memory
  memset(m_pTmpCrashDesc, 0, sizeof(CRASH_DESCRIPTION));
  memcpy(m_pTmpCrashDesc->m_uchMagic, "CRD", 3);  
  m_pTmpCrashDesc->m_wSize = sizeof(CRASH_DESCRIPTION);
  m_pTmpCrashDesc->m_dwTotalSize = sizeof(CRASH_DESCRIPTION);  
  m_pTmpCrashDesc->m_dwCrashRptVer = CRASHRPT_VER;
  m_pTmpCrashDesc->m_dwInstallFlags = m_dwFlags;
  m_pTmpCrashDesc->m_MinidumpType = m_MinidumpType;
  m_pTmpCrashDesc->m_bAddScreenshot = m_bAddScreenshot;
  m_pTmpCrashDesc->m_dwScreenshotFlags = m_dwScreenshotFlags;  
  //m_pTmpCrashDesc->m_bAppRestart = m_bAppRestart;
  m_pTmpCrashDesc->send_method = send_method_;
  m_pTmpCrashDesc->m_dwAppNameOffs = PackString(m_sAppName);
  m_pTmpCrashDesc->m_dwAppVersionOffs = PackString(m_sAppVersion);
  m_pTmpCrashDesc->m_dwCrashGUIDOffs = PackString(m_sCrashGUID);
  m_pTmpCrashDesc->m_dwImageNameOffs = PackString(m_sImageName);
  m_pTmpCrashDesc->m_dwLangFileNameOffs = PackString(m_sLangFileName);
  m_pTmpCrashDesc->m_dwPathToDebugHelpDllOffs = PackString(m_sPathToDebugHelpDll);
  m_pTmpCrashDesc->m_dwRestartCmdLineOffs = PackString(m_sRestartCmdLine);
  m_pTmpCrashDesc->m_dwPrivacyPolicyURLOffs = PackString(m_sPrivacyPolicyURL);
  m_pTmpCrashDesc->m_dwUnsentCrashReportsFolderOffs = PackString(m_sUnsentCrashReportsFolder);  
  m_pTmpCrashDesc->m_dwCustomSenderIconOffs = PackString(m_sCustomSenderIcon);
  m_pTmpCrashDesc->m_dwUrlOffs = PackString(m_sUrl); 
  return m_pTmpCrashDesc;
}

DWORD CCrashHandler::PackString(CString str)
{
  DWORD dwTotalSize = m_pTmpCrashDesc->m_dwTotalSize;
  int nStrLen = str.GetLength()*sizeof(TCHAR);
  WORD wLength = (WORD)(sizeof(STRING_DESC)+nStrLen);
  
  LPBYTE pView = m_pTmpSharedMem->CreateView(dwTotalSize, wLength);  
  STRING_DESC* pStrDesc = (STRING_DESC*)pView;
  memcpy(pStrDesc->m_uchMagic, "STR", 3);
  pStrDesc->m_wSize = wLength;
  memcpy(pView+sizeof(STRING_DESC), str.GetBuffer(0), nStrLen); 

  m_pTmpCrashDesc->m_dwTotalSize += wLength;

  m_pTmpSharedMem->DestroyView(pView);
  return dwTotalSize;
}

DWORD CCrashHandler::PackFileItem(FileItem& fi)
{
  DWORD dwTotalSize = m_pTmpCrashDesc->m_dwTotalSize;
  WORD wLength = sizeof(FILE_ITEM);
  m_pTmpCrashDesc->m_dwTotalSize += wLength;
  m_pTmpCrashDesc->m_uFileItems++;

  LPBYTE pView = m_pTmpSharedMem->CreateView(dwTotalSize, wLength);    
  FILE_ITEM* pFileItem = (FILE_ITEM*)pView;

  memcpy(pFileItem->m_uchMagic, "FIL", 3);  
  pFileItem->m_dwSrcFilePathOffs = PackString(fi.m_sSrcFilePath);
  pFileItem->m_dwDstFileNameOffs = PackString(fi.m_sDstFileName);
  pFileItem->m_dwDescriptionOffs = PackString(fi.m_sDescription);
  pFileItem->m_bMakeCopy = fi.m_bMakeCopy;
  pFileItem->m_wSize = (WORD)(m_pTmpCrashDesc->m_dwTotalSize-dwTotalSize);
  
  m_pTmpSharedMem->DestroyView(pView);
  return dwTotalSize;
}

DWORD CCrashHandler::PackProperty(CString sName, CString sValue)
{
  DWORD dwTotalSize = m_pTmpCrashDesc->m_dwTotalSize;
  WORD wLength = sizeof(CUSTOM_PROP);
  m_pTmpCrashDesc->m_dwTotalSize += wLength;
  m_pTmpCrashDesc->m_uCustomProps++;

  LPBYTE pView = m_pTmpSharedMem->CreateView(dwTotalSize, wLength);    
  CUSTOM_PROP* pProp = (CUSTOM_PROP*)pView;

  memcpy(pProp->m_uchMagic, "CPR", 3); 
  pProp->m_dwNameOffs = PackString(sName);
  pProp->m_dwValueOffs = PackString(sValue);
  pProp->m_wSize = (WORD)(m_pTmpCrashDesc->m_dwTotalSize-dwTotalSize);
  
  m_pTmpSharedMem->DestroyView(pView);
  return dwTotalSize;
}

DWORD CCrashHandler::PackRegKey(CString sKeyName, CString sDstFileName)
{
  DWORD dwTotalSize = m_pTmpCrashDesc->m_dwTotalSize;
  WORD wLength = sizeof(REG_KEY);
  m_pTmpCrashDesc->m_dwTotalSize += wLength;
  m_pTmpCrashDesc->m_uRegKeyEntries++;

  LPBYTE pView = m_pTmpSharedMem->CreateView(dwTotalSize, wLength);    
  REG_KEY* pKey = (REG_KEY*)pView;

  memcpy(pKey->m_uchMagic, "REG", 3); 
  pKey->m_dwRegKeyNameOffs = PackString(sKeyName);
  pKey->m_dwDstFileNameOffs = PackString(sDstFileName);
  pKey->m_wSize = (WORD)(m_pTmpCrashDesc->m_dwTotalSize-dwTotalSize);
  
  m_pTmpSharedMem->DestroyView(pView);
  return dwTotalSize;
}

BOOL CCrashHandler::IsInitialized()
{
  return m_bInitialized;
}

int CCrashHandler::Destroy()
{
  crSetErrorMsg(_T("Unspecified error."));

  if(!m_bInitialized)
  {
    crSetErrorMsg(_T("Can't destroy not initialized crash handler."));
    return 1;
  }  

  // Reset exception callback
  if (m_oldSehHandler)
    SetUnhandledExceptionFilter(m_oldSehHandler);

  m_oldSehHandler = NULL;

  // All installed per-thread C++ exception handlers should be uninstalled 
  // using crUninstallFromCurrentThread() before calling Destroy()

  {
    CAutoLock lock(&m_csThreadExceptionHandlers);
    ATLASSERT(m_ThreadExceptionHandlers.size()==0);          
  }

  m_pProcessCrashHandler = NULL;

  // OK.
  m_bInitialized = FALSE;
  crSetErrorMsg(_T("Success."));
  return 0;
}

// Sets internal pointers to previously used exception handlers to NULL
void CCrashHandler::InitPrevExceptionHandlerPointers()
{
  m_oldSehHandler = NULL;

#if _MSC_VER>=1300
  m_prevPurec = NULL;
  m_prevNewHandler = NULL;
#endif

#if _MSC_VER>=1300 && _MSC_VER<1400    
   m_prevSec = NULL;
#endif

#if _MSC_VER>=1400
  m_prevInvpar = NULL;
#endif  

  m_prevSigABRT = NULL;  
  m_prevSigINT = NULL;  
  m_prevSigTERM = NULL;
}

CCrashHandler* CCrashHandler::GetCurrentProcessCrashHandler()
{   
  return m_pProcessCrashHandler;
}

void CCrashHandler::ReleaseCurrentProcessCrashHandler()
{
  if(m_pProcessCrashHandler!=NULL)
  {
    delete m_pProcessCrashHandler;
    m_pProcessCrashHandler = NULL;
  }
}

int CCrashHandler::SetProcessExceptionHandlers(DWORD dwFlags)
{
  crSetErrorMsg(_T("Unspecified error."));

  // If 0 is specified as dwFlags, assume all handlers should be
  // installed
  if((dwFlags&0x1FF)==0)
    dwFlags |= 0x1FFF;
  
  if(dwFlags&CR_INST_STRUCTURED_EXCEPTION_HANDLER)
  {
    // Install top-level SEH handler
    m_oldSehHandler = SetUnhandledExceptionFilter(SehHandler);    
  }

  _set_error_mode(_OUT_TO_STDERR);

#if _MSC_VER>=1300
  if(dwFlags&CR_INST_PURE_CALL_HANDLER)
  {
    // Catch pure virtual function calls.
    // Because there is one _purecall_handler for the whole process, 
    // calling this function immediately impacts all threads. The last 
    // caller on any thread sets the handler. 
    // http://msdn.microsoft.com/en-us/library/t296ys27.aspx
    m_prevPurec = _set_purecall_handler(PureCallHandler);    
  }

  if(dwFlags&CR_INST_NEW_OPERATOR_ERROR_HANDLER)
  {
    // Catch new operator memory allocation exceptions
    _set_new_mode(1); // Force malloc() to call new handler too
    m_prevNewHandler = _set_new_handler(NewHandler);
  }
#endif

#if _MSC_VER>=1400
  if(dwFlags&CR_INST_INVALID_PARAMETER_HANDLER)
  {
    // Catch invalid parameter exceptions.
    m_prevInvpar = _set_invalid_parameter_handler(InvalidParameterHandler); 
  }
#endif


#if _MSC_VER>=1300 && _MSC_VER<1400    
  if(dwFlags&CR_INST_SECURITY_ERROR_HANDLER)
  {
    // Catch buffer overrun exceptions
    // The _set_security_error_handler is deprecated in VC8 C++ run time library
    m_prevSec = _set_security_error_handler(SecurityHandler);
  }
#endif

   // Set up C++ signal handlers
  
  
  if(dwFlags&CR_INST_SIGABRT_HANDLER)
  {
#if _MSC_VER>=1400  
  _set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT);
#endif
  // Catch an abnormal program termination
  m_prevSigABRT = signal(SIGABRT, SigabrtHandler);  
  }
   
  if(dwFlags&CR_INST_SIGILL_HANDLER)
  {
    // Catch illegal instruction handler
    m_prevSigINT = signal(SIGINT, SigintHandler);     
  }
  
  if(dwFlags&CR_INST_TERMINATE_HANDLER)
  {
    // Catch a termination request
    m_prevSigTERM = signal(SIGTERM, SigtermHandler);          
  }

  crSetErrorMsg(_T("Success."));
  return 0;
}

int CCrashHandler::UnSetProcessExceptionHandlers()
{
  crSetErrorMsg(_T("Unspecified error."));

  // Unset all previously set handlers

#if _MSC_VER>=1300
  if(m_prevPurec!=NULL)
    _set_purecall_handler(m_prevPurec);

  if(m_prevNewHandler!=NULL)
    _set_new_handler(m_prevNewHandler);
#endif

#if _MSC_VER>=1400
  if(m_prevInvpar!=NULL)
    _set_invalid_parameter_handler(m_prevInvpar);
#endif //_MSC_VER>=1400  

#if _MSC_VER>=1300 && _MSC_VER<1400    
  if(m_prevSec!=NULL)
    _set_security_error_handler(m_prevSec);
#endif //_MSC_VER<1400
     
  if(m_prevSigABRT!=NULL)
    signal(SIGABRT, m_prevSigABRT);  
  
  if(m_prevSigINT!=NULL)
    signal(SIGINT, m_prevSigINT);     

  if(m_prevSigTERM!=NULL)
   signal(SIGTERM, m_prevSigTERM);    

  crSetErrorMsg(_T("Success."));
  return 0;
}

// Installs C++ exception handlers that function on per-thread basis
int CCrashHandler::SetThreadExceptionHandlers(DWORD dwFlags)
{
  crSetErrorMsg(_T("Unspecified error."));

  // If 0 is specified as dwFlags, assume all available exception handlers should be
  // installed  
  if((dwFlags&0x1FFF)==0)
    dwFlags |= 0x1FFF;

  DWORD dwThreadId = GetCurrentThreadId();

  CAutoLock lock(&m_csThreadExceptionHandlers);

  std::map<DWORD, ThreadExceptionHandlers>::iterator it = 
    m_ThreadExceptionHandlers.find(dwThreadId);

  if(it!=m_ThreadExceptionHandlers.end())
  {
    // handlers are already set for the thread    
    crSetErrorMsg(_T("Can't install handlers for current thread twice."));
    return 1; // failed
  }
  
  ThreadExceptionHandlers handlers;

  if(dwFlags&CR_INST_TERMINATE_HANDLER)
  {
    // Catch terminate() calls. 
    // In a multithreaded environment, terminate functions are maintained 
    // separately for each thread. Each new thread needs to install its own 
    // terminate function. Thus, each thread is in charge of its own termination handling.
    // http://msdn.microsoft.com/en-us/library/t6fk7h29.aspx
    handlers.m_prevTerm = set_terminate(TerminateHandler);       
  }

  if(dwFlags&CR_INST_UNEXPECTED_HANDLER)
  {
    // Catch unexpected() calls.
    // In a multithreaded environment, unexpected functions are maintained 
    // separately for each thread. Each new thread needs to install its own 
    // unexpected function. Thus, each thread is in charge of its own unexpected handling.
    // http://msdn.microsoft.com/en-us/library/h46t5b69.aspx  
    handlers.m_prevUnexp = set_unexpected(UnexpectedHandler);    
  }

  if(dwFlags&CR_INST_SIGFPE_HANDLER)
  {
    // Catch a floating point error
    typedef void (*sigh)(int);
    handlers.m_prevSigFPE = signal(SIGFPE, (sigh)SigfpeHandler);     
  }

  
  if(dwFlags&CR_INST_SIGILL_HANDLER)
  {
    // Catch an illegal instruction
    handlers.m_prevSigILL = signal(SIGILL, SigillHandler);     
  }

  if(dwFlags&CR_INST_SIGSEGV_HANDLER)
  {
    // Catch illegal storage access errors
    handlers.m_prevSigSEGV = signal(SIGSEGV, SigsegvHandler);   
  }

  // Insert the structure to the list of handlers  
  m_ThreadExceptionHandlers[dwThreadId] = handlers;

  // OK.
  crSetErrorMsg(_T("Success."));
  return 0;
}

int CCrashHandler::UnSetThreadExceptionHandlers()
{
  crSetErrorMsg(_T("Unspecified error."));

  DWORD dwThreadId = GetCurrentThreadId();

  CAutoLock lock(&m_csThreadExceptionHandlers);

  std::map<DWORD, ThreadExceptionHandlers>::iterator it = 
    m_ThreadExceptionHandlers.find(dwThreadId);

  if(it==m_ThreadExceptionHandlers.end())
  {
    // No exception handlers were installed for the caller thread?    
    crSetErrorMsg(_T("Crash handler wasn't previously installed for current thread."));
    return 1;
  }
  
  ThreadExceptionHandlers* handlers = &(it->second);

  if(handlers->m_prevTerm!=NULL)
    set_terminate(handlers->m_prevTerm);

  if(handlers->m_prevUnexp!=NULL)
    set_unexpected(handlers->m_prevUnexp);

  if(handlers->m_prevSigFPE!=NULL)
    signal(SIGFPE, handlers->m_prevSigFPE);     

  if(handlers->m_prevSigILL!=NULL)
    signal(SIGINT, handlers->m_prevSigILL);     

  if(handlers->m_prevSigSEGV!=NULL)
    signal(SIGSEGV, handlers->m_prevSigSEGV); 

  // Remove from the list
  m_ThreadExceptionHandlers.erase(it);

  // OK.
  crSetErrorMsg(_T("Success."));
  return 0;
}


int CCrashHandler::AddFile(LPCTSTR pszFile, LPCTSTR pszDestFile, LPCTSTR pszDesc, DWORD dwFlags)
{
  crSetErrorMsg(_T("Unspecified error."));

  // make sure the file exist
  struct _stat st;
  int result = _tstat(pszFile, &st);

  if (result!=0 && (dwFlags&CR_AF_MISSING_FILE_OK)==0)
  {    
    crSetErrorMsg(_T("Couldn't stat file. File may not exist."));
    return 1;
  }

  // Add file to file list.
  FileItem fi;
  fi.m_sDescription = pszDesc;
  fi.m_sSrcFilePath = pszFile;
  fi.m_bMakeCopy = (dwFlags&CR_AF_MAKE_FILE_COPY)!=0;
  if(pszDestFile!=NULL)
  {
    fi.m_sDstFileName = pszDestFile;
    m_files[pszDestFile] = fi;
  }
  else
  {
    CString sDestFile = pszFile;
    int pos = -1;
    sDestFile.Replace('/', '\\');
    pos = sDestFile.ReverseFind('\\');
    if(pos!=-1)
      sDestFile = sDestFile.Mid(pos+1);

    fi.m_sDstFileName = sDestFile;
    m_files[sDestFile] = fi;
  }

  // Pack this file item into shared mem.
  PackFileItem(fi);

  // OK.
  crSetErrorMsg(_T("Success."));
  return 0;
}

int CCrashHandler::AddProperty(CString sPropName, CString sPropValue)
{
  crSetErrorMsg(_T("Unspecified error."));

  if(sPropName.IsEmpty())
  {
    crSetErrorMsg(_T("Invalid property name specified."));
    return 1;
  }

  m_props[sPropName] = sPropValue;

  PackProperty(sPropName, sPropValue);

  // OK.
  crSetErrorMsg(_T("Success."));
  return 0;
}

int CCrashHandler::AddScreenshot(DWORD dwFlags, int nJpegQuality)
{ 
  crSetErrorMsg(_T("Unspecified error."));

  if(nJpegQuality<0 || nJpegQuality>100)
  {
    crSetErrorMsg(_T("Invalid Jpeg quality."));
    return 1;
  }

  m_bAddScreenshot = TRUE;
  m_dwScreenshotFlags = dwFlags;
  m_nJpegQuality = nJpegQuality;

  // Pack this info into shared memory
  m_pCrashDesc->m_bAddScreenshot = TRUE;
  m_pCrashDesc->m_dwScreenshotFlags = dwFlags;
  m_pCrashDesc->m_nJpegQuality = nJpegQuality;

  crSetErrorMsg(_T("Success."));
  return 0;
}

static void LogWhenUnNormalExit() {
  FILE* fp = fopen(CR_CRASH_LOG_FILE, "ab+");
  if ( fp!= NULL) {
    // 0 stands for fail 
    fprintf(fp, "%d\t0\n", time(NULL));
  } else {
    //  printf("fail to open file crash.log");
    MessageBoxA(NULL, "fail to open file crash.log", "error", 0);
  }
  fclose(fp);
}

// write to log, telling that a crash happen this time
int CCrashHandler::GenerateErrorReport(
  PCR_EXCEPTION_INFO pExceptionInfo)
{
  LogWhenUnNormalExit();
  crSetErrorMsg(_T("Unspecified error."));

  // Validate input parameters 
  if(pExceptionInfo==NULL)
  {
    crSetErrorMsg(_T("Exception info is NULL."));
    return 1;
  }

  // Get exception pointers if not provided. 
  if(pExceptionInfo->pexcptrs==NULL)
  {
    GetExceptionPointers(pExceptionInfo->code, &pExceptionInfo->pexcptrs);
  }

  // Save current process ID, thread ID and exception pointers address to shared mem.
  m_pCrashDesc->m_dwProcessId = GetCurrentProcessId();
  m_pCrashDesc->m_dwThreadId = GetCurrentThreadId();
  m_pCrashDesc->m_pExceptionPtrs = pExceptionInfo->pexcptrs;  
  m_pCrashDesc->m_bSendRecentReports = FALSE;
  m_pCrashDesc->m_nExceptionType = pExceptionInfo->exctype;

  if(pExceptionInfo->exctype==CR_SEH_EXCEPTION)
  {
    m_pCrashDesc->m_dwExceptionCode = pExceptionInfo->code;
  }
  else if(pExceptionInfo->exctype==CR_CPP_SIGFPE)
  {
    m_pCrashDesc->m_uFPESubcode = pExceptionInfo->fpe_subcode;
  }
  else if(pExceptionInfo->exctype==CR_CPP_INVALID_PARAMETER)
  {
    m_pCrashDesc->m_dwInvParamExprOffs = PackString(pExceptionInfo->expression);
    m_pCrashDesc->m_dwInvParamFunctionOffs = PackString(pExceptionInfo->function);
    m_pCrashDesc->m_dwInvParamFileOffs = PackString(pExceptionInfo->file);
    m_pCrashDesc->m_uInvParamLine = pExceptionInfo->line;
  }

  // If error report is being generated manually, disable app restart.
  if(pExceptionInfo->bManual)
    m_pCrashDesc->m_dwInstallFlags &= ~CR_INST_APP_RESTART;

  // Let client know about the crash via the crash callback function. 
  if (m_lpfnCallback!=NULL && m_lpfnCallback(NULL)==FALSE)
  {
    crSetErrorMsg(_T("The operation was cancelled by client."));
    return 2;
  }
  
  // Start the crash_sender.exe process which will take the dekstop screenshot, 
  // copy user-specified files to the error report folder, create minidump, 
  // notify user about crash, compress the report into ZIP archive and send 
  // the error report. 
    
  int result = LaunchCrashSender(m_sCrashGUID, TRUE, &pExceptionInfo->hSenderProcess);
  if(result!=0)
  {
    ATLASSERT(result==0);
    crSetErrorMsg(_T("Error launching crash_sender.exe"));
    
    // Failed to launch crash sender process.
    // Try notify user about crash using message box.
    CString szCaption;
    szCaption.Format(_T("%s has stopped working"), Utility::getAppName());
    CString szMessage;
    szMessage.Format(_T("Error launching crash_sender.exe"));
    MessageBox(NULL, szMessage, szCaption, MB_OK|MB_ICONERROR);    
    return 3;
  }
  
  // OK
  crSetErrorMsg(_T("Success."));
  return 0; 
}

int CCrashHandler::AddRegKey(LPCTSTR szRegKey, LPCTSTR szDstFileName, DWORD dwFlags)
{
  UNREFERENCED_PARAMETER(dwFlags);
  crSetErrorMsg(_T("Unspecified error."));

  if(szDstFileName==NULL ||
     szRegKey==NULL)
  {
    // Invalid param
    crSetErrorMsg(_T("Invalid registry key or invalid destination file is specified."));
    return 1;
  }

  CString sDstFileName = CString(szDstFileName);
  if(sDstFileName.Find(_T("\\"))>=0 ||
     sDstFileName.Find(_T("\r"))>=0 ||
     sDstFileName.Find(_T("\n"))>=0 ||
     sDstFileName.Find(_T("\t"))>=0)
  {
    // Inacceptable character found.
    return 1;
  }

  HKEY hKey = NULL;
  CString sKey = szRegKey;  
  int nSkip = 0;
  if(sKey.Left(19).Compare(_T("HKEY_LOCAL_MACHINE\\"))==0)
  {
    hKey = HKEY_LOCAL_MACHINE;
    nSkip = 18;
  }
  else if(sKey.Left(18).Compare(_T("HKEY_CURRENT_USER\\"))==0)
  {
    hKey = HKEY_CURRENT_USER;
    nSkip = 17;
  }    
  else 
  {
    crSetErrorMsg(_T("Invalid registry branch is specified."));
    return 1; // Invalid key.
  }

  CString sSubKey = sKey.Mid(nSkip+1);  
  if(sSubKey.IsEmpty())
  {
    crSetErrorMsg(_T("Empty subkey is not allowed."));
    return 2; // Empty subkey not allowed
  }

  HKEY hKeyResult = NULL;
  LRESULT lResult = RegOpenKeyEx(hKey, sSubKey, 0, KEY_READ, &hKeyResult);
  if(lResult!=ERROR_SUCCESS)
  {
    crSetErrorMsg(_T("The registry key coudn't be open."));
    return 3; // Invalid key.
  }
  RegCloseKey(hKeyResult);

  m_RegKeys[CString(szRegKey)] = sDstFileName;

  PackRegKey(szRegKey, sDstFileName);

  // OK
  crSetErrorMsg(_T("Success."));
  return 0;
}

// The following code gets exception pointers using a workaround found in CRT code.
void CCrashHandler::GetExceptionPointers(DWORD dwExceptionCode, 
  EXCEPTION_POINTERS** ppExceptionPointers)
{
  // The following code was taken from VC++ 8.0 CRT (invarg.c: line 104)
  
  EXCEPTION_RECORD ExceptionRecord;
  CONTEXT ContextRecord;
  memset(&ContextRecord, 0, sizeof(CONTEXT));
  
#ifdef _X86_

  __asm {
      mov dword ptr [ContextRecord.Eax], eax
      mov dword ptr [ContextRecord.Ecx], ecx
      mov dword ptr [ContextRecord.Edx], edx
      mov dword ptr [ContextRecord.Ebx], ebx
      mov dword ptr [ContextRecord.Esi], esi
      mov dword ptr [ContextRecord.Edi], edi
      mov word ptr [ContextRecord.SegSs], ss
      mov word ptr [ContextRecord.SegCs], cs
      mov word ptr [ContextRecord.SegDs], ds
      mov word ptr [ContextRecord.SegEs], es
      mov word ptr [ContextRecord.SegFs], fs
      mov word ptr [ContextRecord.SegGs], gs
      pushfd
      pop [ContextRecord.EFlags]
  }

  ContextRecord.ContextFlags = CONTEXT_CONTROL;
#pragma warning(push)
#pragma warning(disable:4311)
  ContextRecord.Eip = (ULONG)_ReturnAddress();
  ContextRecord.Esp = (ULONG)_AddressOfReturnAddress();
#pragma warning(pop)
  ContextRecord.Ebp = *((ULONG *)_AddressOfReturnAddress()-1);


#elif defined (_IA64_) || defined (_AMD64_)

  /* Need to fill up the Context in IA64 and AMD64. */
  RtlCaptureContext(&ContextRecord);

#else  /* defined (_IA64_) || defined (_AMD64_) */

  ZeroMemory(&ContextRecord, sizeof(ContextRecord));

#endif  /* defined (_IA64_) || defined (_AMD64_) */

  ZeroMemory(&ExceptionRecord, sizeof(EXCEPTION_RECORD));

  ExceptionRecord.ExceptionCode = dwExceptionCode;
  ExceptionRecord.ExceptionAddress = _ReturnAddress();

  ///
  
  EXCEPTION_RECORD* pExceptionRecord = new EXCEPTION_RECORD;
  memcpy(pExceptionRecord, &ExceptionRecord, sizeof(EXCEPTION_RECORD));
  CONTEXT* pContextRecord = new CONTEXT;
  memcpy(pContextRecord, &ContextRecord, sizeof(CONTEXT));

  *ppExceptionPointers = new EXCEPTION_POINTERS;
  (*ppExceptionPointers)->ExceptionRecord = pExceptionRecord;
  (*ppExceptionPointers)->ContextRecord = pContextRecord;  
}

// Launches crash_sender.exe process
int CCrashHandler::LaunchCrashSender(CString sCmdLineParams, BOOL bWait, HANDLE* phProcess)
{
  crSetErrorMsg(_T("Success."));
  
  /* Create CrashSender process */

  STARTUPINFO si;
  memset(&si, 0, sizeof(STARTUPINFO));
  si.cb = sizeof(STARTUPINFO);

  PROCESS_INFORMATION pi;
  memset(&pi, 0, sizeof(PROCESS_INFORMATION));    

  CString sCmdLine;
  sCmdLine.Format(_T("\"%s\" \"%s\""), sCmdLineParams, sCmdLineParams.GetBuffer(0));
  BOOL bCreateProcess = CreateProcess(
    m_sPathToCrashSender, sCmdLine.GetBuffer(0), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
  if(!bCreateProcess)
  {
    ATLASSERT(bCreateProcess);
    crSetErrorMsg(_T("Error creating CrashSender process."));
    return 1;
  }
  
  if(bWait)
  {
    /* Wait until CrashSender finishes with making screenshot, 
      copying files, creating minidump. */  

    WaitForSingleObject(m_hEvent, INFINITE);  
  }

  // Return handle to the crash_sender.exe process.
  if(phProcess!=NULL)
  {
    *phProcess = pi.hProcess;
  }

  return 0;
}

// Acquires the crash lock. Other threads that may crash will
// wait until we unlock.
void CCrashHandler::CrashLock(BOOL bLock)
{
  if(bLock)
    m_csCrashLock.Lock();
  else
    m_csCrashLock.Unlock();
}

// Structured exception handler
LONG WINAPI CCrashHandler::SehHandler(PEXCEPTION_POINTERS pExceptionPtrs)
{ 
  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);  

  if(pCrashHandler!=NULL)
  {
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. 
    pCrashHandler->CrashLock(TRUE);

    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_SEH_EXCEPTION;
    ei.pexcptrs = pExceptionPtrs;

    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);    
  }   

  // Unreacheable code  
  return EXCEPTION_EXECUTE_HANDLER;
}

// CRT terminate() call handler
void __cdecl CCrashHandler::TerminateHandler()
{
  // Abnormal program termination (terminate() function was called)

  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  {
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_TERMINATE_CALL;
    
    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);    
  }    
}

// CRT unexpected() call handler
void __cdecl CCrashHandler::UnexpectedHandler()
{
  // Unexpected error (unexpected() function was called)

  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  {
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_UNEXPECTED_CALL;

    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);    
  }    
}

// CRT Pure virtual method call handler
#if _MSC_VER>=1300
void __cdecl CCrashHandler::PureCallHandler()
{
  // Pure virtual function call

  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  {
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_PURE_CALL;
    
    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);    
  }  
}
#endif

// CRT buffer overrun handler. Available in CRT 7.1 only
#if _MSC_VER>=1300 && _MSC_VER<1400
void __cdecl CCrashHandler::SecurityHandler(int code, void *x)
{
  // Security error (buffer overrun).

  code;
  x;
  
  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  {    
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_SECURITY_ERROR;
    
    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);
  }
}
#endif 

// CRT invalid parameter handler
#if _MSC_VER>=1400
void __cdecl CCrashHandler::InvalidParameterHandler(
  const wchar_t* expression, 
  const wchar_t* function, 
  const wchar_t* file, 
  unsigned int line, 
  uintptr_t pReserved)
 {
   pReserved;

   // Invalid parameter exception

   CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
   ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  {
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_INVALID_PARAMETER;
    ei.expression = expression;
    ei.function = function;
    ei.file = file;
    ei.line = line;    

    pCrashHandler->GenerateErrorReport(&ei);

    pCrashHandler->CrashLock(FALSE);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);    
  }   
 }
#endif

// CRT new operator fault handler
#if _MSC_VER>=1300
int __cdecl CCrashHandler::NewHandler(size_t)
{
  // 'new' operator memory allocation exception

  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  {     
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_NEW_OPERATOR_ERROR;
    ei.pexcptrs = NULL;    

    pCrashHandler->GenerateErrorReport(&ei);

    pCrashHandler->CrashLock(FALSE);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);
  }

  // Unreacheable code
  return 0;
}
#endif //_MSC_VER>=1300

// CRT SIGABRT signal handler
void CCrashHandler::SigabrtHandler(int)
{
  // Caught SIGABRT C++ signal

  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  {     
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_SIGABRT;    

    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);   
  }
}

// CRT SIGFPE signal handler
void CCrashHandler::SigfpeHandler(int /*code*/, int subcode)
{
  // Floating point exception (SIGFPE)
 
  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  {     
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_SIGFPE;
    ei.pexcptrs = (PEXCEPTION_POINTERS)_pxcptinfoptrs;
    ei.fpe_subcode = subcode;

    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);    
  }
}

// CRT sigill signal handler
void CCrashHandler::SigillHandler(int)
{
  // Illegal instruction (SIGILL)

  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  {    
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_SIGILL;
    
    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);    
  }
}

// CRT sigint signal handler
void CCrashHandler::SigintHandler(int)
{
  // Interruption (SIGINT)

  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  { 
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_SIGINT;

    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);    
  }
}

// CRT SIGSEGV signal handler
void CCrashHandler::SigsegvHandler(int)
{
  // Invalid storage access (SIGSEGV)

  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
   
  if(pCrashHandler!=NULL)
  {     
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);    
    ei.exctype = CR_CPP_SIGSEGV;
    ei.pexcptrs = (PEXCEPTION_POINTERS)_pxcptinfoptrs;
        
    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);    
  }
}

// CRT SIGTERM signal handler
void CCrashHandler::SigtermHandler(int)
{
  // Termination request (SIGTERM)

  CCrashHandler* pCrashHandler = CCrashHandler::GetCurrentProcessCrashHandler();
  ATLASSERT(pCrashHandler!=NULL);

  if(pCrashHandler!=NULL)
  {    
    // Acquire lock to avoid other threads (if exist) to crash while we are 
    // inside. We do not unlock, because process is to be terminated.
    pCrashHandler->CrashLock(TRUE);

    // Fill in the exception info
    CR_EXCEPTION_INFO ei;
    memset(&ei, 0, sizeof(CR_EXCEPTION_INFO));
    ei.cb = sizeof(CR_EXCEPTION_INFO);
    ei.exctype = CR_CPP_SIGTERM;
    
    pCrashHandler->GenerateErrorReport(&ei);

    // Terminate process
    TerminateProcess(GetCurrentProcess(), 1);    
  }
}


