/* * 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 BTWindow : public BASE_CLASS { protected: /// Object initialization (0 parameters). BTWindow(void) _BTWND_INITIALIZER_ () { } /// Object initialization (1 parameter). template explicit BTWindow(T1 param1) _BTWND_INITIALIZER_ (param1) { } /// Object initialization (2 parameters) template BTWindow(T1 param1, T2 param2) _BTWND_INITIALIZER_ (param1, param2) { } /// Object initialization (3 parameters). template BTWindow(T1 param1, T2 param2, T3 param3) _BTWND_INITIALIZER_ (param1, param2, param3) { } /// Object initialization (4 parameters). template BTWindow(T1 param1, T2 param2, T3 param3, T4 param4) _BTWND_INITIALIZER_ (param1, param2, param3, param4) { } /// Object initialization (5 parameters). template 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 inline WNDPROC BTWindow::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 LRESULT BTWindow::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 LRESULT BTWindow::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 LRESULT BTWindow::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 BTDialog : public BASE_CLASS { protected: /// Object initialization (0 parameters). BTDialog(void) _BTDLG_INITIALIZER_ () { } /// Object initialization (1 parameter). template explicit BTDialog(T1 param1) _BTDLG_INITIALIZER_ (param1) { } /// Object initialization (2 parameters) template BTDialog(T1 param1, T2 param2) _BTDLG_INITIALIZER_ (param1, param2) { } /// Object initialization (3 parameters). template BTDialog(T1 param1, T2 param2, T3 param3) _BTDLG_INITIALIZER_ (param1, param2, param3) { } /// Object initialization (4 parameters). template BTDialog(T1 param1, T2 param2, T3 param3, T4 param4) _BTDLG_INITIALIZER_ (param1, param2, param3, param4) { } /// Object initialization (5 parameters). template 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 inline DLGPROC BTDialog::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 INT_PTR BTDialog::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 INT_PTR BTDialog::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 INT_PTR BTDialog::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_