
/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/


/******************************************************************************

                        P R O C E S S   V I E W E R

    Name:       pviewer.c

    Description:
        This program demonstrates the usage of special registry APIs
        for collecting performance data.

        C files used in this app:
            pviewer.c       - this file
            pviewdat.c      - updates the dialog
            perfdata.c      - gets performance data structures
            objdata.c       - access performance data objects
            instdata.c      - access performance data instances
            cntrdata.c      - access performance data counters

******************************************************************************/




#include <windows.h>
#include <winperf.h>
#include "perfdata.h"
#include "pviewdat.h"
#include "pviewdlg.h"
#include <string.h>
#include <stdio.h>



#define INDEX_STR_LEN       10
#define MACHINE_NAME_LEN    MAX_COMPUTERNAME_LENGTH+2
#define MACHINE_NAME_SIZE   MACHINE_NAME_LEN+1


/****
Globals
****/

TCHAR           INDEX_PROCTHRD_OBJ[2*INDEX_STR_LEN];
TCHAR           INDEX_COSTLY_OBJ[3*INDEX_STR_LEN];

TCHAR           gszMachineName[MACHINE_NAME_SIZE];
TCHAR           gszCurrentMachine[MACHINE_NAME_SIZE];

DWORD           gPerfDataSize = 50*1024;            // start with 50K
PPERF_DATA      gpPerfData;

DWORD           gCostlyDataSize = 100*1024;         // start wiih 100K
PPERF_DATA      gpCostlyData;


PPERF_OBJECT    gpProcessObject;                    // pointer to process objects
PPERF_OBJECT    gpThreadObject;                     // pointer to thread objects
PPERF_OBJECT    gpThreadDetailsObject;              // pointer to thread detail objects
PPERF_OBJECT    gpAddressSpaceObject;               // pointer to address space objects
PPERF_OBJECT    gpImageObject;                      // pointer to image objects


HKEY            ghPerfKey = HKEY_PERFORMANCE_DATA;  // get perf data from this key
HKEY            ghMachineKey = HKEY_LOCAL_MACHINE;  // get title index from this key


HCURSOR         ghCursor[2];                        // 0 = arrow, 1 = hourglass

HANDLE          ghMemUpdateEvent;                   // to signal a refresh of mem stats
HANDLE          ghMemUpdateMutex;                   // to restrict overlapping refreshes




/****
Prototypes
****/

