/*** *DbgRpt.c - Debug Cluster Reporting Functions * * Copyright (c) 1988-1998, Microsoft Corporation. All rights reserved. * *Purpose: * *******************************************************************************/ #include #include #include #include #include #include #include #include #define CLRTL_INCLUDE_DEBUG_REPORTING #include "ClRtlDbg.h" #define _ClRtlInterlockedIncrement InterlockedIncrement #define _ClRtlInterlockedDecrement InterlockedDecrement /*--------------------------------------------------------------------------- * * Debug Reporting * --------------------------------------------------------------------------*/ static int ClRtlMessageWindow( int, const char *, const char *, const char *, const char * ); static int __clrtlMessageBoxA( LPCSTR lpText, LPCSTR lpCaption, UINT uType ); extern "C" { _CLRTL_REPORT_HOOK _pfnReportHook; long _clrtlAssertBusy = -1; int _ClRtlDbgMode[_CLRTLDBG_ERRCNT] = { _CLRTLDBG_MODE_DEBUG, _CLRTLDBG_MODE_DEBUG | _CLRTLDBG_MODE_WNDW, _CLRTLDBG_MODE_DEBUG | _CLRTLDBG_MODE_WNDW }; _HFILE _ClRtlDbgFile[_CLRTLDBG_ERRCNT] = { _CLRTLDBG_INVALID_HFILE, _CLRTLDBG_INVALID_HFILE, _CLRTLDBG_INVALID_HFILE }; } static const char * _ClRtlDbgModeMsg[_CLRTLDBG_ERRCNT] = { "Warning", "Error", "Assertion Failed" }; /*** *void _ClRtlDebugBreak - call OS-specific debug function * *Purpose: * call OS-specific debug function * *Entry: * *Exit: * *Exceptions: * *******************************************************************************/ #undef _ClRtlDbgBreak extern "C" void _cdecl _ClRtlDbgBreak( void ) { DebugBreak(); } //*** _ClRtlDbgBreak() /*** *int _ClRtlSetReportMode - set the reporting mode for a given report type * *Purpose: * set the reporting mode for a given report type * *Entry: * int nRptType - the report type * int fMode - new mode for given report type * *Exit: * previous mode for given report type * *Exceptions: * *******************************************************************************/ extern "C" int __cdecl _ClRtlSetReportMode( int nRptType, int fMode ) { int oldMode; if (nRptType < 0 || nRptType >= _CLRTLDBG_ERRCNT) return -1; if (fMode == _CLRTLDBG_REPORT_MODE) return _ClRtlDbgMode[nRptType]; /* verify flags values */ if (fMode & ~(_CLRTLDBG_MODE_FILE | _CLRTLDBG_MODE_DEBUG | _CLRTLDBG_MODE_WNDW)) return -1; oldMode = _ClRtlDbgMode[nRptType]; _ClRtlDbgMode[nRptType] = fMode; return oldMode; } //*** _ClRtlSetReportMode() /*** *int _ClRtlSetReportFile - set the reporting file for a given report type * *Purpose: * set the reporting file for a given report type * *Entry: * int nRptType - the report type * _HFILE hFile - new file for given report type * *Exit: * previous file for given report type * *Exceptions: * *******************************************************************************/ extern "C" _HFILE __cdecl _ClRtlSetReportFile( int nRptType, _HFILE hFile ) { _HFILE oldFile; if (nRptType < 0 || nRptType >= _CLRTLDBG_ERRCNT) return _CLRTLDBG_HFILE_ERROR; if (hFile == _CLRTLDBG_REPORT_FILE) return _ClRtlDbgFile[nRptType]; oldFile = _ClRtlDbgFile[nRptType]; if (_CLRTLDBG_FILE_STDOUT == hFile) _ClRtlDbgFile[nRptType] = GetStdHandle(STD_OUTPUT_HANDLE); else if (_CLRTLDBG_FILE_STDERR == hFile) _ClRtlDbgFile[nRptType] = GetStdHandle(STD_ERROR_HANDLE); else _ClRtlDbgFile[nRptType] = hFile; return oldFile; } //*** _ClRtlSetReportFile() /*** *_CLRTL_REPORT_HOOK _ClRtlSetReportHook() - set client report hook * *Purpose: * set client report hook * *Entry: * _CLRTL_REPORT_HOOK pfnNewHook - new report hook * *Exit: * return previous hook * *Exceptions: * *******************************************************************************/ extern "C" _CLRTL_REPORT_HOOK __cdecl _ClRtlSetReportHook( _CLRTL_REPORT_HOOK pfnNewHook ) { _CLRTL_REPORT_HOOK pfnOldHook = _pfnReportHook; _pfnReportHook = pfnNewHook; return pfnOldHook; } //*** _ClRtlSetReportHook() #define MAXLINELEN 64 #define MAX_MSG 4096 #define TOOLONGMSG "_ClRtlDbgReport: String too long or IO Error" /*** *int _ClRtlDbgReport() - primary reporting function * *Purpose: * Display a message window with the following format. * * ================= Microsft Visual C++ Debug Library ================ * * {Warning! | Error! | Assertion Failed!} * * Program: c:\test\mytest\foo.exe * [Module: c:\test\mytest\bar.dll] * [File: c:\test\mytest\bar.c] * [Line: 69] * * { | Expression: } * * [For information on how your program can cause an assertion * failure, see the Visual C++ documentation on asserts] * * (Press Retry to debug the application) * * =================================================================== * *Entry: * int nRptType - report type * const char * szFile - file name * int nLine - line number * const char * szModule - module name * const char * szFormat - format string * ... - var args * *Exit: * if (MessageBox) * { * Abort -> aborts * Retry -> return TRUE * Ignore-> return FALSE * } * else * return FALSE * *Exceptions: * *******************************************************************************/ extern "C" int __cdecl _ClRtlDbgReport( int nRptType, const char * szFile, int nLine, const char * szModule, const char * szFormat, ... ) { int retval; va_list arglist; char szLineMessage[MAX_MSG] = {0}; char szOutMessage[MAX_MSG] = {0}; char szUserMessage[MAX_MSG] = {0}; #define ASSERTINTRO1 "Assertion failed: " #define ASSERTINTRO2 "Assertion failed!" va_start(arglist, szFormat); if (nRptType < 0 || nRptType >= _CLRTLDBG_ERRCNT) return -1; /* * handle the (hopefully rare) case of * * 1) ASSERT while already dealing with an ASSERT * or * 2) two threads asserting at the same time */ if (_CLRTLDBG_ASSERT == nRptType && _ClRtlInterlockedIncrement(&_clrtlAssertBusy) > 0) { /* use only 'safe' functions -- must not assert in here! */ static int (APIENTRY *pfnwsprintfA)(LPSTR, LPCSTR, ...) = NULL; if (NULL == pfnwsprintfA) { HINSTANCE hlib = LoadLibraryA("user32.dll"); if (NULL == hlib || NULL == (pfnwsprintfA = (int (APIENTRY *)(LPSTR, LPCSTR, ...)) GetProcAddress(hlib, "wsprintfA"))) return -1; } (*pfnwsprintfA)( szOutMessage, "Second Chance Assertion Failed: File %s, Line %d\n", szFile, nLine); OutputDebugStringA(szOutMessage); _ClRtlInterlockedDecrement(&_clrtlAssertBusy); _ClRtlDbgBreak(); return -1; } if (szFormat && _vsnprintf(szUserMessage, MAX_MSG-max(sizeof(ASSERTINTRO1),sizeof(ASSERTINTRO2)), szFormat, arglist) < 0) strcpy(szUserMessage, TOOLONGMSG); if (_CLRTLDBG_ASSERT == nRptType) strcpy(szLineMessage, szFormat ? ASSERTINTRO1 : ASSERTINTRO2); strcat(szLineMessage, szUserMessage); if (_CLRTLDBG_ASSERT == nRptType) { if (_ClRtlDbgMode[nRptType] & _CLRTLDBG_MODE_FILE) strcat(szLineMessage, "\r"); strcat(szLineMessage, "\n"); } if (szFile) { if (_snprintf(szOutMessage, MAX_MSG, "%s(%d) : %s", szFile, nLine, szLineMessage) < 0) strcpy(szOutMessage, TOOLONGMSG); } else strcpy(szOutMessage, szLineMessage); /* user hook may handle report */ if (_pfnReportHook && (*_pfnReportHook)(nRptType, szOutMessage, &retval)) { if (_CLRTLDBG_ASSERT == nRptType) _ClRtlInterlockedDecrement(&_clrtlAssertBusy); return retval; } if (_ClRtlDbgMode[nRptType] & _CLRTLDBG_MODE_FILE) { if (_ClRtlDbgFile[nRptType] != _CLRTLDBG_INVALID_HFILE) { DWORD written; WriteFile(_ClRtlDbgFile[nRptType], szOutMessage, strlen(szOutMessage), &written, NULL); } } if (_ClRtlDbgMode[nRptType] & _CLRTLDBG_MODE_DEBUG) { OutputDebugStringA(szOutMessage); } if (_ClRtlDbgMode[nRptType] & _CLRTLDBG_MODE_WNDW) { char szLine[20]; retval = ClRtlMessageWindow(nRptType, szFile, nLine ? _itoa(nLine, szLine, 10) : NULL, szModule, szUserMessage); if (_CLRTLDBG_ASSERT == nRptType) _ClRtlInterlockedDecrement(&_clrtlAssertBusy); return retval; } if (_CLRTLDBG_ASSERT == nRptType) _ClRtlInterlockedDecrement(&_clrtlAssertBusy); /* ignore */ return FALSE; } //*** _ClRtlDbgReport() /*** *static int ClRtlMessageWindow() - report to a message window * *Purpose: * put report into message window, allow user to choose action to take * *Entry: * int nRptType - report type * const char * szFile - file name * const char * szLine - line number * const char * szModule - module name * const char * szUserMessage - user message * *Exit: * if (MessageBox) * { * Abort -> aborts * Retry -> return TRUE * Ignore-> return FALSE * } * else * return FALSE * *Exceptions: * *******************************************************************************/ static int ClRtlMessageWindow( int nRptType, const char * szFile, const char * szLine, const char * szModule, const char * szUserMessage ) { int nCode; char *szShortProgName; char *szShortModuleName; char szExeName[MAX_PATH]; char szOutMessage[MAX_MSG]; _CLRTL_ASSERTE(szUserMessage != NULL); /* Shorten program name */ if (!GetModuleFileNameA(NULL, szExeName, MAX_PATH)) strcpy(szExeName, ""); szShortProgName = szExeName; if (strlen(szShortProgName) > MAXLINELEN) { szShortProgName += strlen(szShortProgName) - MAXLINELEN; strncpy(szShortProgName, "...", 3); } /* Shorten module name */ szShortModuleName = (char *) szModule; if (szShortModuleName && strlen(szShortModuleName) > MAXLINELEN) { szShortModuleName += strlen(szShortModuleName) - MAXLINELEN; strncpy(szShortModuleName, "...", 3); } if (_snprintf(szOutMessage, MAX_MSG, "Debug %s!\n\nProgram: %s%s%s%s%s%s%s%s%s%s%s" "\n\n(Press Retry to debug the application)", _ClRtlDbgModeMsg[nRptType], szShortProgName, szShortModuleName ? "\nModule: " : "", szShortModuleName ? szShortModuleName : "", szFile ? "\nFile: " : "", szFile ? szFile : "", szLine ? "\nLine: " : "", szLine ? szLine : "", szUserMessage[0] ? "\n\n" : "", szUserMessage[0] && _CLRTLDBG_ASSERT == nRptType ? "Expression: " : "", szUserMessage[0] ? szUserMessage : "", 0 /*_CLRTLDBG_ASSERT == nRptType*/ ? // Don't display this text, it's superfluous "\n\nFor information on how your program can cause an assertion" "\nfailure, see the Visual C++ documentation on asserts." : "") < 0) strcpy(szOutMessage, TOOLONGMSG); /* Report the warning/error */ nCode = __clrtlMessageBoxA( szOutMessage, "Microsoft Visual C++ Debug Library", MB_TASKMODAL|MB_ICONHAND|MB_ABORTRETRYIGNORE|MB_SETFOREGROUND); /* Abort: abort the program */ if (IDABORT == nCode) { /* raise abort signal */ raise(SIGABRT); /* We usually won't get here, but it's possible that SIGABRT was ignored. So exit the program anyway. */ _exit(3); } /* Retry: return 1 to call the debugger */ if (IDRETRY == nCode) return 1; /* Ignore: continue execution */ return 0; } //*** ClRtlMessageWindow() /*** *__clrtlMessageBoxA - call MessageBoxA dynamically. * *Purpose: * Avoid static link with user32.dll. Only load it when actually needed. * *Entry: * see MessageBoxA docs. * *Exit: * see MessageBoxA docs. * *Exceptions: * *******************************************************************************/ static int __clrtlMessageBoxA( LPCSTR lpText, LPCSTR lpCaption, UINT uType ) { static int (APIENTRY *pfnMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT) = NULL; static HWND (APIENTRY *pfnGetActiveWindow)(void) = NULL; static HWND (APIENTRY *pfnGetLastActivePopup)(HWND) = NULL; HWND hWndParent = NULL; if (NULL == pfnMessageBoxA) { HINSTANCE hlib = LoadLibraryA("user32.dll"); if (NULL == hlib || NULL == (pfnMessageBoxA = (int (APIENTRY *)(HWND, LPCSTR, LPCSTR, UINT)) GetProcAddress(hlib, "MessageBoxA"))) return 0; pfnGetActiveWindow = (HWND (APIENTRY *)(void)) GetProcAddress(hlib, "GetActiveWindow"); pfnGetLastActivePopup = (HWND (APIENTRY *)(HWND)) GetProcAddress(hlib, "GetLastActivePopup"); } if (pfnGetActiveWindow) hWndParent = (*pfnGetActiveWindow)(); if (hWndParent != NULL && pfnGetLastActivePopup) hWndParent = (*pfnGetLastActivePopup)(hWndParent); return (*pfnMessageBoxA)(hWndParent, lpText, lpCaption, uType); } //*** __clrtlMessageBoxA()