/*++

Copyright (c) 1998-2000  Microsoft Corporation

Module Name:
    tracer.c

Abstract:
    This module contains the code for debug tracing of a windows app.

Author:
    Michael Tsang (MikeTs) 02-May-2000

Environment:
    User mode

Revision History:

--*/

#include "pch.h"

//
// Global Data
//
HANDLE     ghServerThread = NULL;
HINSTANCE  ghInstance = 0;
PSZ        gpszWinTraceClass = "WinTrace_Class";
HWND       ghwndTracer = 0;
HWND       ghwndEdit = 0;
HWND       ghwndPropSheet = 0;
HFONT      ghFont = 0;
HCURSOR    ghStdCursor = 0;
HCURSOR    ghWaitCursor = 0;
DWORD      gdwfTracer = 0;
LIST_ENTRY glistClients = {0};
char       gszApp[16] = {0};
char       gszSearchText[128] = {0}; //BUGBUG
char       gszFileName[MAX_PATH + 1] = {0};
char       gszSaveFilterSpec[80] = {0};
int        giPointSize = 120;
LOGFONT    gLogFont = {0};
SETTINGS   gDefGlobalSettings = {0, 0, 0};
const int  gTrigPtCtrlMap[NUM_TRIGPTS] =
           {
                IDC_TRIGPT1, IDC_TRIGPT2, IDC_TRIGPT3, IDC_TRIGPT4, IDC_TRIGPT5,
                IDC_TRIGPT6, IDC_TRIGPT7, IDC_TRIGPT8, IDC_TRIGPT9, IDC_TRIGPT10
           };
const int  gTrigPtTraceMap[NUM_TRIGPTS] =
           {
                IDC_TRIGPT1_TRACE, IDC_TRIGPT2_TRACE, IDC_TRIGPT3_TRACE,
                IDC_TRIGPT4_TRACE, IDC_TRIGPT5_TRACE, IDC_TRIGPT6_TRACE,
                IDC_TRIGPT7_TRACE, IDC_TRIGPT8_TRACE, IDC_TRIGPT9_TRACE,
                IDC_TRIGPT10_TRACE
           };
const int  gTrigPtBreakMap[NUM_TRIGPTS] =
           {
                IDC_TRIGPT1_BREAK, IDC_TRIGPT2_BREAK, IDC_TRIGPT3_BREAK,
                IDC_TRIGPT4_BREAK, IDC_TRIGPT5_BREAK, IDC_TRIGPT6_BREAK,
                IDC_TRIGPT7_BREAK, IDC_TRIGPT8_BREAK, IDC_TRIGPT9_BREAK,
                IDC_TRIGPT10_BREAK
           };
const int  gTrigPtTextMap[NUM_TRIGPTS] =
           {
                IDC_TRIGPT1_TEXT, IDC_TRIGPT2_TEXT, IDC_TRIGPT3_TEXT,
                IDC_TRIGPT4_TEXT, IDC_TRIGPT5_TEXT, IDC_TRIGPT6_TEXT,
                IDC_TRIGPT7_TEXT, IDC_TRIGPT8_TEXT, IDC_TRIGPT9_TEXT,
                IDC_TRIGPT10_TEXT
           };
const int  gTrigPtTraceTextMap[NUM_TRIGPTS] =
           {
                IDC_TRIGPT1_TRACE_TEXT, IDC_TRIGPT2_TRACE_TEXT,
                IDC_TRIGPT3_TRACE_TEXT, IDC_TRIGPT4_TRACE_TEXT,
                IDC_TRIGPT5_TRACE_TEXT, IDC_TRIGPT6_TRACE_TEXT,
                IDC_TRIGPT7_TRACE_TEXT, IDC_TRIGPT8_TRACE_TEXT,
                IDC_TRIGPT9_TRACE_TEXT, IDC_TRIGPT10_TRACE_TEXT
           };
const int  gTrigPtBreakTextMap[NUM_TRIGPTS] =
           {
                IDC_TRIGPT1_BREAK_TEXT, IDC_TRIGPT2_BREAK_TEXT,
                IDC_TRIGPT3_BREAK_TEXT, IDC_TRIGPT4_BREAK_TEXT,
                IDC_TRIGPT5_BREAK_TEXT, IDC_TRIGPT6_BREAK_TEXT,
                IDC_TRIGPT7_BREAK_TEXT, IDC_TRIGPT8_BREAK_TEXT,
                IDC_TRIGPT9_BREAK_TEXT, IDC_TRIGPT10_BREAK_TEXT
           };

/*++
    @doc    EXTERNAL

    @func   int | WinMain | Program entry point.

    @parm   IN HINSTANCE | hInstance | Instance handle.
    @parm   IN HINSTANCE | hPrevInstance | Handle of previous instance.
    @parm   IN LPSTR | pszCmdLine | Points to the command line string.
    @parm   IN int | nCmdShow | Show state.

    @rvalue Always returns 0.
--*/