BOOL CALLBACK   PviewDlgProc (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
void    PviewDlgRefresh (HWND hWnd);
void    PviewDlgRefreshCostlyData (HWND hPviewDlg);
void    PviewDlgRefreshProcess (HWND hWnd);
void    PviewDlgRefreshThread (HWND hWnd);
void    PviewDlgRefreshCurSelProcess (HWND hWnd);
void    PviewDlgRefreshCurSelThread (HWND hWnd);
WORD    PviewDlgGetCurSelPriority (HWND hWnd);
BOOL    PviewDlgChangePriority (HWND hWnd, DWORD wParam, WORD wItem);
BOOL    PviewDlgTerminateProcess (HWND hPviewDlg);

BOOL CALLBACK   MemDlgProc (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
void    MemDlgUpdateThread (HWND hWnd);
void    MemDlgRefresh (HWND hWnd, HWND hPviewDlg);
void    MemDlgRefreshCurSelImage (HWND hMemDlg, HWND hPviewDlg);

INT     GetCurSelText (HWND hList, LPTSTR str);
DWORD   GetCurSelData (HWND hWnd, DWORD dwList);
INT     ReSelectText (HWND hList, INT StartIndex, LPTSTR str);
void    SetPerfIndexes (HWND hWnd);
DWORD   GetTitleIdx (HWND hWnd, LPTSTR TitleSz[], DWORD LastIndex, LPTSTR Name);
void    SetListBoxTabStops (HWND hWnd);
void    SetLocalMachine (void);
BOOL    ConnectComputer (HWND hWnd);
void    DisableControls (HWND hPviewDlg);
void    EnableControls (HWND hPviewDlg);




//********************************************************
//
//  WinMain --
//
//      Build Up: create the program's dialog box,
//          load the desired icons, enter the message
//          loop.
//
//      Tear Down: free up the memory allocated by the
//          dialog box proc, and exit.
//
int WINAPI WinMain (HINSTANCE   hInstance,
                    HINSTANCE   hPrevInstance,
                    LPSTR       lpCmdLine,
                    int         nCmdShow)
{
HANDLE  hWndDialog;
MSG     msg;


    // load our default cursors
    //
    ghCursor[0] = LoadCursor (0, IDC_ARROW);
    ghCursor[1] = LoadCursor (0, IDC_WAIT);

    // open our dialog box
    //
    hWndDialog = CreateDialogParam (hInstance,
                                    MAKEINTRESOURCE (PVIEW_DLG),
                                    NULL,
                                    (DLGPROC) PviewDlgProc,
                                    (LONG)0);

    // associate the icon with our window
    //
    SetClassLong (hWndDialog, GCL_HICON, (LONG)LoadIcon(hInstance, TEXT("VIEWPICON")) );


    // the almighty Windows message loop:
    //
    while (GetMessage (&msg, NULL, 0, 0))
        if (!IsDialogMessage (hWndDialog, &msg))
            {
            TranslateMessage (&msg);
            DispatchMessage (&msg);
            }

    // close up shop
    //
    DestroyWindow (hWndDialog);
    LocalFree (gpPerfData);

    return 0;
}




/*****************
PviewDlg functions
*****************/

//********************************************************
//
//  PviewDlgProc --
//
//      Pview dialog procedure
//
BOOL CALLBACK   PviewDlgProc   (HWND    hWnd,
                                UINT    wMsg,
                                WPARAM  wParam,
                                LPARAM  lParam)
{
WORD    wItem;


    switch (wMsg)
        {

        case WM_INITDIALOG:
            SetListBoxTabStops (hWnd);
            SendDlgItemMessage (hWnd, PVIEW_COMPUTER, EM_LIMITTEXT, MACHINE_NAME_LEN, 0);
            PostMessage (hWnd, WM_COMMAND, PVIEW_REFRESH, 0);
            break;

        case WM_CLOSE:
            PostQuitMessage (0);
            break;

        case WM_COMMAND:
            //
            // handle our app-specific controls:
            //
            switch (LOWORD (wParam))
                {
                // works just like "close"
                //
                case PVIEW_EXIT:
                    PostQuitMessage (0);
                    break;

                // if somebody moved the highlight in the thread list,
                //  update the view
                //
                case PVIEW_THREAD_LIST:
                    if (HIWORD(wParam) == LBN_DBLCLK || HIWORD(wParam) == LBN_SELCHANGE)
                        {
                        PviewDlgRefreshCurSelThread (hWnd);
                        PostMessage (hWnd, WM_COMMAND, PVIEW_REFRESH_COSTLY_DATA, 0);
                        }
                    break;

                // if somebody clicked on a new process, update all of the
                //  affected information.
                //
                case PVIEW_PROCESS_LIST:
                    if (HIWORD(wParam) == CBN_DBLCLK || HIWORD(wParam) == CBN_SELCHANGE)
                        {
                        PviewDlgRefreshCurSelProcess (hWnd);
                        PostMessage (hWnd, WM_COMMAND, PVIEW_REFRESH_COSTLY_DATA, 0);
                        if (HIWORD(wParam) == CBN_DBLCLK)
                            PostMessage (hWnd, WM_COMMAND, PVIEW_MEMORY_DETAIL, 0);
                        }
                    break;

                // the user wishes to view the memory stats in detail:
                //
                case PVIEW_MEMORY_DETAIL:
                    //
                    // check to see if we can get exclusive access
                    //  to the memory statistics
                    //
                    if (WaitForSingleObject (ghMemUpdateMutex, 0))

                        // we can't, so just return.
                        //
                        return FALSE;

                    else
                        {
                        // we have exclusive access, so start up the
                        //  memory statistics dialog.
                        //
                        // release the mutex first so the dialog can use it.
                        //
                        ReleaseMutex (ghMemUpdateMutex);
                        DialogBoxParam (NULL,
                                        MAKEINTRESOURCE (MEMORY_DLG),
                                        hWnd,
                                        (DLGPROC) MemDlgProc,
                                        (LONG)hWnd);
                        }
                    break;

                // somebody clicked one of the priority radio
                //  buttons.  Find out which one was selected...
                //
                case PVIEW_PRIORITY_HIGH:
                case PVIEW_PRIORITY_NORMAL:
					 case PVIEW_PRIORITY_IDL:

                    if (SendDlgItemMessage (hWnd, PVIEW_PRIORITY_HIGH, BM_GETCHECK, 0, 0))
                        wItem = PVIEW_PRIORITY_HIGH;
                    else if (SendDlgItemMessage (hWnd, PVIEW_PRIORITY_NORMAL, BM_GETCHECK, 0, 0))
                        wItem = PVIEW_PRIORITY_NORMAL;
                    else
                        wItem = PVIEW_PRIORITY_IDL;

                    // if the user actually clicked on a NEW state,
                    //  do the change.
                    //
                    if (LOWORD(wParam) != wItem)
                        {
                        // of course, if it's a remote machine, disallow
                        //  the modification.
                        //
                        if (lstrcmp (gszCurrentMachine, gszMachineName))
                            {
                            SendDlgItemMessage (hWnd, wItem, BM_SETCHECK, 1, 0);
                            SetFocus (GetDlgItem (hWnd, wItem));
                            MessageBox (hWnd,
                                        TEXT("Cannot change process priority on remote machine"),
                                        TEXT("Set priority"),
                                        MB_ICONEXCLAMATION|MB_OK);
                            }

                        // at this point, we know we are affecting the local
                        //  machine, and a change has to be made.
                        //  Just Do It(TM).
                        //
                        else if (PviewDlgChangePriority (hWnd, wParam, wItem))
                            PviewDlgRefresh (hWnd);

                        }
                    break;

                case PVIEW_THREAD_HIGHEST:
                case PVIEW_THREAD_ABOVE:
                case PVIEW_THREAD_NORMAL:
                case PVIEW_THREAD_BELOW:
                case PVIEW_THREAD_LOWEST:
                    //
                    // this selection hasn't been fleshed out yet.
                    //
                    PviewDlgRefreshCurSelThread (hWnd);
                    break;

                // terminate the selected process
                //
                case PVIEW_TERMINATE:
                    if (PviewDlgTerminateProcess (hWnd))
                        PviewDlgRefresh (hWnd);
                    break;

                // if the text has changed, we want to connect and
                //  view another system's processes...
                //
                case PVIEW_COMPUTER:
                    if (HIWORD(wParam) == EN_CHANGE)
                        EnableWindow (GetDlgItem (hWnd, PVIEW_CONNECT), TRUE);
                    else
                        return FALSE;
                    break;

                // we were told to connect, go ahead and try...
                //
                case PVIEW_CONNECT:
                    if (ConnectComputer (hWnd))
                        {
                        SetPerfIndexes (hWnd);
                        PviewDlgRefresh (hWnd);
                        }
                    break;

                // refresh the current information displayed
                //
                case PVIEW_REFRESH:
                    if (ConnectComputer (hWnd))
                        SetPerfIndexes (hWnd);
                    PviewDlgRefresh (hWnd);
                    break;

                // refresh the currently updated costly
                //  statistics
                //
                case PVIEW_REFRESH_COSTLY_DATA:
                    if (WaitForSingleObject (ghMemUpdateMutex, 0))
                        return FALSE;

                    PviewDlgRefreshCostlyData (hWnd);
                    ReleaseMutex (ghMemUpdateMutex);
                    break;

                default:
                    return FALSE;
                }
            break;

        default:
            return FALSE;
        }

    return TRUE;
}




//********************************************************
//
//  PviewDlgRefresh --
//
//      Refresh the pview dialog.
//
void    PviewDlgRefresh (HWND hWnd)
{
static  HANDLE  hMemUpdateThread = NULL;
static  DWORD   MemUpdateThreadID;
MSG     Msg;


    SetCursor (ghCursor[1]);


    if (hMemUpdateThread)       // get memory data
        SetEvent (ghMemUpdateEvent);
    else
        hMemUpdateThread = CreateThread (NULL,
                                         0,
                                         (LPTHREAD_START_ROUTINE)MemDlgUpdateThread,
                                         (LPVOID)hWnd,
                                         0,
                                         &MemUpdateThreadID);


    // get performance data
    //
    gpPerfData = RefreshPerfData (ghPerfKey, INDEX_PROCTHRD_OBJ, gpPerfData, &gPerfDataSize);

    gpProcessObject = FindObject (gpPerfData, PX_PROCESS);
    gpThreadObject  = FindObject (gpPerfData, PX_THREAD);


    // refresh
    //
    PviewDlgRefreshProcess (hWnd);
    PviewDlgRefreshThread (hWnd);



	 // Remove all mouse and key messages. They are not accepted
    //  while the cursor is a hourglass.
    //
    while (PeekMessage (&Msg, hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE));
    while (PeekMessage (&Msg, hWnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE));

    SetCursor (ghCursor[0]);

}




//********************************************************
//
//  PviewDlgRefreshCostlyData --
//
//      Refresh the costly data.
//
void    PviewDlgRefreshCostlyData (HWND hPviewDlg)
{
LPTSTR          szProcessName;
LPTSTR          szThreadName;
PPERF_INSTANCE  pInstance;
DWORD           dwIndex;


    dwIndex       = GetCurSelData (hPviewDlg, PVIEW_PROCESS_LIST);
    pInstance     = FindInstanceN (gpProcessObject, dwIndex);
    szProcessName = InstanceName (pInstance);

    RefreshPviewDlgMemoryData (hPviewDlg,
                               pInstance,
                               gpProcessObject,
                               gpAddressSpaceObject);


    dwIndex      = GetCurSelData (hPviewDlg, PVIEW_THREAD_LIST);
    pInstance    = FindInstanceN (gpThreadObject, dwIndex);
    szThreadName = InstanceName (pInstance);

    RefreshPviewDlgThreadPC (hPviewDlg,
                             szProcessName,
                             szThreadName,
                             gpThreadDetailsObject,
                             gpCostlyData);

}




//********************************************************
//
//  PviewDlgRefreshProcess --
//
//      Refresh the process list and data in pview dialog.
//
void    PviewDlgRefreshProcess (HWND hWnd)
{
TCHAR   szProcessString[256];
INT     nProcess;
INT     nIndex;
HWND    hProcessList;
DWORD   dwProcessIndex;


    // refresh process list
    //
    hProcessList = GetDlgItem (hWnd, PVIEW_PROCESS_LIST);
    nProcess     = GetCurSelText (hProcessList, szProcessString);

    SendMessage (hProcessList, WM_SETREDRAW, FALSE, 0);
    SendMessage (hProcessList, LB_RESETCONTENT, 0, 0);
    SendMessage (hProcessList, LB_SETITEMDATA, 0, 0);


    RefreshProcessList (hProcessList, gpProcessObject);

    // refresh process data
    //
    if (nProcess != LB_ERR)
        nIndex = ReSelectText (hProcessList, nProcess, szProcessString);
    else
        nIndex = 0;


    dwProcessIndex = SendMessage (hProcessList, LB_GETITEMDATA, nIndex, 0);

    RefreshProcessData (hWnd, gpProcessObject, dwProcessIndex);

    SendMessage (hProcessList, WM_SETREDRAW, TRUE, 0);

}




//********************************************************
//
//  PviewDlgRefreshThread --
//
//      Refresh the thread list and data in pview dialog.
//
void    PviewDlgRefreshThread (HWND hWnd)
{
TCHAR           szThreadString[256];
INT             nThread;
INT             nIndex;
HWND            hThreadList;
DWORD           dwThreadIndex;

PPERF_INSTANCE  pProcessInstance;
DWORD           dwProcessIndex;


    // get process info
    //
    dwProcessIndex = GetCurSelData (hWnd, PVIEW_PROCESS_LIST);
    pProcessInstance = FindInstanceN (gpProcessObject, dwProcessIndex);


    // refresh thread list
    //
    hThreadList  = GetDlgItem (hWnd, PVIEW_THREAD_LIST);
    nThread      = GetCurSelText (hThreadList, szThreadString);

    SendMessage (hThreadList, WM_SETREDRAW, FALSE, 0);
    SendMessage (hThreadList, LB_RESETCONTENT, 0, 0);
    SendMessage (hThreadList, LB_SETITEMDATA, 0, 0);

    RefreshThreadList (hThreadList, gpThreadObject, dwProcessIndex);


    // refresh thread data
    //
    if (nThread != LB_ERR)
        nIndex = ReSelectText (hThreadList, nThread, szThreadString);
    else
        nIndex = 0;

    dwThreadIndex    = SendMessage (hThreadList, LB_GETITEMDATA, nIndex, 0);

    RefreshThreadData (hWnd,
                       gpThreadObject,
                       dwThreadIndex,
                       gpProcessObject,
                       pProcessInstance);

    SendMessage (hThreadList, WM_SETREDRAW, TRUE, 0);

}




//********************************************************
//
//  PviewDlgGetCurSelPriority --
//
//      Get the process priority of currently selected process.
//
WORD    PviewDlgGetCurSelPriority (HWND hWnd)
{
DWORD           dwIndex;
PPERF_INSTANCE  pInst;

    dwIndex = GetCurSelData (hWnd, PVIEW_PROCESS_LIST);
    pInst = FindInstanceN (gpProcessObject, dwIndex);
    return ProcessPriority (gpProcessObject, pInst);
}




//********************************************************
//
//  PviewDlgRefreshCurSelProcess --
//
//      Refresh the data of currently selected process.
//
void    PviewDlgRefreshCurSelProcess (HWND hWnd)
{
DWORD   dwIndex;

    dwIndex = GetCurSelData (hWnd, PVIEW_PROCESS_LIST);
    RefreshProcessData (hWnd, gpProcessObject, dwIndex);

    PviewDlgRefreshThread (hWnd);
}




//********************************************************
//
//  PviewDlgRefreshCurSelThread --
//
//      Refresh the data of currently selected thread.
//
void    PviewDlgRefreshCurSelThread (HWND hWnd)
{
PPERF_INSTANCE  pProcessInstance;
DWORD           dwIndex;

    dwIndex = GetCurSelData (hWnd, PVIEW_PROCESS_LIST);
    pProcessInstance = FindInstanceN (gpProcessObject, dwIndex);

    dwIndex = GetCurSelData (hWnd, PVIEW_THREAD_LIST);

    RefreshThreadData (hWnd,
                       gpThreadObject,
                       dwIndex,
                       gpProcessObject,
                       pProcessInstance);
}




//********************************************************
//
//  PviewDlgChangePriority --
//
//      Change process priority.
//
BOOL    PviewDlgChangePriority (HWND hWnd, DWORD wParam, WORD wItem)
{
DWORD           dwIndex;
PPERF_INSTANCE  pInst;
PPERF_COUNTER   pCountID;
DWORD           *pProcessID;
DWORD           ProcessID;
HANDLE          hProcess;
BOOL            bStat;



    dwIndex = GetCurSelData (hWnd, PVIEW_PROCESS_LIST);
    pInst = FindInstanceN (gpProcessObject, dwIndex);


    if (pCountID = FindCounter (gpProcessObject, PX_PROCESS_ID))
        {
        pProcessID = (DWORD *) CounterData (pInst, pCountID);
        ProcessID = *pProcessID;
        }
    else
        {
        SendDlgItemMessage (hWnd, wItem, BM_SETCHECK, 1, 0);
        SetFocus (GetDlgItem (hWnd, wItem));
        MessageBox (hWnd,
                    TEXT("Cannot find ID for this process"),
                    TEXT("Set priority"),
                    MB_ICONEXCLAMATION|MB_OK);
        return FALSE;
        }


    hProcess = OpenProcess (PROCESS_SET_INFORMATION, FALSE, ProcessID);
    if (!hProcess)
        {
        SendDlgItemMessage (hWnd, wItem, BM_SETCHECK, 1, 0);
        SetFocus (GetDlgItem (hWnd, wItem));
        MessageBox (hWnd,
                    TEXT("Unable to open the process; Priority not changed"),
                    TEXT("Set priority"),
                    MB_ICONEXCLAMATION|MB_OK);
        return FALSE;
        }



    switch (wParam)
        {
        case PVIEW_PRIORITY_HIGH:
            bStat = SetPriorityClass (hProcess, HIGH_PRIORITY_CLASS);
            break;

        case PVIEW_PRIORITY_NORMAL:
            bStat = SetPriorityClass (hProcess, NORMAL_PRIORITY_CLASS);
            break;

        case PVIEW_PRIORITY_IDL:
            bStat = SetPriorityClass (hProcess, IDLE_PRIORITY_CLASS);
            break;

        default:
            break;
        }


    CloseHandle (hProcess);

    if (!bStat)
        {
        SendDlgItemMessage (hWnd, wItem, BM_SETCHECK, 1, 0);
        SetFocus (GetDlgItem (hWnd, wItem));
        MessageBox (hWnd,
                    TEXT("Unable to change priority"),
                    TEXT("Set priority"),
                    MB_ICONEXCLAMATION|MB_OK);
        return FALSE;
        }


    return TRUE;

}




//********************************************************
//
//  PviewDlgTerminateProcess --
//
//      Terminate the current selected process.
//
BOOL    PviewDlgTerminateProcess (HWND hPviewDlg)
{
DWORD           dwIndex;
PPERF_INSTANCE  pInst;
PPERF_COUNTER   pCountID;
DWORD           *pProcessID;
DWORD           ProcessID;
HANDLE          hProcess;
TCHAR           szTemp[50];


    dwIndex = GetCurSelData (hPviewDlg, PVIEW_PROCESS_LIST);
    pInst = FindInstanceN (gpProcessObject, dwIndex);


    if (pCountID = FindCounter (gpProcessObject, PX_PROCESS_ID))
        {
        pProcessID = (DWORD *) CounterData (pInst, pCountID);
        ProcessID = *pProcessID;
        }
    else
        {
        MessageBox (hPviewDlg,
                    TEXT("Cannot find ID for this process"),
                    TEXT("Terminate Process"),
                    MB_ICONEXCLAMATION|MB_OK);
        return FALSE;
        }


    wsprintf (szTemp, TEXT("Terminate process %s (ID %#x)?"),
              InstanceName (pInst), ProcessID);

    if (MessageBox (hPviewDlg, szTemp, TEXT("Terminate Process"), MB_ICONSTOP|MB_OKCANCEL) != IDOK)
        return FALSE;


    hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, ProcessID);
    if (!hProcess)
        {
        MessageBox (hPviewDlg,
                    TEXT("Unable to open the process; Process not terminated"),
                    TEXT("Terminate Process"),
                    MB_ICONEXCLAMATION|MB_OK);
        return FALSE;
        }


    if (!TerminateProcess (hProcess, 99))
        {
        MessageBox (hPviewDlg,
                    TEXT("Unable to terminate the process."),
                    TEXT("Terminate Process"),
                    MB_ICONEXCLAMATION|MB_OK);

        CloseHandle (hProcess);
        return FALSE;
        }


    CloseHandle (hProcess);

    return TRUE;

}




/***************
MemDlg functions
***************/

//********************************************************
//
//  MemDlgProc --
//
//      MemoryDlg procedure
//
BOOL CALLBACK   MemDlgProc (HWND    hWnd,
                            UINT    wMsg,
                            WPARAM  wParam,
                            LPARAM  lParam)
{
static HWND hPviewDlg;


    switch (wMsg)
        {
        case WM_INITDIALOG:
            hPviewDlg = (HWND)lParam;
            PostMessage (hWnd, WM_COMMAND, MEMORY_REFRESH, 0);
            break;

        case WM_QUIT:
        case WM_CLOSE:
            EndDialog (hWnd, TRUE);
            break;

        case WM_COMMAND:
            switch (LOWORD (wParam))
                {
                // get the memory statistics for the currently selected
                //  process/thread
                //
                case MEMORY_IMAGE:
                    if (HIWORD(wParam) == CBN_DBLCLK || HIWORD(wParam) == CBN_SELCHANGE)
                        {
                        if (WaitForSingleObject (ghMemUpdateMutex, 0))
                            return FALSE;

                        MemDlgRefreshCurSelImage (hWnd, hPviewDlg);
                        ReleaseMutex (ghMemUpdateMutex);
                        }
                    else
                        return FALSE;
                    break;

                // refresh the current memory statistics,
                //  retry if we can't get the mutex
                //
                case MEMORY_REFRESH:
                    if (WaitForSingleObject (ghMemUpdateMutex, 1000))
                        {
                        // can't get the mutex, retry...
                        //
                        PostMessage (hWnd, WM_COMMAND, MEMORY_REFRESH, 0);
                        return FALSE;
                        }

                    MemDlgRefresh (hWnd, hPviewDlg);
                    ReleaseMutex (ghMemUpdateMutex);
                    break;

                case IDCANCEL:
                case IDOK:
                    EndDialog (hWnd, TRUE);
                    break;

                default:
                    return FALSE;
                }
        default:
            return FALSE;
        }


    return TRUE;

}




//********************************************************
//
//  MemDlgUpdateThread --
//
//      This function runs in a separate thread to collect memory data.
//
void MemDlgUpdateThread (HWND hPviewDlg)
{

    ghMemUpdateMutex = CreateMutex (NULL, TRUE, NULL);
    ghMemUpdateEvent = CreateEvent (NULL, FALSE, FALSE, NULL);


    while (TRUE)
        {
        EnableWindow (GetDlgItem (hPviewDlg, PVIEW_MEMORY_DETAIL), FALSE);


        gpCostlyData = RefreshPerfData (ghPerfKey,
                                        INDEX_COSTLY_OBJ,
                                        gpCostlyData,
                                        &gCostlyDataSize);


        gpAddressSpaceObject  = FindObject (gpCostlyData, PX_PROCESS_ADDRESS_SPACE);
        gpThreadDetailsObject = FindObject (gpCostlyData, PX_THREAD_DETAILS);
        gpImageObject         = FindObject (gpCostlyData, PX_IMAGE);


        EnableWindow (GetDlgItem (hPviewDlg, PVIEW_MEMORY_DETAIL), TRUE);

        ReleaseMutex (ghMemUpdateMutex);

        PostMessage (hPviewDlg, WM_COMMAND, PVIEW_REFRESH_COSTLY_DATA, 0);


        WaitForSingleObject (ghMemUpdateEvent, INFINITE);
        WaitForSingleObject (ghMemUpdateMutex, INFINITE);
        }

}




//********************************************************
//
//  MemDlgRefresh --
//
//      Refresh the memory dialog.
//
void MemDlgRefresh (HWND hMemDlg, HWND hPviewDlg)
{
HWND            hImageList;
DWORD           dwIndex;
BOOL            bStat;
PPERF_INSTANCE  pInstance;


    hImageList = GetDlgItem (hMemDlg, MEMORY_IMAGE);

    SendMessage (hImageList, WM_SETREDRAW, FALSE, 0);
    SendMessage (hImageList, CB_RESETCONTENT, 0, 0);
    SendMessage (hImageList, CB_SETITEMDATA, 0, 0);

    dwIndex = GetCurSelData (hPviewDlg, PVIEW_PROCESS_LIST);
    pInstance = FindInstanceN (gpProcessObject, dwIndex);

    bStat = RefreshMemoryDlg (hMemDlg,
                              pInstance,
                              gpProcessObject,
                              gpAddressSpaceObject,
                              gpImageObject);

    SendMessage (hImageList, WM_SETREDRAW, TRUE, 0);
    SendMessage (hImageList, CB_SETCURSEL, 0, 0);

    if (!bStat)
        {
        MessageBox (hMemDlg,
                    TEXT("Unable to retrieve memory detail"),
                    TEXT("Memory detail"),
                    MB_ICONSTOP|MB_OK);
        PostMessage (hMemDlg, WM_CLOSE, 0, 0);
        }

}




//********************************************************
//
//  MemDlgRefreshCurSelImage --
//
//      Refresh the current selected image for memory dialog.
//
void    MemDlgRefreshCurSelImage (HWND hMemDlg, HWND hPviewDlg)
{
HWND    hList;
INT     nIndex;
DWORD   dwIndex;


    hList = GetDlgItem (hMemDlg, MEMORY_IMAGE);
    nIndex = SendMessage (hList, CB_GETCURSEL, 0, 0);

    if (nIndex == CB_ERR)
        nIndex = 0;

    dwIndex = SendMessage (hList, CB_GETITEMDATA, nIndex, 0);

    if (dwIndex == 0xFFFFFFFF)
        MemDlgRefresh (hMemDlg, hPviewDlg);
    else
        RefreshMemoryDlgImage (hMemDlg, dwIndex, gpImageObject);

}




/****************
utility functions
****************/

//********************************************************
//
//  GetCurSelText --
//
//      Get the text of current selection.  Used for later ReSelectText().
//
INT     GetCurSelText (HWND hList, LPTSTR str)
{
INT     Index;
INT     Length;

    Index = SendMessage (hList, LB_GETCURSEL, 0, 0);
    Length = SendMessage (hList, LB_GETTEXT, Index, (DWORD)str);

    return Index;
}




//********************************************************
//
//  GetCurSelData --
//
//      Get the data associated with the current selection.
//
DWORD   GetCurSelData (HWND hWnd, DWORD dwList)
{
HWND    hList;
INT     nIndex;
DWORD   dwIndex;


    hList  = GetDlgItem (hWnd, dwList);
    nIndex = SendMessage (hList, LB_GETCURSEL, 0, 0);

    if (nIndex == LB_ERR)
        nIndex = 0;

    dwIndex = SendMessage (hList, LB_GETITEMDATA, nIndex, 0);

    return dwIndex;
}




//********************************************************
//
//  ReSelectText --
//
//      Reselect the line specified by str.  Returns the new index.  If cannot
//      find the line or any error, then 0 is returned.
//
INT     ReSelectText (HWND hList, INT StartIndex, LPTSTR str)
{
INT     Index;
INT     Length;
TCHAR   SaveChar = TEXT('\0');


    Index = SendMessage (hList, LB_FINDSTRING, StartIndex, (DWORD)str);

    if (Index == LB_ERR)
        {
        Length = lstrlen (str);

        while (Index == LB_ERR && Length)
            {
            SaveChar = str[Length-1];
            str[Length-1] = TEXT('\0');

            Index = SendMessage (hList, LB_FINDSTRING, StartIndex, (DWORD)str);

            str[Length-1] = SaveChar;
            Length--;
            }
        }

    if (Index == LB_ERR)
        return 0;
    else
        {
        SendMessage (hList, LB_SETCURSEL, Index, 0);
        return Index;
        }

}




//********************************************************
//
//  SetPerfIndexes
//
//      Setup the perf data indexes.
//
void    SetPerfIndexes (HWND hWnd)
{
LPTSTR  TitleBuffer;
LPTSTR  *Title;
DWORD   Last;
TCHAR   szTemp[50];
DWORD   dwR;


    dwR = GetPerfTitleSz (ghMachineKey, ghPerfKey, &TitleBuffer, &Title, &Last);

    if (dwR != ERROR_SUCCESS)
        {
        wsprintf (szTemp, TEXT("Unable to retrieve counter indexes, ERROR -> %#x"), dwR);
        MessageBox (hWnd, szTemp, TEXT("Pviewer"), MB_OK|MB_ICONEXCLAMATION);
        return;
        }


	 PX_PROCESS 							 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS);
	 PX_PROCESS_CPU						 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_CPU);
	 PX_PROCESS_PRIV						 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIV);
	 PX_PROCESS_USER						 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_USER);
	 PX_PROCESS_WORKING_SET 			 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_WORKING_SET);
	 PX_PROCESS_PEAK_WS					 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PEAK_WS);
	 PX_PROCESS_PRIO						 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIO);
	 PX_PROCESS_ELAPSE					 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_ELAPSE);
	 PX_PROCESS_ID 						 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_ID);
	 PX_PROCESS_PRIVATE_PAGE			 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIVATE_PAGE);
	 PX_PROCESS_VIRTUAL_SIZE			 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_VIRTUAL_SIZE);
	 PX_PROCESS_PEAK_VS					 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PEAK_VS);
	 PX_PROCESS_FAULT_COUNT 			 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_FAULT_COUNT);

	 PX_THREAD								 = GetTitleIdx (hWnd, Title, Last, PN_THREAD);
	 PX_THREAD_CPU 						 = GetTitleIdx (hWnd, Title, Last, PN_THREAD_CPU);
	 PX_THREAD_PRIV						 = GetTitleIdx (hWnd, Title, Last, PN_THREAD_PRIV);
	 PX_THREAD_USER						 = GetTitleIdx (hWnd, Title, Last, PN_THREAD_USER);
	 PX_THREAD_START						 = GetTitleIdx (hWnd, Title, Last, PN_THREAD_START);
	 PX_THREAD_SWITCHES					 = GetTitleIdx (hWnd, Title, Last, PN_THREAD_SWITCHES);
	 PX_THREAD_PRIO						 = GetTitleIdx (hWnd, Title, Last, PN_THREAD_PRIO);
	 PX_THREAD_BASE_PRIO 				 = GetTitleIdx (hWnd, Title, Last, PN_THREAD_BASE_PRIO);
	 PX_THREAD_ELAPSE 					 = GetTitleIdx (hWnd, Title, Last, PN_THREAD_ELAPSE);

	 PX_THREAD_DETAILS					 = GetTitleIdx (hWnd, Title, Last, PN_THREAD_DETAILS);
	 PX_THREAD_PC							 = GetTitleIdx (hWnd, Title, Last, PN_THREAD_PC);

	 PX_IMAGE								 = GetTitleIdx (hWnd, Title, Last, PN_IMAGE);
	 PX_IMAGE_NOACCESS					 = GetTitleIdx (hWnd, Title, Last, PN_IMAGE_NOACCESS);
	 PX_IMAGE_READONLY					 = GetTitleIdx (hWnd, Title, Last, PN_IMAGE_READONLY);
	 PX_IMAGE_READWRITE					 = GetTitleIdx (hWnd, Title, Last, PN_IMAGE_READWRITE);
	 PX_IMAGE_WRITECOPY					 = GetTitleIdx (hWnd, Title, Last, PN_IMAGE_WRITECOPY);
	 PX_IMAGE_EXECUTABLE 				 = GetTitleIdx (hWnd, Title, Last, PN_IMAGE_EXECUTABLE);
	 PX_IMAGE_EXE_READONLY				 = GetTitleIdx (hWnd, Title, Last, PN_IMAGE_EXE_READONLY);
	 PX_IMAGE_EXE_READWRITE 			 = GetTitleIdx (hWnd, Title, Last, PN_IMAGE_EXE_READWRITE);
	 PX_IMAGE_EXE_WRITECOPY 			 = GetTitleIdx (hWnd, Title, Last, PN_IMAGE_EXE_WRITECOPY);

	 PX_PROCESS_ADDRESS_SPACE			 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_ADDRESS_SPACE);
	 PX_PROCESS_PRIVATE_NOACCESS		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIVATE_NOACCESS);
	 PX_PROCESS_PRIVATE_READONLY		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIVATE_READONLY);
	 PX_PROCESS_PRIVATE_READWRITE 	 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIVATE_READWRITE);
	 PX_PROCESS_PRIVATE_WRITECOPY 	 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIVATE_WRITECOPY);
	 PX_PROCESS_PRIVATE_EXECUTABLE	 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIVATE_EXECUTABLE);
	 PX_PROCESS_PRIVATE_EXE_READONLY  = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIVATE_EXE_READONLY);
	 PX_PROCESS_PRIVATE_EXE_READWRITE = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIVATE_EXE_READWRITE);
	 PX_PROCESS_PRIVATE_EXE_WRITECOPY = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_PRIVATE_EXE_WRITECOPY);

	 PX_PROCESS_MAPPED_NOACCESS		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_MAPPED_NOACCESS);
	 PX_PROCESS_MAPPED_READONLY		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_MAPPED_READONLY);
	 PX_PROCESS_MAPPED_READWRITE		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_MAPPED_READWRITE);
	 PX_PROCESS_MAPPED_WRITECOPY		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_MAPPED_WRITECOPY);
	 PX_PROCESS_MAPPED_EXECUTABLE 	 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_MAPPED_EXECUTABLE);
	 PX_PROCESS_MAPPED_EXE_READONLY	 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_MAPPED_EXE_READONLY);
	 PX_PROCESS_MAPPED_EXE_READWRITE  = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_MAPPED_EXE_READWRITE);
	 PX_PROCESS_MAPPED_EXE_WRITECOPY  = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_MAPPED_EXE_WRITECOPY);

	 PX_PROCESS_IMAGE_NOACCESS 		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_IMAGE_NOACCESS);
	 PX_PROCESS_IMAGE_READONLY 		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_IMAGE_READONLY);
	 PX_PROCESS_IMAGE_READWRITE		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_IMAGE_READWRITE);
	 PX_PROCESS_IMAGE_WRITECOPY		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_IMAGE_WRITECOPY);
	 PX_PROCESS_IMAGE_EXECUTABLE		 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_IMAGE_EXECUTABLE);
	 PX_PROCESS_IMAGE_EXE_READONLY	 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_IMAGE_EXE_READONLY);
	 PX_PROCESS_IMAGE_EXE_READWRITE	 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_IMAGE_EXE_READWRITE);
	 PX_PROCESS_IMAGE_EXE_WRITECOPY	 = GetTitleIdx (hWnd, Title, Last, PN_PROCESS_IMAGE_EXE_WRITECOPY);


    wsprintf (INDEX_PROCTHRD_OBJ, TEXT("%ld %ld"), PX_PROCESS, PX_THREAD);
    wsprintf (INDEX_COSTLY_OBJ, TEXT("%ld %ld %ld"),
              PX_PROCESS_ADDRESS_SPACE, PX_IMAGE, PX_THREAD_DETAILS);


    LocalFree (TitleBuffer);
    LocalFree (Title);

}




