/*++

Copyright (c) 1993  Microsoft Corporation

Module Name:

    browse.c

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 <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <commdlg.h>
#include <mmsystem.h>
#include <direct.h>

#include "drwatson.h"
#include "proto.h"
#include "resource.h"
#include "messages.h"


//
// defines
//
#define DEFAULT_WAIT_TIME   (1000 * 60 * 5)     // wait for 5 minutes

//
// static global variables
//
static HANDLE         hThreadDebug;
static PDEBUGPACKET   dp;


DWORD TimerKillThread( HWND hwnd );
LRESULT NotifyWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL 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;
    WNDCLASS       wndclass;
    DWORD          dwThreadId;
    HINSTANCE      hInst;


    dp = (PDEBUGPACKET) malloc( sizeof(DEBUGPACKET) );
    memset( dp, 0, sizeof(DEBUGPACKET) );
    GetCommandLineArgs( &dp->dwPidToDebug, &dp->hEventToSignal );

    RegInitialize( &dp->options );

    if (dp->options.fVisual) {
        hInst                   = GetModuleHandle( NULL );
        wndclass.style          = CS_HREDRAW | CS_VREDRAW;
        wndclass.lpfnWndProc    = NotifyWndProc;
        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_WINDOW + 1);
        wndclass.lpszMenuName   = NULL;
        wndclass.lpszClassName  = "NotifyDialog";
        RegisterClass( &wndclass );

        dp->hwnd = CreateDialog( hInst,
                                 MAKEINTRESOURCE( NOTIFYDIALOG ),
                                 0,
                                 NotifyWndProc );
    }

    hThreadDebug = CreateThread( NULL,
                            16000,
                            (LPTHREAD_START_ROUTINE)DispatchDebugEventThread,
                            dp,
                            THREAD_SET_INFORMATION,
                            (LPDWORD)&dwThreadId
                          );

    if (dp->options.fSound) {
        if ((waveOutGetNumDevs() == 0) || (!strlen(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 );
    }

    return;
}

LRESULT
NotifyWndProc (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;
    char           szTaskName[MAX_PATH];
    char           szHelpFileName[MAX_PATH];

    switch (message) {
        case WM_CREATE:
            return FALSE;

        case WM_INITDIALOG:

            SubclassControls( hwnd );

            //
            // OK is not enabled until the debugger thread finishes
            //
            EnableWindow( GetDlgItem( hwnd, IDOK ), FALSE );

            //
            // CANCEL is not enabled until debugactiveprocess is finished
            //
            EnableWindow( GetDlgItem( hwnd, IDCANCEL ), FALSE );

            //
            //  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);
            GetTaskName( dp->dwPidToDebug, szTaskName, &dwSize );

            //
            // prevent recursion in the case where drwatson faults
            //
            if (stricmp(szTaskName,"drwtsn32")==0) {
                ExitProcess(0);
            }

            SetDlgItemText( hwnd, ID_TEXT1, szTaskName);

            //
            // create a thread to terminate DrWatson if the user does
            // not push the OK putton
            //
            CreateThread( NULL,
                          16000,
                          (LPTHREAD_START_ROUTINE)TimerKillThread,
                          (LPVOID)hwnd,
                          THREAD_SET_INFORMATION,
                          (LPDWORD)&dwThreadId
                        );
            return TRUE;

        case WM_ACTIVATEAPP:
        case WM_SETFOCUS:
            SetFocusToCurrentControl();
            return 0;

        case WM_COMMAND:
            switch (wParam) {
                case IDOK:
                    PostQuitMessage( 0 );
                    break;

                case IDCANCEL:
                    //
                    // terminate the debugger thread
                    //
                    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,
                                  THREAD_SET_INFORMATION,
                                  (LPDWORD)&dwThreadId
                                );

                    //
                    // wait for the termination thread to kill the debuggee
                    //
                    WaitForSingleObject( hThread, 30000 );

                    //
                    // now post a quit message so that DrWatson will go away
                    //
                    PostQuitMessage( 0 );
                    break;

                case ID_HELP:
                    //
                    // call winhelp
                    //
                    GetHelpFileName( szHelpFileName, sizeof(szHelpFileName) );
                    WinHelp( hwnd, szHelpFileName, HELP_CONTEXT, IDH_WHAT );
                    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.
            //
            EnableWindow( GetDlgItem( hwnd, IDOK ), TRUE );
            SetFocus( GetDlgItem(hwnd, IDOK) );
            SetFocusToCurrentControl();
            return 0;

        case WM_ATTACHCOMPLETE:

            //
            // the message is received from the debugger thread when
            // the debugactiveprocess() is completed
            //
            EnableWindow( GetDlgItem( hwnd, IDCANCEL ), TRUE );
            return 0;

        case WM_EXCEPTIONINFO:

            SetDlgItemText( hwnd, ID_TEXT2, (char *) lParam);
            return 0;

        case WM_DESTROY:
            PostQuitMessage( 0 );
            return 0;
    }

    return DefWindowProc( hwnd, message, wParam, lParam );
}

DWORD
TimerKillThread( HWND hwnd )

/*++

Routine Description:

    This function executes in its own thread.  The purpose is to wait
    dwMilliseconds and notify the hwndMain window by destroting it.
    This thread is used to terminate DRWTSN32 when the use does not
    respond to the application error popup.

Arguments:

    dwMilliseconds   - amount of time to wait

Return Value:

    Zero.

--*/

