text: /*++
  
  Copyright (c) 1993-2002  Microsoft Corporation
  
  Module Name:
  
      notify.cpp
  
  Abstract:
      This file implements the functions that make use of the common
      file open dialogs for browsing for files/directories.
  
  Author:
  
      Wesley Witt (wesw) 1-May-1993
  
  Environment:
  
      User Mode
  
  --*/
  
  #include "pch.cpp"
  
  
  //
  // defines
  //
  #define DEFAULT_WAIT_TIME   (1000 * 60 * 5)     // wait for 5 minutes
  #define MAX_PRINTF_BUF_SIZE (1024 * 4)
  
  HANDLE         hThreadDebug = 0;
  PDEBUGPACKET   dp;
  
  
  INT_PTR
  CALLBACK
  NotifyDialogProc (
      HWND hwnd,
      UINT message,
      WPARAM wParam,
      LPARAM lParam
      );
  
  INT_PTR
  CALLBACK
  UsageDialogProc (
      HWND hDlg,
      UINT message,
      WPARAM wParam,
      LPARAM lParam
      );
  
  
  void
  NotifyWinMain (
      void
      )
  
  /*++
  
  Routine Description:
  
      This is the entry point for DRWTSN32
  
  Arguments:
  
      None.
  
  Return Value:
  
      None.
  
  --*/
  
  {
      MSG            msg;
      DWORD          dwThreadId;
      HINSTANCE      hInst;
  
  
      dp = (PDEBUGPACKET) calloc( sizeof(DEBUGPACKET), sizeof(BYTE) );
      if ( dp == NULL) {
          return;
      }
      GetCommandLineArgs( &dp->dwPidToDebug, &dp->hEventToSignal );
  
      RegInitialize( &dp->options );
  
      if (dp->options.fVisual) {
          WNDCLASS wndclass;
          
          hInst                   = GetModuleHandle( NULL );
          wndclass.style          = CS_HREDRAW | CS_VREDRAW;
          wndclass.lpfnWndProc    = (WNDPROC)NotifyDialogProc;
          wndclass.cbClsExtra     = 0;
          wndclass.cbWndExtra     = DLGWINDOWEXTRA;
          wndclass.hInstance      = hInst;
          wndclass.hIcon          = LoadIcon( hInst, MAKEINTRESOURCE(APPICON) );
          wndclass.hCursor        = LoadCursor( NULL, IDC_ARROW );
          wndclass.hbrBackground  = (HBRUSH) (COLOR_3DFACE + 1);
          wndclass.lpszMenuName   = NULL;
          wndclass.lpszClassName  = _T("NotifyDialog");
          RegisterClass( &wndclass );
  
          dp->hwnd = CreateDialog( hInst,
                                   MAKEINTRESOURCE( NOTIFYDIALOG ),
                                   0,
                                   NotifyDialogProc );
  	if (dp->hwnd == NULL) {
  	    return;
  	}
      }
  
      hThreadDebug = CreateThread( NULL,
                              16000,
                              (LPTHREAD_START_ROUTINE)DispatchDebugEventThread,
                              dp,
                              0,
                              &dwThreadId
                            );
  
      if (hThreadDebug == NULL) {
  	return;
      }
  
      if (dp->options.fSound) {
          if ((waveOutGetNumDevs() == 0) || (!_tcslen(dp->options.szWaveFile))) {
              MessageBeep( MB_ICONHAND );
              MessageBeep( MB_ICONHAND );
          }
          else {
              PlaySound( dp->options.szWaveFile, NULL, SND_FILENAME );
          }
      }
  
      if (dp->options.fVisual) {
          ShowWindow( dp->hwnd, SW_SHOWNORMAL );
          while (GetMessage (&msg, NULL, 0, 0)) {
              if (!IsDialogMessage( dp->hwnd, &msg )) {
                  TranslateMessage (&msg) ;
                  DispatchMessage (&msg) ;
              }
          }
      }
      else {
          WaitForSingleObject( hThreadDebug, INFINITE );
      }
  
      CloseHandle( hThreadDebug );
  
      return;
  }
  
  INT_PTR
  CALLBACK
  NotifyDialogProc (
      HWND hwnd,
      UINT message,
      WPARAM wParam,
      LPARAM lParam
      )
  
  /*++
  
  Routine Description:
  
      Window procedure for the DRWTSN32.EXE popup.  This is the popup
      that is displayed when an application error occurs.
  
  Arguments:
  
      hwnd       - window handle to the dialog box
  
      message    - message number
  
      wParam     - first message parameter
  
      lParam     - second message parameter
  
  Return Value:
  
      TRUE       - did not process the message
      FALSE      - did process the message
  
  --*/
  
  {
      DWORD          dwThreadId;
      DWORD          dwSize;
      HANDLE         hThread;
      _TCHAR         szTaskName[MAX_PATH];
  
      static DWORD   AttachComplete=FALSE;
      static DWORD   Cancel=FALSE;
  
      _TCHAR         buf[MAX_PRINTF_BUF_SIZE];
  
  
      switch (message) {
          case WM_CREATE:
              return FALSE;
  
          case WM_INITDIALOG:
  
              SubclassControls( hwnd );
  
              //
              // OK is hidden until the debugger thread finishes
              //
              ShowWindow( GetDlgItem( hwnd, IDOK ), SW_HIDE );
  
              //
              // CANCEL is enabled right away
              //
              EnableWindow( GetDlgItem( hwnd, IDCANCEL ), TRUE );
  
              //
              //  make sure that the user can see the dialog box
              //
              SetForegroundWindow( hwnd );
  
              //
              // get the task name and display it on the dialog box
              //
              dwSize = sizeof(szTaskName) / sizeof(_TCHAR);
              GetTaskName( dp->dwPidToDebug, szTaskName, &dwSize );
  
              //
              // prevent recursion in the case where drwatson faults
              //
              if (_tcsicmp(szTaskName, _T("drwtsn32")) == 0) {
                  ExitProcess(0);
              }
  
  
              //
              // Add the text in the dialog box
              //
              memset(buf,0,sizeof(buf));
              GetNotifyBuf( buf, MAX_PRINTF_BUF_SIZE, MSG_NOTIFY, szTaskName );
              SetDlgItemText( hwnd, ID_TEXT1, buf);
  
              memset(buf,0,sizeof(buf));
              GetNotifyBuf( buf, MAX_PRINTF_BUF_SIZE, MSG_NOTIFY2 );
              SetDlgItemText( hwnd, ID_TEXT2, buf );
  
              return TRUE;
  
          case WM_ACTIVATEAPP:
          case WM_SETFOCUS:
              SetFocusToCurrentControl();
              return 0;
  
          case WM_TIMER:
              SendMessage( hwnd, WM_COMMAND, IDOK, 0 );
              return 0;
  
          case WM_COMMAND:
              switch (wParam) {
                  case IDOK:
                      SendMessage( hwnd, WM_DESTROY, 0, 0 );
                      break;
  
                  case IDCANCEL:
                      Cancel = TRUE;
  
                      // Make the window go away, but don't kill the
                      // the process until the WM_ATTACHCOMPLETE has
                      // occurred
                      ShowWindow( hwnd, SW_HIDE );
                      SendMessage( hwnd, WM_FINISH, 0, 0 );
                      
  		    // Delete the dump file, since its invalid anyway
  		    DeleteCrashDump();
  		    break;
              }
              break;
  
          case WM_NEXTDLGCTL:
              DefDlgProc( hwnd, message, wParam, lParam );
              return 0;
  
          case WM_DUMPCOMPLETE:
  
              //
              // the message is received from the debugger thread
              // when the postmortem dump is finished.  all we need to do
              // is enable the OK button and wait for the user to press the
              // OK button or for the timer to expire.  in either case
              // DrWatson will terminate.
              //
  
              // Disable and hide the Cancel button
              EnableWindow( GetDlgItem( hwnd, IDCANCEL ), FALSE);
              ShowWindow( GetDlgItem(hwnd, IDCANCEL ), SW_HIDE);
  
              // Show and Enable the OK button
              ShowWindow( GetDlgItem( hwnd, IDOK ), SW_SHOW);
              EnableWindow( GetDlgItem( hwnd, IDOK ), TRUE );
              SetFocus( GetDlgItem(hwnd, IDOK) );
              SetFocusToCurrentControl();
  
              SetTimer( hwnd, 1, DEFAULT_WAIT_TIME, NULL );
              return 0;
  
          case WM_ATTACHCOMPLETE:
              //
              // the message is received from the debugger thread when
              // the debugactiveprocess() is completed.
              //
  
              AttachComplete = TRUE;
              SendMessage( hwnd, WM_FINISH, 0, 0 );
              return 0;
  
          case WM_FINISH:
              if (AttachComplete && Cancel) {
  
                  //
                  // terminate the debugger thread
                  //
                  if ( hThreadDebug ) TerminateThread( hThreadDebug, 0 );
  
                  //
                  // create a thread to terminate the debuggee
                  // this is necessary if cancel is pressed before the
                  // debugger thread finishes the postmortem dump
                  //
                  hThread = CreateThread( NULL,
                            16000,
                            (LPTHREAD_START_ROUTINE)TerminationThread,
                            dp,
                            0,
                            &dwThreadId
                          );
  
                  //
                  // wait for the termination thread to kill the debuggee
                  //
                  WaitForSingleObject( hThread, 30000 );
  
                  CloseHandle( hThread );
  
                  //
                  // now post a quit message so that DrWatson will go away
                  //
                  SendMessage( hwnd, WM_DESTROY, 0, 0 );
              }
              return 0;
  
          case WM_EXCEPTIONINFO:
  
              return 0;
  
          case WM_DESTROY:
              KillTimer( hwnd, 1 );
              PostQuitMessage( 0 );
              return 0;
      }
  
      return DefWindowProc( hwnd, message, wParam, lParam );
  }
  
  BOOLEAN
  GetCommandLineArgs(
      LPDWORD dwPidToDebug,
      LPHANDLE hEventToSignal
      )
  
  /*++
  
  Routine Description:
  
      Parses the command line for the 3 possible command lines
      arguments:
  
           -p %ld        process id
           -e %ld        event id
           -g            go
  
  Arguments:
  
      dwPidToDebug - Returns the process id of the process to debug
  
      hEventToSignal - Returns the handle to an event which will be signalled
          when the attach is complete.
  
  Return Value:
  
      None.
  
  --*/
  
  {
      _TCHAR      *lpstrCmd = GetCommandLine();
      _TUCHAR       ch;
      _TCHAR      buf[4096];
      BOOLEAN     rval = FALSE;
      BOOLEAN     ParsedEvent = FALSE;
      BOOLEAN     ParsedPid = FALSE;
  
      // skip over program name
      do {
          ch = *lpstrCmd++;
      }
      while (ch != _T(' ') && ch != _T('\t') && ch != _T('\0'));
  
      //  skip over any following white space
      while (ch == _T(' ') || ch == _T('\t')) {
          ch = *lpstrCmd++;
      }
  
      //  process each switch character _T('-') as encountered
  
      while (ch == _T('-')) {
          ch = *lpstrCmd++;
          //  process multiple switch characters as needed
          do {
              switch (ch) {
                  case _T('e'):
                  case _T('E'):
                      // event to signal takes decimal argument
                      // skip whitespace
                      do {
                          ch = *lpstrCmd++;
                      }
                      while (ch == _T(' ') || ch == _T('\t'));
                      while (ch >= _T('0') && ch <= _T('9')) {
                          if (!ParsedEvent) {
                              *hEventToSignal = (HANDLE)
                                  ((DWORD_PTR)*hEventToSignal * 10 + ch - _T('0'));
                          }
                          ch = *lpstrCmd++;
                      }
                      rval = TRUE;
                      ParsedEvent = TRUE;
                      break;
  
                  case _T('p'):
                  case _T('P'):
                      // pid debug takes decimal argument
  
                      do
                          ch = *lpstrCmd++;
                      while (ch == _T(' ') || ch == _T('\t'));
  
                      if ( ch == _T('-') ) {
                          ch = *lpstrCmd++;
                          if ( ch == _T('1') ) {
                              if (!ParsedPid) {
                                  *dwPidToDebug = (DWORD)-1;
                              }
                              ch = *lpstrCmd++;
                          }
                      }
                      else {
                          while (ch >= _T('0') && ch <= _T('9')) {
                              if (!ParsedPid) {
                                  *dwPidToDebug =
                                      *dwPidToDebug * 10 + ch - _T('0');
                              }
                              ch = *lpstrCmd++;
                          }
                      }
                      rval = TRUE;
                      ParsedPid = TRUE;
                      break;
  
                  case _T('g'):
                  case _T('G'):
                      // GO
                      // Ignored but provided for compatiblity with windbg & ntsd
                      ch = *lpstrCmd++;
                      break;
  
                  case _T('?'):
                      DialogBox( GetModuleHandle(NULL),
                                 MAKEINTRESOURCE(USAGEDIALOG),
                                 NULL,
                                 UsageDialogProc
                               );
                      rval = TRUE;
                      ch = *lpstrCmd++;
                      break;
  
                  case _T('i'):
                  case _T('I'):
                      FormatMessage(
                        FORMAT_MESSAGE_FROM_HMODULE,
                        NULL,
                        MSG_INSTALL_NOTIFY,
                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                        buf,
                        sizeof(buf) / sizeof(_TCHAR),
                        NULL
                        );
                      RegInstallDrWatson( tolower(lpstrCmd[0]) == _T('q') );
                      MessageBox( NULL,
                                  buf,
                                  _T("Dr. Watson"),
                                  MB_ICONINFORMATION | MB_OK |
                                  MB_SETFOREGROUND );
                      rval = TRUE;
                      ch = *lpstrCmd++;
                      break;
  
                  default:
                      return rval;
              }
          } while (ch != _T(' ') && ch != _T('\t') && ch != _T('\0'));
  
          while (ch == _T(' ') || ch == _T('\t')) {
              ch = *lpstrCmd++;
          }
      }
      return rval;
  }
  
  INT_PTR
  CALLBACK
  UsageDialogProc (
      HWND hDlg,
      UINT message,
      WPARAM wParam,
      LPARAM lParam
      )
  
  /*++
  
  Routine Description:
  
      This is the dialog procedure for the assert dialog box.  Normally
      an assertion box is simply a message box but in this case a Help
      button is desired so a dialog box is used.
  
  Arguments:
  
      hDlg       - window handle to the dialog box
  
      message    - message number
  
      wParam     - first message parameter
  
      lParam     - second message parameter
  
  Return Value:
  
      TRUE       - did not process the message
      FALSE      - did process the message
  
  --*/
  
  {
      _TCHAR        buf[4096];
  
      switch (message) {
          case WM_INITDIALOG:
              FormatMessage(
                FORMAT_MESSAGE_FROM_HMODULE,
                NULL,
                MSG_USAGE,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                buf,
                sizeof(buf) / sizeof(_TCHAR),
                NULL
                );
              SetDlgItemText( hDlg, ID_USAGE, buf );
              break;
  
          case WM_COMMAND:
              switch (wParam) {
                  case IDOK:
                      EndDialog( hDlg, 0 );
                      break;
              }
              break;
      }
  
      return FALSE;
  }
  
  
  void
  __cdecl
  GetNotifyBuf(
      LPTSTR buf,
      DWORD bufsize,
      DWORD dwFormatId,
      ...
      )
  
  /*++
  
  Routine Description:
  
      This is function is a printf style function for printing messages
      in a message file.
  
  Arguments:
  
      dwFormatId    - format id in the message file
  
      ...           - var args
  
  Return Value:
  
      None.
  
  --*/
  
  {
      DWORD       dwCount;
      va_list     args;
  
      va_start( args, dwFormatId );
  
      dwCount = FormatMessage(
                  FORMAT_MESSAGE_FROM_HMODULE,
                  NULL,
                  dwFormatId,
                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //Default language
                  buf,
                  bufsize,
                  &args
                  );
  
      va_end(args);
  
      Assert( dwCount != 0 );
  
      return;
  }
  