//********************************************************
//
//  GetTitleIdx
//
//      Searches Titles[] for Name.  Returns the index found.
//
DWORD   GetTitleIdx (HWND hWnd, LPTSTR Title[], DWORD LastIndex, LPTSTR Name)
{
DWORD   Index;

    for (Index = 0; Index <= LastIndex; Index++)
        if (Title[Index])
            if (!lstrcmpi (Title[Index], Name))
                return Index;

    MessageBox (hWnd, Name, TEXT("Pviewer cannot find index"), MB_OK);
    return 0;
}




//********************************************************
//
//  SetListBoxTabStops --
//
//      Set tab stops in the two list boxes.
//
void    SetListBoxTabStops (HWND hWnd)
{
HWND    hListBox;
INT     Tabs[4] = {22*4, 36*4, 44*4};

    hListBox = GetDlgItem (hWnd, PVIEW_PROCESS_LIST);
    SendMessage (hListBox, LB_SETTABSTOPS, 3, (DWORD)Tabs);

    hListBox = GetDlgItem (hWnd, PVIEW_THREAD_LIST);
    SendMessage (hListBox, LB_SETTABSTOPS, 3, (DWORD)Tabs);
}




//********************************************************
//
//  SetLocalMachine --
//
//      Set local machine as performance data focus.
//
//      Sets    ghPerfKey
//              ghMachineKey
//              gszMachineName
//              gszCurrentMachine
//
void    SetLocalMachine (void)
{
TCHAR   szName[MACHINE_NAME_SIZE];
DWORD   dwSize = MACHINE_NAME_SIZE;


	 // close remote connections, if any
    //
    if (ghPerfKey != HKEY_PERFORMANCE_DATA)
        RegCloseKey (ghPerfKey);

    if (ghMachineKey != HKEY_LOCAL_MACHINE)
        RegCloseKey (ghMachineKey);


    // set to registry keys on local machine
    //
    ghPerfKey    = HKEY_PERFORMANCE_DATA;
    ghMachineKey = HKEY_LOCAL_MACHINE;



    // get computer name
    GetComputerName (szName, &dwSize);



    if (szName[0] != '\\' || szName[1] != '\\')     // must have two '\\'
        {
        wsprintf (gszMachineName, TEXT("\\\\%s"), szName);
        lstrcpy (gszCurrentMachine, gszMachineName);
        }
    else
        {
        lstrcpy (gszMachineName, szName);
        lstrcpy (gszCurrentMachine, gszMachineName);
        }

}