{
    //
    // wait as long as i'm told to
    //
    Sleep( DEFAULT_WAIT_TIME );

    //
    // tell the popup that its time to go away
    //
//  SendMessage( hwnd, WM_DESTROY, 0, 0 );

    return 0;
}

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:

    dp             - pointer to a debug packet

Return Value:

    None.

--*/

{
    char        *lpstrCmd = GetCommandLine();
    UCHAR       ch;
    char        buf[4096];
    BOOLEAN     rval = FALSE;

    // skip over program name
    do {
        ch = *lpstrCmd++;
    }
    while (ch != ' ' && ch != '\t' && ch != '\0');

    //  skip over any following white space
    while (ch == ' ' || ch == '\t') {
        ch = *lpstrCmd++;
    }

    //  process each switch character '-' as encountered

    while (ch == '-') {
        ch = *lpstrCmd++;
        //  process multiple switch characters as needed
        do {
            switch (ch) {
                case 'e':
                case 'E':
                    // event to signal takes decimal argument
                    // skip whitespace
                    do {
                        ch = *lpstrCmd++;
                    }
                    while (ch == ' ' || ch == '\t');
                    while (ch >= '0' && ch <= '9') {
                        (DWORD)*hEventToSignal =
                                  (DWORD)*hEventToSignal * 10 + ch - '0';
                        ch = *lpstrCmd++;
                    }
                    rval = TRUE;
                    break;

                case 'p':
                case 'P':
                    // pid debug takes decimal argument

                    do
                        ch = *lpstrCmd++;
                    while (ch == ' ' || ch == '\t');

                    if ( ch == '-' ) {
                        ch = *lpstrCmd++;
                        if ( ch == '1' ) {
                            *dwPidToDebug = 0xffffffff;
                            ch = *lpstrCmd++;
                        }
                    }
                    else {
                        while (ch >= '0' && ch <= '9') {
                            *dwPidToDebug =
                                       *dwPidToDebug * 10 + ch - '0';
                            ch = *lpstrCmd++;
                        }
                    }
                    rval = TRUE;
                    break;

                case 'g':
                case 'G':
                    ch = *lpstrCmd++;
                    break;

                case '?':
                    DialogBox( GetModuleHandle(NULL),
                               MAKEINTRESOURCE(USAGEDIALOG),
                               NULL,
                               UsageDialogProc
                             );
                    rval = TRUE;
                    ch = *lpstrCmd++;
                    break;

                case 'i':
                case 'I':
                    FormatMessage(
                      FORMAT_MESSAGE_FROM_HMODULE,
                      NULL,
                      MSG_INSTALL_NOTIFY,
                      0,
                      buf,
                      sizeof(buf),
                      NULL
                      );
                    RegInstallDrWatson( tolower(lpstrCmd[0]) == 'q' );
                    MessageBox( NULL,
                                buf,
                                "Dr. Watson for Windows NT",
                                MB_ICONINFORMATION | MB_OK |
                                MB_SETFOREGROUND );
                    rval = TRUE;
                    ch = *lpstrCmd++;
                    break;

                default:
                    return rval;
            }
        }
        while (ch != ' ' && ch != '\t' && ch != '\0');

        while (ch == ' ' || ch == '\t') {
            ch = *lpstrCmd++;
        }
    }
    return rval;
}

BOOL 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

--*/

{
    char        buf[4096];

    switch (message) {
        case WM_INITDIALOG:
            FormatMessage(
              FORMAT_MESSAGE_FROM_HMODULE,
              NULL,
              MSG_USAGE,
              0, // GetUserDefaultLangID(),
              buf,
              sizeof(buf),
              NULL
              );
            SetDlgItemText( hDlg, ID_USAGE, buf );
            break;

        case WM_COMMAND:
            switch (wParam) {
                case IDOK:
                    EndDialog( hDlg, 0 );
                    break;
            }
            break;
    }

    return FALSE;
}
