text: /***
  *DbgRpt.c - Debug Cluster Reporting Functions
  *
  *       Copyright (c) 1988-1998, Microsoft Corporation. All rights reserved.
  *
  *Purpose:
  *
  *******************************************************************************/
  
  #include <malloc.h>
  #include <mbstring.h>
  #include <stdarg.h>
  #include <stdlib.h>
  #include <stdio.h>
  #include <signal.h>
  #include <string.h>
  
  #include <windows.h>
  
  #define CLRTL_INCLUDE_DEBUG_REPORTING
  #include "ClRtlDbg.h"
  #include <strsafe.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 512
  #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]
  *
  *       {<warning or error message> | Expression: <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;
      }
  
      szUserMessage[ MAX_MSG - 1 ] = 0;
      if (szFormat && StringCbVPrintfA( szUserMessage,
                     MAX_MSG-max(sizeof(ASSERTINTRO1),sizeof(ASSERTINTRO2))-1,
                     szFormat,
                     arglist) != S_OK ) {
          PCHAR dot;
          dot = szUserMessage + MAX_MSG - 4;
          StringCbCopyA( dot, 4, "..." );
      }
      if (_CLRTLDBG_ASSERT == nRptType) {
          if ( szFormat == 0 ) {
              StringCbCopyA( szLineMessage, MAX_MSG, ASSERTINTRO1 );
          }
          else {
              StringCbCopyA( szLineMessage, MAX_MSG, ASSERTINTRO2 );
          }
      }
      StringCbCatA( szLineMessage, MAX_MSG, szUserMessage );
  
      if (_CLRTLDBG_ASSERT == nRptType)
      {
          if (_ClRtlDbgMode[nRptType] & _CLRTLDBG_MODE_FILE) {
              StringCbCatA( szLineMessage, MAX_MSG, "\r" );
          }
          StringCbCatA( szLineMessage, MAX_MSG, "\n" );
      }
  
      if (szFile)
      {
          if ( StringCbPrintfA(szOutMessage, MAX_MSG, "%s(%d) : %s",
              szFile, nLine, szLineMessage) != S_OK )
          StringCbCopyA( szOutMessage, MAX_MSG, TOOLONGMSG );
      }
      else {
          StringCbCopyA( szOutMessage, MAX_MSG, 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))
          StringCbCopyA(szExeName, MAX_PATH, "<program name unknown>");
  
      szShortProgName = szExeName;
  
      if (strlen(szShortProgName) > MAXLINELEN)
      {
          szShortProgName += MAXLINELEN - 4;
          StringCbCopyA( szShortProgName, 4, "..." );
      }
  
      /* Shorten module name */
      szShortModuleName = (char *) szModule;
  
      if (szShortModuleName && strlen(szShortModuleName) > MAXLINELEN)
      {
          szShortModuleName += MAXLINELEN - 4;
          StringCbCopyA( szShortModuleName, 4, "..." );
      }
  
      if ( StringCbPrintfA( 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."
              : "") != S_OK )
          StringCbCopyA( szOutMessage, MAX_MSG, 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()
  