/* DebugHelper.h Copyright © 2004-2006 Heiko Herkenrath This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // Version 3.0, Sept 26 2005 // Heiko Herkenrath // ----------------------------- // Switches: //#define DEACTIVATE_DEBUG_HELPER //#define NO_DEBUG_HELPER //#define ALSO_USE_DEBUG_HELPER_FOR_RELEASE // ----------------------------- #if !defined(ALSO_USE_DEBUG_HELPER_FOR_RELEASE) #if !defined(_DEBUG) #define DEACTIVATE_DEBUG_HELPER #endif #endif // ----------------------------- #ifndef NO_DEBUG_HELPERS #define DEBUG_BUFFER_SIZE 1024 // Creating display text for all functions #if !defined(DEACTIVATE_DEBUG_HELPER) __inline WCHAR* CreateDebugText(const WCHAR* pszFmt, const WCHAR* pszFile, unsigned int uLine, va_list va) { static int iDebugCallCount = 0; TCHAR* pszDebugText; TCHAR* pszDebugBuf; TCHAR* ptszLastError; DWORD dwLastError; #if defined(UNICODE) char* pszLastError; #endif dwLastError = GetLastError(); iDebugCallCount++; MessageBeep(MB_ICONQUESTION); if (!pszFmt) return NULL; pszDebugText = (TCHAR*)malloc(DEBUG_BUFFER_SIZE*sizeof(TCHAR)); if (pszDebugText) { pszDebugBuf = (TCHAR*)malloc(DEBUG_BUFFER_SIZE*sizeof(TCHAR)); if (pszDebugBuf) { ptszLastError = NULL; #if defined(UNICODE) // FormatMessageW does not work with UnicoWS layer on Win9x/ME pszLastError = NULL; if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, (LPWSTR)&ptszLastError, 0, NULL) == 0) FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, (LPSTR)&pszLastError, 0, NULL); mir_sntprintf(pszDebugBuf, DEBUG_BUFFER_SIZE, _T("[Message %i]\r\nLast Error: %u\r\nDescription: %hs%sCall: %s, %u\r\n\r\n%s"), iDebugCallCount, dwLastError, pszLastError?pszLastError:"", ptszLastError?ptszLastError:_T(""), PathFindFileName(pszFile), uLine, pszFmt); if (pszLastError) LocalFree(pszLastError); if (ptszLastError) LocalFree(ptszLastError); #else FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, (LPTSTR)&ptszLastError, 0, NULL); mir_sntprintf(pszDebugBuf, DEBUG_BUFFER_SIZE, _T("[Message %i]\r\nLast Error: %u\r\nDescription: %sCall: %s, %u\r\n\r\n%s"), iDebugCallCount, dwLastError, ptszLastError?ptszLastError:_T(""), PathFindFileName(pszFile), uLine, pszFmt); if (ptszLastError) LocalFree(ptszLastError); #endif mir_vsnprintf(pszDebugText, DEBUG_BUFFER_SIZE, pszDebugBuf, va); free(pszDebugBuf); } } SetLastError(ERROR_SUCCESS); return pszDebugText; } #endif // Check if a specific flag is bitwise-or'ed into a flags variable // (returns flag name as string or empty string if not contained) #if !defined(DEACTIVATE_DEBUG_HELPER) #define INFLAGS(flags, flag) ( ((flags&flag) && ((flags^=flag)||TRUE))?_T("|"#flag):_T("")) #else #define INFLAGS(flags, flag) #endif // BOOLSTR [make a string out of a boolean value] #if !defined(DEACTIVATE_DEBUG_HELPER) #define BOOLSTR(b) ((b)?_T("TRUE"):_T("FALSE")) #else #define BOOLSTR(b) #endif // BOX [show debug message box with text] #if !defined(DEACTIVATE_DEBUG_HELPER) // Functions: #define BOX(str) BOX_Helper(_T(__FILE__), __LINE__, _T("%s"), _T(str)) #define BOX1(fmt, p1) BOX_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), NULL, NULL, NULL) #define BOX2(fmt, p1, p2) BOX_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), NULL, NULL) #define BOX3(fmt, p1, p2, p3) BOX_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), NULL) #define BOX4(fmt, p1, p2, p3, p4) BOX_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), (p4)) // --- __inline void BOX_Helper(const TCHAR* pszFile, unsigned int uLine, const TCHAR* pszFmt, ...) { va_list va; TCHAR* pszText; va_start(va, pszFmt); pszText = CreateDebugText(pszFmt, pszFile, uLine, va); va_end(va); if (pszText) { // Only show if CTRL is not pressed if (!(GetAsyncKeyState(VK_CONTROL)&0x8000)) MessageBoxEx(NULL, pszText, _T("Debug"), MB_OK|MB_TASKMODAL|MB_SETFOREGROUND, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)); free(pszText); } } #else #define BOX(str) #define BOX1(fmt, p1) #define BOX2(fmt, p1, p2) #define BOX3(fmt, p1, p2, p3) #define BOX4(fmt, p1, p2, p3, p4) #endif // STEP [show debug message box with current code step] #define STEP(section, id) BOX2("Reached Step %s|%u", _T(section), id) // STR [write line into debugger output] #if !defined(DEACTIVATE_DEBUG_HELPER) // Functions: #define STR(str) STR_Helper(_T(__FILE__), __LINE__, _T("%s"), _T(str)) #define STR1(fmt, p1) STR_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), NULL, NULL, NULL) #define STR2(fmt, p1, p2) STR_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), NULL, NULL) #define STR3(fmt, p1, p2, p3) STR_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), NULL, NULL) #define STR4(fmt, p1, p2, p3, p4) STR_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), (p4)) // --- __inline void STR_Helper(const TCHAR* pszFile, unsigned int uLine, const TCHAR* pszFmt, ...) { va_list va; TCHAR* pszText; va_start(va, pszFmt); pszText = CreateDebugText(pszFmt, pszFile, uLine, va); va_end(va); if (pszText) { OutputDebugString(pszText); OutputDebugString(_T("\r\n")); free(pszText); } } #else #define STR(str) #define STR1(fmt, p1) #define STR2(fmt, p1, p2) #define STR3(fmt, p1, p2, p3) #define STR4(fmt, p1, p2, p3, p4) #endif // DBG [simple wrapper around OutputDebugString] #if !defined(DEACTIVATE_DEBUG_HELPER) #define DBGT(str) OutputDebugString(_T(str)_T("\r\n")) #define DBG(str) { OutputDebugString(str); OutputDebugString(_T("\r\n")); } #else #define DBGT(str) #define DBG(str) #endif // LOG [append line to debug log file] #if !defined(DEACTIVATE_DEBUG_HELPER) // Functions: #define LOG(str) LOG_Helper(_T(__FILE__), __LINE__, _T("%s"), _T(str)) #define LOG1(fmt, p1) LOG_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), NULL, NULL, NULL) #define LOG2(fmt, p1, p2) LOG_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), NULL, NULL) #define LOG3(fmt, p1, p2, p3) LOG_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), NULL) #define LOG4(fmt, p1, p2, p3, p4) LOG_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), (p4)) // --- __inline void LOG_Helper(const TCHAR* pszFile, unsigned int uLine, const TCHAR* pszFmt, ...) { va_list va; DWORD dwDebugFileWritten = 0; TCHAR* pszText; TCHAR szDebugLogFile[MAX_PATH]; HANDLE hDebugLogFile; static BOOL bFirstCall = TRUE; va_start(va, pszFmt); pszText = CreateDebugText(pszFmt, pszFile, uLine, va); va_end(va); // Filename if (pszText) { GetModuleFileName(NULL, szDebugLogFile, sizeof(szDebugLogFile)-1); PathRemoveFileSpec(szDebugLogFile); PathAppend(szDebugLogFile, _T("Debug.log")); hDebugLogFile = CreateFile(szDebugLogFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hDebugLogFile == INVALID_HANDLE_VALUE) BOX("LOG ERROR: INVALID_HANDLE_VALUE"); SetFilePointer(hDebugLogFile, 0, NULL, bFirstCall?FILE_BEGIN:FILE_END); WriteFile(hDebugLogFile, "\r\n", 2*sizeof(TCHAR), &dwDebugFileWritten, NULL); WriteFile(hDebugLogFile, (PBYTE*)pszText, (DWORD)(lstrlen(pszText)*sizeof(TCHAR)), &dwDebugFileWritten, NULL); SetEndOfFile(hDebugLogFile); CloseHandle(hDebugLogFile); free(pszText); bFirstCall = FALSE; } } #else #define LOG(str) #define LOG1(fmt, p1) #define LOG2(fmt, p1, p2) #define LOG3(fmt, p1, p2, p3) #define LOG4(fmt, p1, p2, p3, p4) #endif // POP [show debug popup (Popup Plugin) with text] /* #if !defined(DEACTIVATE_DEBUG_HELPER) // Functions: #define POP(str) POP_Helper(_T(__FILE__), __LINE__, _T("%s"), _T(str)) #define POP1(fmt, p1) POP_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), NULL, NULL, NULL) #define POP2(fmt, p1, p2) POP_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), NULL, NULL) #define POP3(fmt, p1, p2, p3) POP_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), NULL) #define POP4(fmt, p1, p2, p3, p4) POP_Helper(_T(__FILE__), __LINE__, _T(fmt), (p1), (p2), (p3), (p4)) // --- __inline void POP_Helper(const TCHAR* pszFile, unsigned int uLine, const TCHAR* pszFmt, ...) { #ifdef MS_POPUP_SHOWMESSAGE va_list va; TCHAR* pszText; va_start(va, pszFmt); pszText = CreateDebugText(pszFmt, pszFile, uLine, va); va_end(va); // Only show if CTRL is not pressed if (pszText) { if (!(GetAsyncKeyState(VK_CONTROL)&0x8000)) CallServiceSync(MS_POPUP_SHOWMESSAGE, (WPARAM)pszText, (LPARAM)SM_NOTIFY); free(pszText); } #endif } #else #define POP(str) #define POP1(fmt, p1) #define POP2(fmt, p1, p2) #define POP3(fmt, p1, p2, p3) #define POP4(fmt, p1, p2, p3, p4) #endif */ // LOOPPROT [prevent a loop (for/while/repeat) from becoming an infinite loop] #if !defined(DEACTIVATE_DEBUG_HELPER) // LOOPROT(0) breaks the loop when "Esc" is pressed #define LOOPPROT(max) { \ static unsigned int uDebugLoopCount = 1; \ uDebugLoopCount++; \ if (GetAsyncKeyState(VK_ESCAPE)&0x8000) break; \ MessageBeep(MB_ICONEXCLAMATION); \ if (uDebugLoopCount > (max)) { \ break; \ BOX1("LOOPPROT:\nThe execution of the loop was stopped because it looped %u times.", max); \ uDebugLoopCount = 1;\ } \ } #else #define LOOPPROT(max) #endif #endif