int WINAPI
WinMain(
    IN HINSTANCE hInstance,
    IN HINSTANCE hPrevInstance,
    IN LPSTR     pszCmdLine,
    IN int       nCmdShow
    )
{
    TRTRACEPROC("WinMain", 1)
    int rc = -1;

    TRENTER(("(hInstance=%x,hPrevInstance=%x,CmdLine=%s,CmdShow=%x)\n",
             hInstance, hPrevInstance, pszCmdLine, nCmdShow));

    if (TracerInit(hInstance, nCmdShow))
    {
        MSG msg;

        //
        // Message pump.
        //
        while (GetMessage(&msg, NULL, 0, 0))
        {
//            if ((ghDlgSettings == 0) || !IsDialogMessage(ghDlgSettings, &msg))
            {
//                if (TranslateAccelerator(ghwndTracer, )
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
        }

        rc = (int)msg.wParam;
    }

    TREXIT(("=%x\n", rc));
    return rc;
}       //WinMain

/*++
    @doc    INTERNAL

    @func   BOOL | TracerInit | Initialize tracer.

    @parm   IN HINSTANCE | hInstance | Instance handle.
    @parm   IN int | nCmdShow | Show state.

    @rvalue Always returns 0.
--*/

BOOL
TracerInit(
    IN HINSTANCE hInstance,
    IN int       nCmdShow
    )
{
    TRTRACEPROC("TracerInit", 2)
    BOOL rc = FALSE;

    TRENTER(("(hInstance=%x,CmdShow=%x)\n", hInstance, nCmdShow));

    if ((rc = RegisterTracerClass(hInstance)) == FALSE)
    {
        TRERRPRINT(("Failed to register tracer class.\n"));
    }
    else
    {
        InitializeListHead(&glistClients);
        ghServerThread = (HANDLE)_beginthread(ServerThread,
                                              0,
                                              0);
        if (ghServerThread != (HANDLE)-1)
        {
            ghInstance = hInstance;
            LoadStringA(hInstance, IDS_APP, gszApp, sizeof(gszApp));
            ghStdCursor = LoadCursorA(NULL,
                                      (LPSTR)MAKEINTRESOURCE(IDC_IBEAM));
            ghWaitCursor = LoadCursorA(NULL,
                                       (LPSTR)MAKEINTRESOURCE(IDC_WAIT));
            ghwndTracer = CreateWindowA(gpszWinTraceClass,
                                        "",
                                        WS_OVERLAPPEDWINDOW,
                                        //BUGBUG: get/save location from reg.
                                        CW_USEDEFAULT,
                                        CW_USEDEFAULT,
                                        CW_USEDEFAULT,
                                        CW_USEDEFAULT,
                                        NULL,
                                        NULL,
                                        hInstance,
                                        NULL);
            if (ghwndTracer != 0)
            {
                int size, len;
                PSZ psz;

                psz = gszSaveFilterSpec;
                size = sizeof(gszSaveFilterSpec);
                len = LoadStringA(ghInstance, IDS_TEXTFILES, psz, size) + 1;

                psz += len;
                size -= len;
                lstrcpyA(psz, "*.txt");
                len = lstrlenA(psz) + 1;

                psz += len;
                size -= len;
                len = LoadStringA(ghInstance, IDS_ALLFILES, psz, size) + 1;

                psz += len;
                size -= len;
                lstrcpyA(psz, "*.*");
                len = lstrlenA(psz) + 1;
                psz += len;
                *psz = '\0';

                gdwfTracer = TF_UNTITLED;
                SetTitle(NULL);

                ShowWindow(ghwndTracer, nCmdShow);
            }
            else
            {
                rc = FALSE;
                TRERRPRINT(("Failed to create tracer window.\n"));
            }
            rc = TRUE;
        }
        else
        {
            ghServerThread = NULL;
            TRERRPRINT(("Failed to create server thread.\n"));
        }
    }

    TREXIT(("=%x\n", rc));
    return rc;
}       //TracerInit

/*++
    @doc    INTERNAL

    @func   BOOL | RegisterTracerClass | Register tracer window class.

    @parm   IN HINSTANCE | hInstance | Instance handle.

    @rvalue SUCCESS | Returns TRUE.
    @rvalue SUCCESS | Returns FALSE.
--*/

BOOL
RegisterTracerClass(
    IN HINSTANCE hInstance
    )
{
    TRTRACEPROC("RegisterTracerClass", 2)
    BOOL rc;
    WNDCLASSEXA wcex;

    TRENTER(("(hInstance=%x)\n", hInstance));

    wcex.cbSize = sizeof(wcex);
    wcex.style = 0;
    wcex.lpfnWndProc = TracerWndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIconA(hInstance, (LPSTR)MAKEINTRESOURCE(IDI_TRACER));
    wcex.hCursor = LoadCursorA(NULL, (LPSTR)MAKEINTRESOURCE(IDC_ARROW));
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = (LPSTR)MAKEINTRESOURCE(IDD_MENU);
    wcex.lpszClassName = gpszWinTraceClass;
    wcex.hIconSm = NULL;

    rc = RegisterClassExA(&wcex) != 0;

    TREXIT(("=%x\n", rc));
    return rc;
}       //RegisterTracerClass

/*++
    @doc    EXTERNAL

    @func   LRESULT | TracerWndProc | Window procedure for Tracer.

    @parm   IN HWND | hwnd | Window handle.
    @parm   IN UINT | uiMsg | Message ID.
    @parm   IN WPARAM | wParam | First message parameter.
    @parm   IN LPARAM | lParam | Second message parameter.

    @rvalue Return value is message specific.
--*/

LRESULT CALLBACK
TracerWndProc(
    IN HWND   hwnd,
    IN UINT   uiMsg,
    IN WPARAM wParam,
    IN LPARAM lParam
    )
{
    TRTRACEPROC("TracerWndProc", 3)
    LRESULT rc = 0;

    TRENTER(("(hwnd=%x,Msg=%s,wParam=%x,lParam=%x)\n",
             hwnd, LookupName(uiMsg, WMMsgNames), wParam, lParam));

    switch (uiMsg)
    {
        case WM_CREATE:
        {
            ghwndEdit = CreateWindowExA(WS_EX_CLIENTEDGE,
                                        "Edit",
                                        "",
                                        (gdwfTracer & TF_LINEWRAP)?
                                            ES_STD:
                                            (ES_STD | WS_HSCROLL),
                                        0,
                                        0,
                                        0,
                                        0,
                                        hwnd,
                                        NULL,
                                        ghInstance,
                                        NULL);
            if (ghwndEdit != NULL)
            {
                HDC hDC;

                hDC = GetDC(NULL);
                if (hDC != NULL)
                {
                    INITCOMMONCONTROLSEX ComCtrl;

                    SendMessage(ghwndEdit, EM_LIMITTEXT, 0, 0);
                    ghFont = GetStockObject(SYSTEM_FIXED_FONT);
                    GetObject(ghFont, sizeof(gLogFont), &gLogFont);
                    gLogFont.lfHeight = -MulDiv(giPointSize,
                                                GetDeviceCaps(hDC,
                                                              LOGPIXELSY),
                                                720);
                    ghFont = CreateFontIndirect(&gLogFont);
                    TRASSERT(ghFont != 0);
                    SendMessage(ghwndEdit,
                                WM_SETFONT,
                                (WPARAM)ghFont,
                                MAKELONG(FALSE, 0));
                    ReleaseDC(NULL, hDC);

                    ComCtrl.dwSize = sizeof(ComCtrl);
                    ComCtrl.dwICC = ICC_BAR_CLASSES | ICC_USEREX_CLASSES;
                    if (!InitCommonControlsEx(&ComCtrl))
                    {
                        DestroyWindow(ghwndEdit);
                        TRERRPRINT(("Failed to initialize Common Control\n"));
                        rc = -1;
                    }
                }
                else
                {
                    DestroyWindow(ghwndEdit);
                    TRERRPRINT(("Failed to get display DC\n"));
                    rc = -1;
                }
            }
            else
            {
                TRERRPRINT(("Failed to create edit window.\n"));
                rc = -1;
            }
            break;
        }

        case WM_DESTROY:
        {
            PLIST_ENTRY plist;
            PCLIENT_ENTRY ClientEntry;

            while (!IsListEmpty(&glistClients))
            {
                plist = glistClients.Flink;
                ClientEntry = CONTAINING_RECORD(plist,
                                                CLIENT_ENTRY,
                                                list);
                WTDeregisterClient(NULL, (HCLIENT)ClientEntry);
            }
            DeleteObject(ghFont);
            DestroyWindow(ghwndEdit);
            PostQuitMessage(0);
            break;
        }

        case WM_COMMAND:
            if ((HWND)lParam == ghwndEdit)
            {
                switch (HIWORD(wParam))
                {
                    case EN_ERRSPACE:
                    case EN_MAXTEXT:
                        ErrorMsg(IDS_ERRSPACE);
                        break;
                }
            }
            else if (!TracerCmdProc(hwnd, wParam, lParam))
            {
                rc = DefWindowProc(hwnd, uiMsg, wParam, lParam);
            }
            break;

        case WM_SETFOCUS:
            SetFocus(ghwndEdit);
            break;

        case WM_SIZE:
            MoveWindow(ghwndEdit,
                       0,
                       0,
                       LOWORD(lParam),
                       HIWORD(lParam),
                       TRUE);
            break;

        default:
            rc = DefWindowProc(hwnd, uiMsg, wParam, lParam);
    }

    TREXIT(("=%x\n", rc));
    return rc;
}       //TracerWndProc

/*++
    @doc    INTERNAL

    @func   LRESULT | TracerCmdProc | Process the WM_COMMAND message.

    @parm   IN HWND | hwnd | Window handle.
    @parm   IN WPARAM | wParam | First message parameter.
    @parm   IN LPARAM | lParam | Second message parameter.

    @rvalue Return value is message specific.
--*/

LRESULT
TracerCmdProc(
    IN HWND   hwnd,
    IN WPARAM wParam,
    IN LPARAM lParam
    )
{
    TRTRACEPROC("TracerCmdProc", 3)
    LRESULT rc = 0;

    TRENTER(("(hwnd=%x,wParam=%x,lParam=%x)\n", hwnd, wParam, lParam));

    switch (LOWORD(wParam))
    {
        case M_SAVE:
            if (!(gdwfTracer & TF_UNTITLED) &&
                SaveFile(hwnd, gszFileName, FALSE))
            {
                break;
            }
            //
            // Otherwise, fall through.
            //
        case M_SAVEAS:
        {
            OPENFILENAMEA ofn;
            char szFileName[MAX_PATH + 1] = "";
            char szSaveAs[16];

            memset(&ofn, 0, sizeof(ofn));
            LoadStringA(ghInstance, IDS_SAVEAS, szSaveAs, sizeof(szSaveAs));
            ofn.lStructSize = sizeof(ofn);
            ofn.hwndOwner = ghwndTracer;
            ofn.hInstance = ghInstance;
            ofn.lpstrFilter = gszSaveFilterSpec;
            ofn.nFilterIndex = 1;
            ofn.lpstrFile = szFileName;
            ofn.nMaxFile = sizeof(szFileName);
            if (gdwfTracer & TF_UNTITLED)
            {
                lstrcpyA(szFileName, "*.txt");
            }
            else
            {
                lstrcpynA(szFileName, gszFileName, MAX_PATH);
                szFileName[MAX_PATH] = '\0';
            }
            ofn.lpstrTitle = szSaveAs;
            ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT |
                        OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST;
            ofn.lpstrDefExt = "txt";
            if (GetSaveFileNameA(&ofn))
            {
                if (SaveFile(hwnd, szFileName, TRUE))
                {
                    lstrcpynA(gszFileName, szFileName, MAX_PATH);
                    gdwfTracer &= ~TF_UNTITLED;
                    SetTitle(NULL);
                }
            }
            else
            {
                DWORD dwErr = CommDlgExtendedError();

                if (dwErr != 0)
                {
                    ErrorMsg(IDS_GETSAVEFILENAME_FAILED, dwErr);
                }
            }
            break;
        }

        case M_PRINT:
            //
            // BUGBUG
            //
            break;

        case M_EXIT:
            PostMessage(hwnd, WM_CLOSE, 0, 0);
            break;

        case M_CLEAR:
            SendMessage(ghwndEdit, EM_SETSEL, 0, -1);
            SendMessage(ghwndEdit, EM_REPLACESEL, 0, (LPARAM)"");
            break;

        case M_FIND:
            if (gszSearchText[0])
            {
//BUGBUG                SearchText(gszSearchText);
                break;
            }
            //
            // Otherwise, fall through.
            //
        case M_FINDNEXT:
            //
            // BUGBUG
            //
        case M_GOTO:
            //BUGBUG
            break;

        case M_WORDWRAP:
            //BUGBUG
            break;

        case M_SETFONT:
        {
            HDC hDC;

            hDC = GetDC(NULL);
            if (hDC != NULL)
            {
                CHOOSEFONT cf;

                memset(&cf, 0, sizeof(cf));
                cf.lStructSize = sizeof(cf);
                cf.hwndOwner = hwnd;
                cf.lpLogFont = &gLogFont;
                gLogFont.lfHeight = -MulDiv(giPointSize,
                                            GetDeviceCaps(hDC, LOGPIXELSY),
                                           720);
                cf.Flags = CF_SCREENFONTS | CF_NOVERTFONTS |
                           CF_INITTOLOGFONTSTRUCT;
                cf.nFontType = SCREEN_FONTTYPE;
                ReleaseDC(NULL, hDC);

                if (ChooseFont(&cf))
                {
                    HFONT hfontNew;

                    SetCursor(ghWaitCursor);
                    hfontNew = CreateFontIndirect(&gLogFont);
                    if (hfontNew != NULL)
                    {
                        DeleteObject(ghFont);
                        ghFont = hfontNew;
                        SendMessage(ghwndEdit,
                                    WM_SETFONT,
                                    (WPARAM)ghFont,
                                    MAKELONG(TRUE, 0));
                        giPointSize = cf.iPointSize;
                    }
                    SetCursor(ghStdCursor);
                }
                else
                {
                    DWORD dwErr = CommDlgExtendedError();

                    if (dwErr != 0)
                    {
                        ErrorMsg(IDS_CHOOSEFONT_FAILED, dwErr);
                    }
                }
            }
            break;
        }

        case M_CLIENTS:
        {
            PROPSHEETHEADER psh;
            PLIST_ENTRY plist;

            psh.dwSize = sizeof(psh);
            psh.dwFlags = 0;
            psh.hwndParent = hwnd;
            psh.hInstance = ghInstance;
            psh.pszCaption = MAKEINTRESOURCE(IDS_CLIENT_SETTINGS);

            psh.nPages = 1;
            for (plist = glistClients.Flink;
                 plist != &glistClients;
                 plist = plist->Flink)
            {
                psh.nPages++;;
            }

            psh.phpage = LocalAlloc(LMEM_FIXED,
                                    sizeof(HPROPSHEETPAGE)*psh.nPages);
            psh.nStartPage = 0;
            CreatePropertyPages(psh.phpage);
            if (PropertySheet(&psh) < 0)
            {
                ErrorMsg(IDSERR_PROP_SHEET, GetLastError());
            }
            LocalFree(psh.phpage);
            break;
        }

        case M_HELP:
            //BUGBUG
            break;

        case M_ABOUT:
            ShellAboutA(hwnd,
                        gszApp,
                        "",
                        LoadIconA(ghInstance,
                                  (LPCSTR)MAKEINTRESOURCE(IDI_TRACER)));
            break;
    }

    TREXIT(("=%x\n", rc));
    return rc;
}       //TracerCmdProc

/*++
    @doc    EXTERNAL

    @func   INT_PTR | GlobalSettingsDlgProc | Global setting dialog procedure.

    @parm   IN HWND | hwnd | Window handle.
    @parm   IN UINT | uiMsg | Message ID.
    @parm   IN WPARAM | wParam | First message parameter.
    @parm   IN LPARAM | lParam | Second message parameter.

    @rvalue Return value is message specific.
--*/

INT_PTR APIENTRY
GlobalSettingsDlgProc(
    IN HWND   hwnd,
    IN UINT   uiMsg,
    IN WPARAM wParam,
    IN LPARAM lParam
    )
{
    TRTRACEPROC("GlobalSettingsDlgProc", 3)
    INT_PTR rc = 0;
    static SETTINGS GlobalSettings = {0};

    TRENTER(("(hwnd=%x,Msg=%s,wParam=%x,lParam=%x)\n",
             hwnd, LookupName(uiMsg, WMMsgNames), wParam, lParam));

    switch (uiMsg)
    {
        case WM_INITDIALOG:
        {
            ghwndPropSheet = GetParent(hwnd);
            GlobalSettings = gDefGlobalSettings;
            SendDlgItemMessage(hwnd,
                               IDC_GLOBALVERBOSESPIN,
                               UDM_SETRANGE,
                               0,
                               MAKELONG(MAX_LEVELS, 0));
            SendDlgItemMessage(hwnd,
                               IDC_GLOBALTRACESPIN,
                               UDM_SETRANGE,
                               0,
                               MAKELONG(MAX_LEVELS, 0));
            SendDlgItemMessage(hwnd,
                               IDC_GLOBALVERBOSESPIN,
                               UDM_SETPOS,
                               0,
                               MAKELONG(GlobalSettings.iVerboseLevel, 0));
            SendDlgItemMessage(hwnd,
                               IDC_GLOBALTRACESPIN,
                               UDM_SETPOS,
                               0,
                               MAKELONG(GlobalSettings.iTraceLevel, 0));
            CheckDlgButton(hwnd,
                           IDC_GLOBALTRACEDEBUGGER,
                           (GlobalSettings.dwfSettings &
                            SETTINGS_TRACE_TO_DEBUGGER) != 0);

            rc = TRUE;
            break;
        }

        case WM_DESTROY:
            ghwndPropSheet = NULL;
            break;

        case WM_NOTIFY:
        {
            NMHDR *lpnm = (NMHDR *)lParam;

            switch (lpnm->code)
            {
                case PSN_APPLY:
                    gDefGlobalSettings = GlobalSettings;
                    break;
            }
            break;
        }

        case WM_COMMAND:
        {
            BOOL fChanged = FALSE;

            switch (LOWORD(wParam))
            {
                case IDC_GLOBALVERBOSE:
                case IDC_GLOBALTRACE:
                {
                    switch (HIWORD(wParam))
                    {
                        BOOL fOK;
                        int n;

                        case EN_UPDATE:
                            n = GetDlgItemInt(hwnd,
                                              LOWORD(wParam),
                                              &fOK,
                                              FALSE);
                            if (fOK && (n <= MAX_LEVELS))
                            {
                                if (LOWORD(wParam) == IDC_GLOBALVERBOSE)
                                {
                                    GlobalSettings.iVerboseLevel = n;
                                }
                                else
                                {
                                    GlobalSettings.iTraceLevel = n;
                                }
                                fChanged = TRUE;
                            }
                            else
                            {
                                SetDlgItemInt(hwnd,
                                              LOWORD(wParam),
                                              (LOWORD(wParam) ==
                                               IDC_GLOBALVERBOSE)?
                                                GlobalSettings.iVerboseLevel:
                                                GlobalSettings.iTraceLevel,
                                              FALSE);
                                SendMessage((HWND)lParam,
                                            EM_SETSEL,
                                            0,
                                            -1);
                            }
                            break;
                    }
                    break;
                }

                case IDC_GLOBALTRACEDEBUGGER:
                    if (IsDlgButtonChecked(hwnd, IDC_GLOBALTRACEDEBUGGER))
                    {
                        GlobalSettings.dwfSettings |=
                                SETTINGS_TRACE_TO_DEBUGGER;
                    }
                    else
                    {
                        GlobalSettings.dwfSettings &=
                                ~SETTINGS_TRACE_TO_DEBUGGER;
                    }
                    fChanged = TRUE;
                    break;
            }

            if (fChanged)
            {
                SendMessage(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
            }
            break;
        }
    }

    TREXIT(("=%x\n", rc));
    return rc;
}       //GlobalSettingsDlgProc

/*++
    @doc    EXTERNAL

    @func   INT_PTR | ClientSettingsDlgProc | Client setting dialog procedure.

    @parm   IN HWND | hwnd | Window handle.
    @parm   IN UINT | uiMsg | Message ID.
    @parm   IN WPARAM | wParam | First message parameter.
    @parm   IN LPARAM | lParam | Second message parameter.

    @rvalue Return value is message specific.
--*/

INT_PTR APIENTRY
ClientSettingsDlgProc(
    IN HWND   hwnd,
    IN UINT   uiMsg,
    IN WPARAM wParam,
    IN LPARAM lParam
    )
{
    TRTRACEPROC("ClientSettingsDlgProc", 3)
    INT_PTR rc = 0;

    TRENTER(("(hwnd=%x,Msg=%s,wParam=%x,lParam=%x)\n",
             hwnd, LookupName(uiMsg, WMMsgNames), wParam, lParam));

    switch (uiMsg)
    {
        case WM_INITDIALOG:
        {
            PCLIENT_ENTRY ClientEntry =
                (PCLIENT_ENTRY)((LPPROPSHEETPAGE)lParam)->lParam;
            int i;

            ghwndPropSheet = GetParent(hwnd);
            SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)ClientEntry);

            SendServerRequest(ClientEntry,
                              SRVREQ_GETCLIENTINFO,
                              &ClientEntry->ClientInfo);

            SendDlgItemMessage(hwnd,
                               IDC_CLIENTVERBOSESPIN,
                               UDM_SETRANGE,
                               0,
                               MAKELONG(MAX_LEVELS, 0));
            SendDlgItemMessage(hwnd,
                               IDC_CLIENTTRACESPIN,
                               UDM_SETRANGE,
                               0,
                               MAKELONG(MAX_LEVELS, 0));

            SendDlgItemMessage(hwnd,
                               IDC_CLIENTVERBOSESPIN,
                               UDM_SETPOS,
                               0,
                               MAKELONG(ClientEntry->ClientInfo.Settings.iVerboseLevel,
                                        0));
            SendDlgItemMessage(hwnd,
                               IDC_CLIENTTRACESPIN,
                               UDM_SETPOS,
                               0,
                               MAKELONG(ClientEntry->ClientInfo.Settings.iTraceLevel,
                                        0));

            CheckDlgButton(hwnd,
                           IDC_CLIENTTRACEDEBUGGER,
                           (ClientEntry->ClientInfo.Settings.dwfSettings &
                            SETTINGS_TRACE_TO_DEBUGGER) != 0);
            CheckDlgButton(hwnd,
                           IDC_CLIENTTRIGGERTRACE,
                           (ClientEntry->ClientInfo.Settings.dwfSettings &
                            SETTINGS_TRIGMODE_ENABLED) != 0);

            for (i = 0; i < NUM_TRIGPTS; ++i)
            {
                SetDlgItemText(hwnd,
                               gTrigPtCtrlMap[i],
                               ClientEntry->ClientInfo.TrigPts[i].szProcName);
                CheckDlgButton(hwnd,
                               gTrigPtTraceMap[i],
                               (ClientEntry->ClientInfo.TrigPts[i].dwfTrigPt &
                                TRIGPT_TRACE_ENABLED) != 0);
                CheckDlgButton(hwnd,
                               gTrigPtBreakMap[i],
                               (ClientEntry->ClientInfo.TrigPts[i].dwfTrigPt &
                                TRIGPT_BREAK_ENABLED) != 0);
            }

            EnableTrigPts(hwnd,
                          (ClientEntry->ClientInfo.Settings.dwfSettings &
                           SETTINGS_TRIGMODE_ENABLED) != 0);

            rc = TRUE;
            break;
        }

        case WM_NOTIFY:
        {
            NMHDR *lpnm = (NMHDR *)lParam;

            switch (lpnm->code)
            {
                case PSN_APPLY:
                {
                    PCLIENT_ENTRY ClientEntry;

                    ClientEntry = (PCLIENT_ENTRY)GetWindowLongPtr(hwnd,
                                                                  DWLP_USER);
                    if (ClientEntry != NULL)
                    {
                        ClientEntry->ClientInfo.Settings =
                            ClientEntry->TempSettings;
                        RtlCopyMemory(ClientEntry->ClientInfo.TrigPts,
                                      ClientEntry->TempTrigPts,
                                      sizeof(ClientEntry->ClientInfo.TrigPts));

                        SendServerRequest(ClientEntry,
                                          SRVREQ_SETCLIENTINFO,
                                          &ClientEntry->ClientInfo);
                    }
                    else
                    {
                        TRWARNPRINT(("Notify: Failed to get Client Entry\n"));
                    }
                    break;
                }
            }
            break;
        }

        case WM_COMMAND:
        {
            BOOL fChanged = FALSE;
            PCLIENT_ENTRY ClientEntry;
            BOOL fTrace = FALSE;

            switch (LOWORD(wParam))
            {
                case IDC_CLIENTVERBOSE:
                case IDC_CLIENTTRACE:
                    switch (HIWORD(wParam))
                    {
                        case EN_UPDATE:
                        {
                            BOOL fOK;
                            int n;

                            ClientEntry = (PCLIENT_ENTRY)GetWindowLongPtr(
                                                                hwnd,
                                                                DWLP_USER);
                            if (ClientEntry != NULL)
                            {
                                n = GetDlgItemInt(hwnd,
                                                  LOWORD(wParam),
                                                  &fOK,
                                                  FALSE);
                                if (fOK && (n <= MAX_LEVELS))
                                {
                                    if (LOWORD(wParam) == IDC_CLIENTVERBOSE)
                                    {
                                        ClientEntry->TempSettings.iVerboseLevel
                                            = n;
                                    }
                                    else
                                    {
                                        ClientEntry->TempSettings.iTraceLevel
                                            = n;
                                    }
                                    fChanged = TRUE;
                                }
                                else
                                {
                                    SetDlgItemInt(
                                        hwnd,
                                        LOWORD(wParam),
                                        (LOWORD(wParam) == IDC_CLIENTVERBOSE)?
                                          ClientEntry->TempSettings.iVerboseLevel:
                                          ClientEntry->TempSettings.iTraceLevel,
                                        FALSE);
                                    SendMessage((HWND)lParam,
                                                EM_SETSEL,
                                                0,
                                                -1);
                                }
                            }
                            else
                            {
                                TRWARNPRINT(("Verbose/Trace: Failed to get Client Entry\n"));
                            }
                            break;
                        }
                    }
                    break;

                case IDC_CLIENTTRACEDEBUGGER:
                case IDC_CLIENTTRIGGERTRACE:
                    ClientEntry = (PCLIENT_ENTRY)GetWindowLongPtr(hwnd,
                                                                  DWLP_USER);
                    if (ClientEntry != NULL)
                    {
                        DWORD dwf = (LOWORD(wParam) ==
                                     IDC_CLIENTTRACEDEBUGGER)?
                                        SETTINGS_TRACE_TO_DEBUGGER:
                                        SETTINGS_TRIGMODE_ENABLED;
                        BOOL fChecked = IsDlgButtonChecked(hwnd,
                                                           LOWORD(wParam));

                        if (fChecked)
                        {
                            ClientEntry->TempSettings.dwfSettings |= dwf;
                        }
                        else
                        {
                            ClientEntry->TempSettings.dwfSettings &= ~dwf;
                        }

                        if (LOWORD(wParam) == IDC_CLIENTTRIGGERTRACE)
                        {
                            EnableTrigPts(hwnd, fChecked);
                        }

                        fChanged = TRUE;
                    }
                    else
                    {
                        TRWARNPRINT(("TraceToDebugger/TrigModeEnabled: Failed to get Client Entry\n"));
                    }
                    break;

                case IDC_TRIGPT1:
                case IDC_TRIGPT2:
                case IDC_TRIGPT3:
                case IDC_TRIGPT4:
                case IDC_TRIGPT5:
                case IDC_TRIGPT6:
                case IDC_TRIGPT7:
                case IDC_TRIGPT8:
                case IDC_TRIGPT9:
                case IDC_TRIGPT10:
                    switch (HIWORD(wParam))
                    {
                        case EN_UPDATE:
                        {
                            int n;
                            int iTrigPt;

                            ClientEntry = (PCLIENT_ENTRY)GetWindowLongPtr(
                                                                hwnd,
                                                                DWLP_USER);
                            if (ClientEntry != NULL)
                            {
                                iTrigPt = FindTrigPtIndex(LOWORD(wParam),
                                                          gTrigPtCtrlMap);
                                n = GetDlgItemTextA(
                                        hwnd,
                                        LOWORD(wParam),
                                        ClientEntry->TempTrigPts[iTrigPt].szProcName,
                                        MAX_PROCNAME_LEN - 1);
                                if ((n > 0) ||
                                    (GetLastError() == ERROR_SUCCESS))
                                {
                                    fChanged = TRUE;
                                }
                                else
                                {
                                    TRWARNPRINT(("Failed to get trigger point text (err=%x)\n",
                                                 GetLastError()));
                                }
                            }
                            else
                            {
                                TRWARNPRINT(("TrigPt: Failed to get Client Entry\n"));
                            }
                            break;
                        }
                    }
                    break;

                case IDC_TRIGPT1_TRACE:
                case IDC_TRIGPT2_TRACE:
                case IDC_TRIGPT3_TRACE:
                case IDC_TRIGPT4_TRACE:
                case IDC_TRIGPT5_TRACE:
                case IDC_TRIGPT6_TRACE:
                case IDC_TRIGPT7_TRACE:
                case IDC_TRIGPT8_TRACE:
                case IDC_TRIGPT9_TRACE:
                case IDC_TRIGPT10_TRACE:
                    fTrace = TRUE;
                    //
                    // Fall through ...
                    //
                case IDC_TRIGPT1_BREAK:
                case IDC_TRIGPT2_BREAK:
                case IDC_TRIGPT3_BREAK:
                case IDC_TRIGPT4_BREAK:
                case IDC_TRIGPT5_BREAK:
                case IDC_TRIGPT6_BREAK:
                case IDC_TRIGPT7_BREAK:
                case IDC_TRIGPT8_BREAK:
                case IDC_TRIGPT9_BREAK:
                case IDC_TRIGPT10_BREAK:
                {
                    int iTrigPt;

                    ClientEntry = (PCLIENT_ENTRY)GetWindowLongPtr(hwnd,
                                                                  DWLP_USER);
                    if (ClientEntry != NULL)
                    {
                        DWORD dwf = fTrace? TRIGPT_TRACE_ENABLED:
                                            TRIGPT_BREAK_ENABLED;

                        iTrigPt = FindTrigPtIndex(LOWORD(wParam),
                                                  fTrace? gTrigPtTraceMap:
                                                          gTrigPtBreakMap);
                        if (IsDlgButtonChecked(hwnd, LOWORD(wParam)))
                        {
                            ClientEntry->TempTrigPts[iTrigPt].dwfTrigPt |= dwf;
                        }
                        else
                        {
                            ClientEntry->TempTrigPts[iTrigPt].dwfTrigPt &=
                                ~dwf;
                        }
                        fChanged = TRUE;
                    }
                    else
                    {
                        TRWARNPRINT(("TrigPtEnable: Failed to get Client Entry\n"));
                    }
                    break;
                }
            }

            if (fChanged)
            {
                SendMessage(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
            }
            break;
        }
    }

    TREXIT(("=%x\n", rc));
    return rc;
}       //ClientSettingsDlgProc

/*++
    @doc    INTERNAL

    @func   VOID | EnableTrigPts | Enable trigger points controls.

    @parm   IN HWND | hDlg | Dialog box handle.
    @parm   IN BOOL | fEnable | TRUE if enable.

    @rvalue None.
--*/

VOID
EnableTrigPts(
    IN HWND hDlg,
    IN BOOL fEnable
    )
{
    TRTRACEPROC("EnableTrigPts", 3)
    int i;

    TRENTER(("(hDlg=%x,fEnable=%x)\n", hDlg, fEnable));

    EnableWindow(GetDlgItem(hDlg, IDC_CLIENTTRIGPTGROUPBOX), fEnable);
    for (i = 0; i < NUM_TRIGPTS; ++i)
    {
        EnableWindow(GetDlgItem(hDlg, gTrigPtTextMap[i]), fEnable);
        EnableWindow(GetDlgItem(hDlg, gTrigPtTraceTextMap[i]), fEnable);
        EnableWindow(GetDlgItem(hDlg, gTrigPtBreakTextMap[i]), fEnable);
        EnableWindow(GetDlgItem(hDlg, gTrigPtCtrlMap[i]), fEnable);
        EnableWindow(GetDlgItem(hDlg, gTrigPtTraceMap[i]), fEnable);
        EnableWindow(GetDlgItem(hDlg, gTrigPtBreakMap[i]), fEnable);
    }

    TREXIT(("!\n"));
    return;
}       //EnableTrigPts

/*++
    @doc    INTERNAL

    @func   BOOL | SaveFile | Save the text buffer to a file.

    @parm   IN HWND | hwndParent | Window handle parent.
    @parm   IN PSZ | pszFileName | Points to the file name string to save.
    @parm   IN BOOL | fSaveAs | TRUE if called from SaveAs.

    @rvalue SUCCESS | Returns TRUE.
    @rvalue FAILURE | Returns FALSE.
--*/

BOOL
SaveFile(
    IN HWND hwndParent,
    IN PSZ  pszFileName,
    IN BOOL fSaveAs
    )
{
    TRTRACEPROC("SaveFile", 3)
    BOOL rc = FALSE;
    HANDLE hFile;

    TRENTER(("(hwnd=%x,File=%s,fSaveAs=%x)\n",
             hwndParent, pszFileName, fSaveAs));

    hFile = CreateFileA(pszFileName,
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ,
                        NULL,
                        fSaveAs? OPEN_ALWAYS: OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        BOOL fNew;
        UINT uiTextSize;
        HLOCAL hlText;
        LPSTR lpch;
        DWORD dwcbWritten;

        fNew = (GetLastError() != ERROR_ALREADY_EXISTS);
        uiTextSize = (UINT)SendMessage(ghwndEdit, WM_GETTEXTLENGTH, 0, 0);
        hlText = (HLOCAL)SendMessage(ghwndEdit, EM_GETHANDLE, 0, 0);
        if ((hlText != NULL) && ((lpch = (LPSTR)LocalLock(hlText)) != NULL))
        {
            rc = WriteFile(hFile,
                           lpch,
                           uiTextSize,
                           &dwcbWritten,
                           NULL);
            if (rc == FALSE)
            {
                ErrorMsg(IDS_WRITEFILE_FAILED, pszFileName);
            }
            LocalUnlock(hlText);
        }
        else
        {
            TRERRPRINT(("Failed to get text length or get text handle\n"));
        }
        CloseHandle(hFile);

        if ((rc == FALSE) && fNew)
        {
            DeleteFileA(pszFileName);
        }
    }
    else
    {
        ErrorMsg(IDS_CREATEFILE_FAILED, pszFileName);
    }

    TREXIT(("=%x\n", rc));
    return rc;
}       //SaveFile

/*++
    @doc    INTERNAL

    @func   UINT | CreatePropertyPages | Create the global setting page as
            well as a property page for each registered clients.

    @parm   OUT HPROPSHEETPAGE *| hPages | Points to the array to hold all
            the created property sheet handles.

    @rvalue SUCCESS | Returns number of pages created.
    @rvalue FAILURE | Returns 0.
--*/

UINT
CreatePropertyPages(
    OUT HPROPSHEETPAGE *hPages
    )
{
    TRTRACEPROC("CreatePropertyPages", 3)
    UINT nPages = 0;
    PROPSHEETPAGEA psp;
    PLIST_ENTRY plist;
    PCLIENT_ENTRY ClientEntry;

    TRENTER(("(hPages=%p)\n", hPages));

    psp.dwSize = sizeof(psp);
    psp.dwFlags = 0;
    psp.hInstance = ghInstance;
    psp.pszTitle = NULL;
    psp.lParam = 0;

    psp.pszTemplate = (LPSTR)MAKEINTRESOURCE(IDD_GLOBALSETTINGS);
    psp.pfnDlgProc = GlobalSettingsDlgProc;
    hPages[nPages] = CreatePropertySheetPageA(&psp);
    if (hPages[nPages] != NULL)
    {
        nPages++;
    }

    psp.dwFlags = PSP_USETITLE;
    psp.pszTemplate = (LPSTR)MAKEINTRESOURCE(IDD_CLIENTSETTINGS);
    psp.pfnDlgProc = ClientSettingsDlgProc;
    for (plist = glistClients.Flink;
         plist != &glistClients;
         plist = plist->Flink)
    {
        ClientEntry = CONTAINING_RECORD(plist, CLIENT_ENTRY, list);
        psp.pszTitle = ClientEntry->szClientName;
        psp.lParam = (LPARAM)ClientEntry;
        hPages[nPages] = CreatePropertySheetPageA(&psp);
        if (hPages[nPages] != NULL)
        {
            ClientEntry->hPage = hPages[nPages];
            nPages++;
        }
    }

    TREXIT(("=%d\n", nPages));
    return nPages;
}       //CreatePropertyPages

/*++
    @doc    INTERNAL

    @func   VOID | SetTitle | Set the title bar text.

    @parm   IN PSZ | pszTitle | Points to title text string.  If NULL, set
            title to current file name.

    @rvalue None.
--*/

VOID
SetTitle(
    IN PSZ pszTitle OPTIONAL
    )
{
    TRTRACEPROC("SetTitle", 3)
    char szWindowText[MAX_PATH + 16];

    TRENTER(("(Title=%s)\n", pszTitle));

    if (pszTitle != NULL)
    {
        lstrcpyA(szWindowText, pszTitle);
    }
    else
    {
        int len;

        if (gdwfTracer & TF_UNTITLED)
        {
            LoadStringA(ghInstance,
                        IDS_UNTITLED,
                        szWindowText,
                        sizeof(szWindowText));
        }
        else
        {
            lstrcpynA(szWindowText, gszFileName, sizeof(szWindowText));
        }

        len = lstrlenA(szWindowText);
        LoadStringA(ghInstance,
                    IDS_TITLE,
                    &szWindowText[len],
                    sizeof(szWindowText) - len);
    }
    SetWindowTextA(ghwndTracer, szWindowText);

    TREXIT(("!\n"));
    return;
}       //SetTitle

/*++
    @doc    INTERNAL

    @func   int | FindTrigPtIndex | Find the trigger point index by its
            control ID.

    @parm   IN int | iID | Dialog object control ID.
    @parm   IN const int * | IDTable | Points to the ID map.

    @rvalue SUCCESS | Returns the trigger point index.
    @rvalue FAILURE | Returns -1.
--*/

int
FindTrigPtIndex(
    IN int  iID,
    IN const int *IDTable
    )
{
    TRTRACEPROC("FindTrigPtIndex", 3)
    int i;

    TRENTER(("(ID=%d,IDTable=%p)\n", iID, IDTable));

    for (i = 0; i < NUM_TRIGPTS; ++i)
    {
        if (iID == IDTable[i])
        {
            break;
        }
    }

    if (i == NUM_TRIGPTS)
    {
        i = -1;
    }

    TREXIT(("=%d\n", i));
    return i;
}       //FindTrigPtIndex

/*++
    @doc    INTERNAL

    @func   VOID | ErrorMsg | Put out an error message box.

    @parm   IN ULONG | ErrCode | The given error code.
    @parm   ... | Substituting arguments for the error message.

    @rvalue Returns the number of chars in the message.
--*/

int
ErrorMsg(
    IN ULONG      ErrCode,
    ...
    )
{
    static char szFormat[1024];
    static char szErrMsg[1024];
    int n;
    va_list arglist;

    LoadStringA(ghInstance, ErrCode, szFormat, sizeof(szFormat));
    va_start(arglist, ErrCode);
    n = wvsprintfA(szErrMsg, szFormat, arglist);
    va_end(arglist);
    MessageBoxA(NULL, szErrMsg, gszApp, MB_OK | MB_ICONERROR);

    return n;
}       //ErrorMsg