/*** *dbgrpt.c - Debug CRT Reporting Functions * * Copyright (c) 1988-2001, Microsoft Corporation. All rights reserved. * *Purpose: * *Revision History: * 08-16-94 CFW Module created. * 11-28-94 CFW Change _SetCrtxxx to _CrtSetxxx. * 12-08-94 CFW Use non-win32 names. * 01-05-94 CFW Add report hook. * 01-11-94 CFW Report uses _snprintf, all unsigned chars. * 01-20-94 CFW Change unsigned chars to chars. * 01-24-94 CFW Name cleanup. * 02-09-95 CFW PMac work, _CrtDbgReport now returns 1 for debug, * -1 for error. * 02-15-95 CFW Make all CRT message boxes look alike. * 02-24-95 CFW Use __crtMessageBoxA. * 02-27-95 CFW Move GetActiveWindow/GetLastrActivePopup into * __crtMessageBoxA, add _CrtDbgBreak. * 02-28-95 CFW Fix PMac reporting. * 03-21-95 CFW Add _CRT_ASSERT report type, improve assert windows. * 04-19-95 CFW Avoid double asserts. * 04-25-95 CFW Add _CRTIMP to all exported functions. * 04-30-95 CFW "JIT" message removed. * 05-10-95 CFW Change Interlockedxxx to _CrtInterlockedxxx. * 05-24-95 CFW Change report hook scheme, make _crtAssertBusy available. * 06-06-95 CFW Remove _MB_SERVICE_NOTIFICATION. * 06-08-95 CFW Macos header changes cause warning. * 06-08-95 CFW Add return value parameter to report hook. * 06-27-95 CFW Add win32s support for debug libs. * 07-07-95 CFW Simplify default report mode scheme. * 07-19-95 CFW Use WLM debug string scheme for PMac. * 08-01-95 JWM PMac file output fixed. * 01-08-96 JWM File output now in text mode. * 04-22-96 JWM MAX_MSG increased from 512 to 4096. * 04-29-96 JWM _crtAssertBusy no longer being decremented prematurely. * 01-05-99 GJF Changes for 64-bit size_t. * 05-17-99 PML Remove all Macintosh support. * 03-21-01 PML Add _CrtSetReportHook2 (vs7#124998) * 03-28-01 PML Protect against GetModuleFileName overflow (vs7#231284) * *******************************************************************************/ #ifdef _DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #define _CrtInterlockedIncrement InterlockedIncrement #define _CrtInterlockedDecrement InterlockedDecrement /*--------------------------------------------------------------------------- * * Debug Reporting * --------------------------------------------------------------------------*/ static int CrtMessageWindow( int, const char *, const char *, const char *, const char * ); _CRT_REPORT_HOOK _pfnReportHook; typedef struct ReportHookNode { struct ReportHookNode *prev; struct ReportHookNode *next; unsigned refcount; _CRT_REPORT_HOOK pfnHookFunc; } ReportHookNode; ReportHookNode *_pReportHookList; _CRTIMP long _crtAssertBusy = -1; int _CrtDbgMode[_CRT_ERRCNT] = { _CRTDBG_MODE_DEBUG, _CRTDBG_MODE_WNDW, _CRTDBG_MODE_WNDW }; _HFILE _CrtDbgFile[_CRT_ERRCNT] = { _CRTDBG_INVALID_HFILE, _CRTDBG_INVALID_HFILE, _CRTDBG_INVALID_HFILE }; static const char * _CrtDbgModeMsg[_CRT_ERRCNT] = { "Warning", "Error", "Assertion Failed" }; /*** *void _CrtDebugBreak - call OS-specific debug function * *Purpose: * call OS-specific debug function * *Entry: * *Exit: * *Exceptions: * *******************************************************************************/ #undef _CrtDbgBreak _CRTIMP void _cdecl _CrtDbgBreak( void ) { DebugBreak(); } /*** *int _CrtSetReportMode - 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: * *******************************************************************************/ _CRTIMP int __cdecl _CrtSetReportMode( int nRptType, int fMode ) { int oldMode; if (nRptType < 0 || nRptType >= _CRT_ERRCNT) return -1; if (fMode == _CRTDBG_REPORT_MODE) return _CrtDbgMode[nRptType]; /* verify flags values */ if (fMode & ~(_CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW)) return -1; oldMode = _CrtDbgMode[nRptType]; _CrtDbgMode[nRptType] = fMode; return oldMode; } /*** *int _CrtSetReportFile - 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: * *******************************************************************************/ _CRTIMP _HFILE __cdecl _CrtSetReportFile( int nRptType, _HFILE hFile ) { _HFILE oldFile; if (nRptType < 0 || nRptType >= _CRT_ERRCNT) return _CRTDBG_HFILE_ERROR; if (hFile == _CRTDBG_REPORT_FILE) return _CrtDbgFile[nRptType]; oldFile = _CrtDbgFile[nRptType]; if (_CRTDBG_FILE_STDOUT == hFile) _CrtDbgFile[nRptType] = GetStdHandle(STD_OUTPUT_HANDLE); else if (_CRTDBG_FILE_STDERR == hFile) _CrtDbgFile[nRptType] = GetStdHandle(STD_ERROR_HANDLE); else _CrtDbgFile[nRptType] = hFile; return oldFile; } /*** *_CRT_REPORT_HOOK _CrtSetReportHook() - set client report hook * *Purpose: * set client report hook * *Entry: * _CRT_REPORT_HOOK pfnNewHook - new report hook * *Exit: * return previous hook * *Exceptions: * *******************************************************************************/ _CRTIMP _CRT_REPORT_HOOK __cdecl _CrtSetReportHook( _CRT_REPORT_HOOK pfnNewHook ) { _CRT_REPORT_HOOK pfnOldHook = _pfnReportHook; _pfnReportHook = pfnNewHook; return pfnOldHook; } /*** *_CRT_REPORT_HOOK _CrtSetReportHook2() - configure client report hook in list * *Purpose: * Install or remove a client report hook from the report list. Exists * separately from _CrtSetReportHook because the older function doesn't * work well in an environment where DLLs that are loaded and unloaded * dynamically out of LIFO order want to install report hooks. * *Entry: * int mode - _CRT_RPTHOOK_INSTALL or _CRT_RPTHOOK_REMOVE * _CRT_REPORT_HOOK pfnNewHook - report hook to install/remove/query * *Exit: * Returns -1 if an error was encountered, with EINVAL or ENOMEM set, * else returns the reference count of pfnNewHook after the call. * *Exceptions: * *******************************************************************************/ _CRTIMP int __cdecl _CrtSetReportHook2( int mode, _CRT_REPORT_HOOK pfnNewHook ) { ReportHookNode *p; int ret; /* Handle invalid parameters */ if ((mode != _CRT_RPTHOOK_INSTALL && mode != _CRT_RPTHOOK_REMOVE) || pfnNewHook == NULL) { errno = EINVAL; return -1; } #ifdef _MT if (!_mtinitlocknum(_DEBUG_LOCK)) return -1; _mlock(_DEBUG_LOCK); __try { #endif /* Search for new hook function to see if it's already installed */ for (p = _pReportHookList; p != NULL; p = p->next) if (p->pfnHookFunc == pfnNewHook) break; if (mode == _CRT_RPTHOOK_REMOVE) { /* Remove request - free list node if refcount goes to zero */ if (p != NULL) { if ((ret = --p->refcount) == 0) { if (p->next) p->next->prev = p->prev; if (p->prev) p->prev->next = p->next; else _pReportHookList = p->next; _free_crt(p); } } else { ret = -1; errno = EINVAL; } } else { /* Insert request */ if (p != NULL) { /* Hook function already registered, move to head of list */ ret = ++p->refcount; if (p != _pReportHookList) { if (p->next) p->next->prev = p->prev; p->prev->next = p->next; p->prev = NULL; p->next = _pReportHookList; _pReportHookList->prev = p; _pReportHookList = p; } } else { /* Hook function not already registered, insert new node */ p = (ReportHookNode *)_malloc_crt(sizeof(ReportHookNode)); if (p == NULL) { ret = -1; errno = ENOMEM; } else { p->prev = NULL; p->next = _pReportHookList; if (_pReportHookList) _pReportHookList->prev = p; ret = p->refcount = 1; p->pfnHookFunc = pfnNewHook; _pReportHookList = p; } } } #ifdef _MT } __finally { _munlock(_DEBUG_LOCK); } #endif return ret; } #define MAXLINELEN 64 #define MAX_MSG 4096 #define TOOLONGMSG "_CrtDbgReport: String too long or IO Error" /*** *int _CrtDbgReport() - 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: * *******************************************************************************/ _CRTIMP int __cdecl _CrtDbgReport( 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 >= _CRT_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 (_CRT_ASSERT == nRptType && _CrtInterlockedIncrement(&_crtAssertBusy) > 0) { /* use only 'safe' functions -- must not assert in here! */ static int (APIENTRY *pfnwsprintfA)(LPSTR, LPCSTR, ...) = NULL; if (NULL == pfnwsprintfA) { HANDLE hlib = LoadLibrary("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); OutputDebugString(szOutMessage); _CrtInterlockedDecrement(&_crtAssertBusy); _CrtDbgBreak(); return -1; } if (szFormat && _vsnprintf(szUserMessage, MAX_MSG-max(sizeof(ASSERTINTRO1),sizeof(ASSERTINTRO2)), szFormat, arglist) < 0) strcpy(szUserMessage, TOOLONGMSG); if (_CRT_ASSERT == nRptType) strcpy(szLineMessage, szFormat ? ASSERTINTRO1 : ASSERTINTRO2); strcat(szLineMessage, szUserMessage); if (_CRT_ASSERT == nRptType) { if (_CrtDbgMode[nRptType] & _CRTDBG_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. Check Hook2 list first */ if (_pReportHookList) { ReportHookNode *pnode; #ifdef _MT _mlock(_DEBUG_LOCK); __try { #endif for (pnode = _pReportHookList; pnode; pnode = pnode->next) { if ((*pnode->pfnHookFunc)(nRptType, szOutMessage, &retval)) { if (_CRT_ASSERT == nRptType) _CrtInterlockedDecrement(&_crtAssertBusy); return retval; } } #ifdef _MT } __finally { _munlock(_DEBUG_LOCK); } #endif } if (_pfnReportHook) { if ((*_pfnReportHook)(nRptType, szOutMessage, &retval)) { if (_CRT_ASSERT == nRptType) _CrtInterlockedDecrement(&_crtAssertBusy); return retval; } } if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE) { if (_CrtDbgFile[nRptType] != _CRTDBG_INVALID_HFILE) { DWORD written; WriteFile(_CrtDbgFile[nRptType], szOutMessage, (unsigned long)strlen(szOutMessage), &written, NULL); } } if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_DEBUG) { OutputDebugString(szOutMessage); } if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_WNDW) { char szLine[20]; retval = CrtMessageWindow(nRptType, szFile, nLine ? _itoa(nLine, szLine, 10) : NULL, szModule, szUserMessage); if (_CRT_ASSERT == nRptType) _CrtInterlockedDecrement(&_crtAssertBusy); return retval; } if (_CRT_ASSERT == nRptType) _CrtInterlockedDecrement(&_crtAssertBusy); /* ignore */ return FALSE; } /*** *static int CrtMessageWindow() - 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 CrtMessageWindow( int nRptType, const char * szFile, const char * szLine, const char * szModule, const char * szUserMessage ) { int nCode; char *szShortProgName; char *szShortModuleName; char szExeName[MAX_PATH + 1]; char szOutMessage[MAX_MSG]; _ASSERTE(szUserMessage != NULL); /* Shorten program name */ szExeName[MAX_PATH] = '\0'; if (!GetModuleFileName(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)", _CrtDbgModeMsg[nRptType], szShortProgName, szShortModuleName ? "\nModule: " : "", szShortModuleName ? szShortModuleName : "", szFile ? "\nFile: " : "", szFile ? szFile : "", szLine ? "\nLine: " : "", szLine ? szLine : "", szUserMessage[0] ? "\n\n" : "", szUserMessage[0] && _CRT_ASSERT == nRptType ? "Expression: " : "", szUserMessage[0] ? szUserMessage : "", _CRT_ASSERT == nRptType ? "\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 = __crtMessageBoxA(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; } #endif /* _DEBUG */