359 lines
13 KiB
C++
359 lines
13 KiB
C++
/*
|
|
* This is a part of the BugTrap package.
|
|
* Copyright (c) 2005-2009 IntelleSoft.
|
|
* All rights reserved.
|
|
*
|
|
* Description: This class provides better error handling for ATL/WTL windows.
|
|
* Author: Maksim Pyatkovskiy.
|
|
*
|
|
* This source code is only intended as a supplement to the
|
|
* BugTrap package reference and related electronic documentation
|
|
* provided with the product. See these sources for detailed
|
|
* information regarding the BugTrap package.
|
|
*/
|
|
|
|
#ifndef _BTATLWINDOW_H_
|
|
#define _BTATLWINDOW_H_
|
|
|
|
#pragma once
|
|
|
|
#ifndef _BUGTRAP_H_
|
|
#error Include BugTrap.h first
|
|
#endif // _BUGTRAP_H_
|
|
|
|
#ifndef __cplusplus
|
|
#error C++ compiler is required
|
|
#endif // __cplusplus
|
|
|
|
#ifndef __ATLWIN_H__
|
|
#error This class cannot be used in non ATL applications
|
|
#endif // __ATLWIN_H__
|
|
|
|
namespace ATL
|
|
{
|
|
|
|
#if ! defined _ATL_NO_EXCEPTIONS || defined _EXCEPTION_
|
|
#define _BTWND_INITIALIZER_ : m_pfnFilter(&BT_SehFilter), m_pfnBaseWndProc(BASE_CLASS::GetWindowProc()), BASE_CLASS
|
|
#else
|
|
#define _BTWND_INITIALIZER_ : m_pfnBaseWndProc(BASE_CLASS::GetWindowProc()), BASE_CLASS
|
|
#endif // _ATL_NO_EXCEPTIONS && ! _EXCEPTION_
|
|
|
|
/// This class adds error handling to ATL/WTL windows.
|
|
/// Only one form of exception handling is permitted per function,
|
|
/// therefore this class uses two functions to catch C++ and Windows errors.
|
|
template <class BASE_CLASS>
|
|
class BTWindow : public BASE_CLASS {
|
|
protected:
|
|
/// Object initialization (0 parameters).
|
|
BTWindow(void) _BTWND_INITIALIZER_ () { }
|
|
/// Object initialization (1 parameter).
|
|
template <typename T1>
|
|
explicit BTWindow(T1 param1) _BTWND_INITIALIZER_ (param1) { }
|
|
/// Object initialization (2 parameters)
|
|
template <typename T1, typename T2>
|
|
BTWindow(T1 param1, T2 param2) _BTWND_INITIALIZER_ (param1, param2) { }
|
|
/// Object initialization (3 parameters).
|
|
template <typename T1, typename T2, typename T3>
|
|
BTWindow(T1 param1, T2 param2, T3 param3) _BTWND_INITIALIZER_ (param1, param2, param3) { }
|
|
/// Object initialization (4 parameters).
|
|
template <typename T1, typename T2, typename T3, typename T4>
|
|
BTWindow(T1 param1, T2 param2, T3 param3, T4 param4) _BTWND_INITIALIZER_ (param1, param2, param3, param4) { }
|
|
/// Object initialization (5 parameters).
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5>
|
|
BTWindow(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5) _BTWND_INITIALIZER_ (param1, param2, param3, param4, param5) { }
|
|
/// This window procedure uses SEH to intercept all unhandled exceptions.
|
|
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
/// Overridden to return address of custom window procedure.
|
|
virtual WNDPROC GetWindowProc(void);
|
|
|
|
private:
|
|
#if ! defined _ATL_NO_EXCEPTIONS || defined _EXCEPTION_
|
|
/// This window procedure intercepts C++ exceptions.
|
|
LRESULT CppExceptionHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
/// Exception filter.
|
|
LONG (CALLBACK * m_pfnFilter)(PEXCEPTION_POINTERS pExceptionPointers);
|
|
#ifdef _M_X64
|
|
/// This window procedure saves exception context.
|
|
LRESULT SaveExceptionContext(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
/// Exception context.
|
|
EXCEPTION_POINTERS m_ExceptionPointers;
|
|
#endif // _M_X64
|
|
#endif // _ATL_NO_EXCEPTIONS && ! _EXCEPTION_
|
|
/// Cached address of base window procedure.
|
|
WNDPROC m_pfnBaseWndProc;
|
|
};
|
|
|
|
#undef _BTWND_INITIALIZER_
|
|
|
|
/**
|
|
* @return address of window procedure.
|
|
*/
|
|
template <class BASE_CLASS>
|
|
inline WNDPROC BTWindow<BASE_CLASS>::GetWindowProc()
|
|
{
|
|
return &WindowProc;
|
|
}
|
|
|
|
#if ! defined _ATL_NO_EXCEPTIONS || defined _EXCEPTION_
|
|
#ifdef _M_X64
|
|
/**
|
|
* @param hWnd - window handle.
|
|
* @param uMsg - specifies the Windows message to be processed.
|
|
* @param wParam - provides additional information used in message processing.
|
|
* @param lParam - provides additional information used in message processing.
|
|
* @return the return value depends on the message.
|
|
*/
|
|
template <class BASE_CLASS>
|
|
LRESULT BTWindow<BASE_CLASS>::SaveExceptionContext(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
__try {
|
|
return (*m_pfnBaseWndProc)(hWnd, uMsg, wParam, lParam);
|
|
} __except (CopyMemory(&m_ExceptionPointers, GetExceptionInformation(), sizeof(m_ExceptionPointers)), EXCEPTION_CONTINUE_SEARCH) {
|
|
return 0;
|
|
}
|
|
}
|
|
#endif // _M_X64
|
|
|
|
/**
|
|
* @param hWnd - window handle.
|
|
* @param uMsg - specifies the Windows message to be processed.
|
|
* @param wParam - provides additional information used in message processing.
|
|
* @param lParam - provides additional information used in message processing.
|
|
* @return the return value depends on the message.
|
|
*/
|
|
template <class BASE_CLASS>
|
|
LRESULT BTWindow<BASE_CLASS>::CppExceptionHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
try {
|
|
#ifdef _M_X64
|
|
return SaveExceptionContext(hWnd, uMsg, wParam, lParam);
|
|
#else
|
|
return (*m_pfnBaseWndProc)(hWnd, uMsg, wParam, lParam);
|
|
#endif// ! _M_X64
|
|
}
|
|
#ifndef _ATL_NO_EXCEPTIONS
|
|
catch (CAtlException& rException) {
|
|
BT_SetUserMessageFromCode(rException.m_hr);
|
|
// an exception will be caught by SEH handler
|
|
throw;
|
|
}
|
|
#endif // _ATL_NO_EXCEPTIONS
|
|
#ifdef _EXCEPTION_
|
|
catch (std::exception& rException) {
|
|
// extract error message
|
|
const CHAR* pszErrorMessageA = rException.what();
|
|
if (pszErrorMessageA != NULL && *pszErrorMessageA != '\0') {
|
|
#ifdef _UNICODE
|
|
DWORD dwErrorMessageSizeW = MultiByteToWideChar(CP_ACP, 0, pszErrorMessageA, -1, NULL, 0);
|
|
// alloca() cannot be used in catch block
|
|
WCHAR* pszErrorMessageW = (WCHAR*)malloc(dwErrorMessageSizeW * sizeof(WCHAR));
|
|
if (pszErrorMessageW != NULL) {
|
|
MultiByteToWideChar(CP_ACP, 0, pszErrorMessageA, -1, pszErrorMessageW, dwErrorMessageSizeW);
|
|
BT_SetUserMessage(pszErrorMessageW);
|
|
free(pszErrorMessageW);
|
|
}
|
|
#else
|
|
BT_SetUserMessage(pszErrorMessageA);
|
|
#endif // ! _UNICODE
|
|
}
|
|
m_pfnFilter = &BT_CppFilter;
|
|
// an exception will be caught by SEH handler
|
|
throw;
|
|
}
|
|
#endif // _EXCEPTION_
|
|
}
|
|
#endif // ! _ATL_NO_EXCEPTIONS || _EXCEPTION_
|
|
|
|
/**
|
|
* @param hWnd - window handle.
|
|
* @param uMsg - specifies the Windows message to be processed.
|
|
* @param wParam - provides additional information used in message processing.
|
|
* @param lParam - provides additional information used in message processing.
|
|
* @return the return value depends on the message.
|
|
*/
|
|
template <class BASE_CLASS>
|
|
LRESULT BTWindow<BASE_CLASS>::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
// This cast requires direct inheritance from CWindowImplBase, i.e.
|
|
// CWindowImplBase descendant should be declared first in the list of base classes.
|
|
// More generic solution would require additional template arguments.
|
|
BTWindow* pThis = (BTWindow*)hWnd;
|
|
#if ! defined _ATL_NO_EXCEPTIONS || defined _EXCEPTION_
|
|
__try {
|
|
return pThis->CppExceptionHandler(hWnd, uMsg, wParam, lParam);
|
|
} __except ((*pThis->m_pfnFilter)(GetExceptionInformation())) {
|
|
pThis->m_pfnFilter = &BT_SehFilter;
|
|
return 0;
|
|
}
|
|
#else
|
|
__try {
|
|
return (*pThis->m_pfnBaseWndProc)(hWnd, uMsg, wParam, lParam);
|
|
} __except (BT_SehFilter(GetExceptionInformation())) {
|
|
return 0;
|
|
}
|
|
#endif // _ATL_NO_EXCEPTIONS && ! _EXCEPTION_
|
|
}
|
|
|
|
#if ! defined _ATL_NO_EXCEPTIONS || defined _EXCEPTION_
|
|
#define _BTDLG_INITIALIZER_ : m_pfnFilter(&BT_SehFilter), m_pfnBaseDlgProc(BASE_CLASS::GetDialogProc()), BASE_CLASS
|
|
#else
|
|
#define _BTDLG_INITIALIZER_ : m_pfnBaseDlgProc(BASE_CLASS::GetDialogProc()), BASE_CLASS
|
|
#endif // _ATL_NO_EXCEPTIONS && ! _EXCEPTION_
|
|
|
|
/// This class adds error handling to ATL/WTL dialogs.
|
|
/// Only one form of exception handling is permitted per function,
|
|
/// therefore this class uses two functions to catch C++ and Windows errors.
|
|
template <class BASE_CLASS>
|
|
class BTDialog : public BASE_CLASS {
|
|
protected:
|
|
/// Object initialization (0 parameters).
|
|
BTDialog(void) _BTDLG_INITIALIZER_ () { }
|
|
/// Object initialization (1 parameter).
|
|
template <typename T1>
|
|
explicit BTDialog(T1 param1) _BTDLG_INITIALIZER_ (param1) { }
|
|
/// Object initialization (2 parameters)
|
|
template <typename T1, typename T2>
|
|
BTDialog(T1 param1, T2 param2) _BTDLG_INITIALIZER_ (param1, param2) { }
|
|
/// Object initialization (3 parameters).
|
|
template <typename T1, typename T2, typename T3>
|
|
BTDialog(T1 param1, T2 param2, T3 param3) _BTDLG_INITIALIZER_ (param1, param2, param3) { }
|
|
/// Object initialization (4 parameters).
|
|
template <typename T1, typename T2, typename T3, typename T4>
|
|
BTDialog(T1 param1, T2 param2, T3 param3, T4 param4) _BTDLG_INITIALIZER_ (param1, param2, param3, param4) { }
|
|
/// Object initialization (5 parameters).
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5>
|
|
BTDialog(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5) _BTDLG_INITIALIZER_ (param1, param2, param3, param4, param5) { }
|
|
/// This dialog procedure uses SEH to intercept all unhandled exceptions.
|
|
static INT_PTR CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
/// Overridden to return address of custom dialog procedure.
|
|
virtual DLGPROC GetDialogProc(void);
|
|
|
|
private:
|
|
#if ! defined _ATL_NO_EXCEPTIONS || defined _EXCEPTION_
|
|
/// This dialog procedure intercepts C++ exceptions.
|
|
INT_PTR CppExceptionHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
/// Exception filter.
|
|
LONG (CALLBACK * m_pfnFilter)(PEXCEPTION_POINTERS pExceptionPointers);
|
|
#ifdef _M_X64
|
|
/// This window procedure saves exception context.
|
|
INT_PTR SaveExceptionContext(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
/// Exception context.
|
|
EXCEPTION_POINTERS m_ExceptionPointers;
|
|
#endif // _M_X64
|
|
#endif // _ATL_NO_EXCEPTIONS && ! _EXCEPTION_
|
|
/// Cached address of base dialog procedure.
|
|
DLGPROC m_pfnBaseDlgProc;
|
|
};
|
|
|
|
#undef _BTDLG_INITIALIZER_
|
|
|
|
/**
|
|
* @return address of dialog procedure.
|
|
*/
|
|
template <class BASE_CLASS>
|
|
inline DLGPROC BTDialog<BASE_CLASS>::GetDialogProc()
|
|
{
|
|
return &DialogProc;
|
|
}
|
|
|
|
#if ! defined _ATL_NO_EXCEPTIONS || defined _EXCEPTION_
|
|
#ifdef _M_X64
|
|
/**
|
|
* @param hWnd - window handle.
|
|
* @param uMsg - specifies the Windows message to be processed.
|
|
* @param wParam - provides additional information used in message processing.
|
|
* @param lParam - provides additional information used in message processing.
|
|
* @return the return value depends on the message.
|
|
*/
|
|
template <class BASE_CLASS>
|
|
INT_PTR BTDialog<BASE_CLASS>::SaveExceptionContext(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
__try {
|
|
return (*m_pfnBaseDlgProc)(hWnd, uMsg, wParam, lParam);
|
|
} __except (CopyMemory(&m_ExceptionPointers, GetExceptionInformation(), sizeof(m_ExceptionPointers)), EXCEPTION_CONTINUE_SEARCH) {
|
|
return 0;
|
|
}
|
|
}
|
|
#endif // _M_X64
|
|
|
|
/**
|
|
* @param hWnd - window handle.
|
|
* @param uMsg - specifies the Windows message to be processed.
|
|
* @param wParam - provides additional information used in message processing.
|
|
* @param lParam - provides additional information used in message processing.
|
|
* @return the return value depends on the message.
|
|
*/
|
|
template <class BASE_CLASS>
|
|
INT_PTR BTDialog<BASE_CLASS>::CppExceptionHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
try {
|
|
#ifdef _M_X64
|
|
return SaveExceptionContext(hWnd, uMsg, wParam, lParam);
|
|
#else
|
|
return (*m_pfnBaseDlgProc)(hWnd, uMsg, wParam, lParam);
|
|
#endif// ! _M_X64
|
|
}
|
|
#ifndef _ATL_NO_EXCEPTIONS
|
|
catch (CAtlException& rException) {
|
|
BT_SetUserMessageFromCode(rException.m_hr);
|
|
// an exception will be caught by SEH handler
|
|
throw;
|
|
}
|
|
#endif // _ATL_NO_EXCEPTIONS
|
|
#ifdef _EXCEPTION_
|
|
catch (std::exception& rException) {
|
|
// extract error message
|
|
const CHAR* pszErrorMessageA = rException.what();
|
|
if (pszErrorMessageA != NULL && *pszErrorMessageA != '\0') {
|
|
#ifdef _UNICODE
|
|
DWORD dwErrorMessageSizeW = MultiByteToWideChar(CP_ACP, 0, pszErrorMessageA, -1, NULL, 0);
|
|
// alloca() cannot be used in catch block
|
|
WCHAR* pszErrorMessageW = (WCHAR*)malloc(dwErrorMessageSizeW * sizeof(WCHAR));
|
|
if (pszErrorMessageW != NULL) {
|
|
MultiByteToWideChar(CP_ACP, 0, pszErrorMessageA, -1, pszErrorMessageW, dwErrorMessageSizeW);
|
|
BT_SetUserMessage(pszErrorMessageW);
|
|
free(pszErrorMessageW);
|
|
}
|
|
#else
|
|
BT_SetUserMessage(pszErrorMessageA);
|
|
#endif // ! _UNICODE
|
|
}
|
|
m_pfnFilter = &BT_CppFilter;
|
|
// an exception will be caught by SEH handler
|
|
throw;
|
|
}
|
|
#endif // _EXCEPTION_
|
|
}
|
|
#endif // ! _ATL_NO_EXCEPTIONS || _EXCEPTION_
|
|
|
|
/**
|
|
* @param hWnd - window handle.
|
|
* @param uMsg - specifies the Windows message to be processed.
|
|
* @param wParam - provides additional information used in message processing.
|
|
* @param lParam - provides additional information used in message processing.
|
|
* @return the return value depends on the message.
|
|
*/
|
|
template <class BASE_CLASS>
|
|
INT_PTR BTDialog<BASE_CLASS>::DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
// This cast requires direct inheritance from CDialogImplBase, i.e.
|
|
// CDialogImplBase descendant should be declared first in the list of base classes.
|
|
// More generic solution would require additional template arguments.
|
|
BTDialog* pThis = (BTDialog*)hWnd;
|
|
#if ! defined _ATL_NO_EXCEPTIONS || defined _EXCEPTION_
|
|
__try {
|
|
return pThis->CppExceptionHandler(hWnd, uMsg, wParam, lParam);
|
|
} __except ((*pThis->m_pfnFilter)(GetExceptionInformation())) {
|
|
pThis->m_pfnFilter = &BT_SehFilter;
|
|
return 0;
|
|
}
|
|
#else
|
|
__try {
|
|
return (*pThis->m_pfnBaseDlgProc)(hWnd, uMsg, wParam, lParam);
|
|
} __except (BT_SehFilter(GetExceptionInformation())) {
|
|
return 0;
|
|
}
|
|
#endif // _ATL_NO_EXCEPTIONS && ! _EXCEPTION_
|
|
}
|
|
|
|
}
|
|
|
|
#ifndef BT_DO_NOT_USE_DEFAULT_NAMESPACES
|
|
using namespace ATL;
|
|
#endif // BT_DO_NOT_USE_DEFAULT_NAMESPACES
|
|
|
|
#endif // _BTATLWINDOW_H_
|