Mixing SEH and C++ EH
Some people have noticed my use of two classes (SehGuard and SehException) in a lot of my code and became curious. The reason I’m using them is so I can mix C++ exception handling with structured exception handling. Obviously if you enable C++ with SEH Exceptions in your compiler you can catch SEH exceptions with ‘catch(…)’ but unforutunately you don’t get an object with which you can get any useful information. I went searching for a workaround and found this blog post.
The following is my implementation of the two classes, which is based off the code in that original post (tstring, tcout, etc are just preprocessor defines that resolve to either string/cout/etc or wstring/wcout/etc depending on whether Unicode is defined or not).
Seh.h
// Use pragma directive if supported.
#pragma once
// Use preprocessor header guards if pragma once is unavailable
// Required to ensure portability
#ifndef __CERBERUS__SEH_H
#define __CERBERUS__SEH_H
// Cerberus
#include “StringWrap.h”
// Windows API
#include <Windows.h>
// C++ Standard Library
#include <iostream>
// Function-local SEH guard. Proxies SEH to C++ EH
// Catch via SehException
class SehGuard
{
public:
SehGuard();
~SehGuard();
private:
void* m_prev;
};
// SEH proxy exception.
// Catch this to catch structured exceptions as C++ exceptions.
// Must have SehGuard object created for it to work (function-local).
class SehException
{
public:
SehException(int Code, struct _EXCEPTION_POINTERS* pException);
DWORD GetCode() const;
PVOID GetAddress() const;
struct _EXCEPTION_POINTERS* GetExceptionPointers() const;
private:
unsigned int m_Code;
struct _EXCEPTION_POINTERS* m_pException;
};
// Ostream overload for SehException. To make debug output easier.
std::tostream& operator<< ( std::tostream& out, const SehException& x );
// SEH to C++ EH proxy function
extern void SehTranslatorFunction(unsigned int, struct _EXCEPTION_POINTERS*);
// Generic unhandled exception filter
extern LONG WINAPI MyGenericUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo);
#endif // __CERBERUS__SEH_H
// Cerberus#include “Seh.h”#include “StringWrap.h”// Windows API#include <Windows.h>#include <Dbghelp.h>#include <eh.h>#include <tchar.h>// C++ Standard Library#include <string>#include <vector>// Proxies SEH to C++ EHvoid SehTranslatorFunction(unsigned int Code, struct _EXCEPTION_POINTERS* pException){throw SehException(Code,pException);}// ConstructorSehGuard::SehGuard(){// Set SEH translatorm_prev = _set_se_translator(SehTranslatorFunction);}// DestructorSehGuard::~SehGuard(){// Reset SEH translator_set_se_translator(reinterpret_cast<_se_translator_function>(m_prev));}// Proxy exception constructorSehException::SehException( int Code, struct _EXCEPTION_POINTERS* pException ): m_Code(Code), m_pException(pException){ }// Get exception codeDWORD SehException::GetCode() const{return m_Code;}// Get exception addressPVOID SehException::GetAddress() const{return m_pException->ExceptionRecord->ExceptionAddress;}// Get exception data pointerstruct _EXCEPTION_POINTERS* SehException::GetExceptionPointers() const{return m_pException;}// Ostream overload for SehException. To make debug output easier.// TODO: Full output for x86 and x64 with registers, stack trace, etcstd::tostream& operator<<( std::tostream& out, const SehException& x ){out << _T(”Code: “) << std::hex << x.GetCode() << _T(”. Address: “)<< x.GetAddress() << std::dec << _T(”.”);return out;}// Generic unhandled exception filterLONG WINAPI MyGenericUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo){// Get the current timeSYSTEMTIME sTime;GetLocalTime( &sTime );// Pull out the datestd::vector<TCHAR> Date(10);GetDateFormat(LOCALE_USER_DEFAULT, 0, &sTime, _T(”yyyyMMdd”), &Date[0],10);// Pull out the timestd::vector<TCHAR> Time(10);GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT, &sTime,_T(”hhmmss”), &Time[0], 10);// Create a filename for the crash dump out of the current// date and time.std::tstring Path(TEXT(”Crash-”));Path.append(&Date[0]).append(&Time[0]).append(_T(”.txt”));// Create file to dump outputHANDLE hFile = CreateFile(Path.c_str(), GENERIC_WRITE, 0, NULL,CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);// Create minidumpMINIDUMP_EXCEPTION_INFORMATION aMiniDumpInfo;aMiniDumpInfo.ThreadId = GetCurrentThreadId();aMiniDumpInfo.ExceptionPointers = ExceptionInfo;aMiniDumpInfo.ClientPointers = TRUE;// Write minidumpMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,(MINIDUMP_TYPE) (MiniDumpWithFullMemory|MiniDumpWithHandleData),&aMiniDumpInfo, NULL, NULL);// Close file handleCloseHandle(hFile);// Execute handlerreturn EXCEPTION_EXECUTE_HANDLER;}