//********************************************************
//
//  ConnectComputer --
//
//      Connect to a computer with name entered in PVIEW_COMPUTER.
//      If a new connection is made, then return TRUE else return FALSE.
//
//      Sets    gszCurrentMachine
//              ghPerfKey
//              ghMachineKey
//
BOOL    ConnectComputer (HWND hWnd)
{
DWORD   dwR;
HKEY    hKey;
TCHAR   szTemp[MACHINE_NAME_SIZE];
TCHAR   szTemp2[MACHINE_NAME_SIZE+100];
BOOL    bResult = TRUE;
MSG     Msg;



    SetCursor (ghCursor[1]);


    if (!GetDlgItemText (hWnd, PVIEW_COMPUTER, szTemp, sizeof (szTemp)))
        {
        SetLocalMachine ();
        SetDlgItemText (hWnd, PVIEW_COMPUTER, gszCurrentMachine);
        }

    else if (!lstrcmpi (szTemp, gszCurrentMachine))     // didn't change name
        bResult = FALSE;

    else if (!lstrcmpi (szTemp, gszMachineName))        // local machine
        {
        SetLocalMachine ();
        EnableControls (hWnd);
        }

    else
        {
        // a remote machine, connect to it
        //
        dwR = RegConnectRegistry (szTemp, HKEY_PERFORMANCE_DATA, &hKey);

        if (dwR != ERROR_SUCCESS)
            {
            wsprintf (szTemp2, TEXT("Cannot connect to computer %s"), szTemp);
            MessageBox (hWnd, szTemp2, TEXT(""), MB_ICONEXCLAMATION|MB_OK);

            SetDlgItemText (hWnd, PVIEW_COMPUTER, gszCurrentMachine);

            bResult = FALSE;
            }
        else
            {
            // connected
            //
				lstrcpy (gszCurrentMachine, szTemp);

				if (ghPerfKey != HKEY_PERFORMANCE_DATA)
                RegCloseKey (ghPerfKey);

				ghPerfKey = hKey;



            DisableControls (hWnd);



            // we also need to get the remote machine's title indexes.
            //
            dwR = RegConnectRegistry (gszCurrentMachine, HKEY_LOCAL_MACHINE, &hKey);

				if (ghMachineKey != HKEY_LOCAL_MACHINE)
                RegCloseKey (ghMachineKey);

            if (dwR == ERROR_SUCCESS)
                ghMachineKey = hKey;
            else
                // unable to connect, so we'll use our own indexes.
                //
                ghMachineKey = HKEY_LOCAL_MACHINE;
            }
        }



	 // Remove all mouse and key messages. They are not accepted
    //  while the cursor is a hourglass.
    //
    while (PeekMessage (&Msg, hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE));
    while (PeekMessage (&Msg, hWnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE));

    SetCursor (ghCursor[0]);


    EnableWindow (GetDlgItem (hWnd, PVIEW_CONNECT), FALSE);


    return bResult;

}




//********************************************************
//
//  DisableControls --
//
//      Disable controls that don't make sense on remote machine
//
void DisableControls (HWND hPviewDlg)
{
    EnableWindow (GetDlgItem (hPviewDlg, PVIEW_TERMINATE), FALSE);
    EnableWindow (GetDlgItem (hPviewDlg, PVIEW_PRIORITY_HIGH), FALSE);
    EnableWindow (GetDlgItem (hPviewDlg, PVIEW_PRIORITY_NORMAL), FALSE);
    EnableWindow (GetDlgItem (hPviewDlg, PVIEW_PRIORITY_IDL), FALSE);
}




//********************************************************
//
//  EnableControls --
//
//      Enable controls disabled by DisableControl().
//
void EnableControls (HWND hPviewDlg)
{
    EnableWindow (GetDlgItem (hPviewDlg, PVIEW_TERMINATE), TRUE);
    EnableWindow (GetDlgItem (hPviewDlg, PVIEW_PRIORITY_HIGH), TRUE);
    EnableWindow (GetDlgItem (hPviewDlg, PVIEW_PRIORITY_NORMAL), TRUE);
    EnableWindow (GetDlgItem (hPviewDlg, PVIEW_PRIORITY_IDL), TRUE);
}
