home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / utility / v12n02.zip / PROCMON.C < prev    next >
Text File  |  1992-12-20  |  32KB  |  855 lines

  1. // ProcMon - Simple Process Performance Monitor for Windows/NT
  2. // Demonstrates use of process information block in System Registry.
  3. // Tested with October 92 (beta) Release of Windows/NT.
  4. // Copyright (C) 1992 Ray Duncan
  5. // PC Magazine * Ziff Davis Publishing
  6. //
  7. // Because the current version of Microsoft C does not contain support
  8. // for 64 bit integers, I chose to keep this example program simple by
  9. // just dumping the raw 64 bit counter data instead of building my own
  10. // math routines to "interpret" the counter data.
  11. //
  12. // Known bugs or malfeatures in this version:
  13. //
  14. // If there are two processes with the same name running, PROCMON
  15. // will only let you display counters for the first instance.
  16. //
  17.  
  18. #define dim(x) (sizeof(x) / sizeof(x[0]))   // returns no. of elements
  19. #define MALLOCINCR 4096
  20.  
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <windows.h>
  24. #include <winperf.h>
  25. #include "procmon.h"
  26.  
  27. HANDLE hInst;                               // module instance handle
  28. HWND hFrame;                                // handle for frame window
  29. HWND hLeft;                                 // handle for left child window
  30. HWND hRight;                                // handle for right child window
  31. HFONT hFont;                                // handle for nonprop. font
  32. INT CharX, CharY;                           // character dimensions
  33.  
  34. char szFrameClass[] = "ProcMon";            // classname for frame window
  35. char szAppName[] = "Process Monitor";       // long application name
  36. char szMenuName[] = "ProcMonMenu";          // name of menu resource
  37. char szIcon[] = "ProcMonIcon";              // name of icon resource
  38. char szIni[] = "ProcMon.ini";               // name of private INI file
  39.  
  40. DWORD dwPerfDataLen = 0;                    // size of performance data
  41. PPERFDATA pPerfData;                        // addr of perf data block
  42. PPERFGROUP pFirstGroup;                     // addr of first object group
  43. INT iTotGroups;                             // total object groups
  44. PPERFGROUP pProcessGroup;                   // address of process group
  45. PSTR pTitles;                               // addr of object title database
  46. char szCurProcess[256];                     // name of current process here
  47.  
  48. //
  49. // Table of window messages supported by FrameWndProc()
  50. // and the functions which correspond to each message.
  51. //
  52. struct decodeMsg frameMsgs[] = {
  53.     WM_CREATE, DoCreate,
  54.     WM_SIZE, DoSize,
  55.     WM_COMMAND, DoCommand,
  56.     WM_CLOSE, DoClose,
  57.     WM_DESTROY, DoDestroy, } ;
  58.  
  59. //
  60. // Table of menubar item IDs and their corresponding functions.
  61. //
  62. struct decodeMsg menuitems[] = {
  63.     IDM_EXIT, DoMenuExit,
  64.     IDM_ABOUT, DoMenuAbout,
  65.     IDM_REFRESH, DoRefresh, } ;
  66.  
  67. // 
  68. // Table of magic numbers for counter types and corresponding 
  69. // descriptive strings and counter data sizes.  Where the PERFMON.H
  70. // file does not declare a counter as a specific size, I have assumed
  71. // that the counter is 32-bits and marked the counter with ???.
  72. //
  73. struct decodeCounter counterNames[] = {
  74.     0, "Unknown Counter Type", PERF_CTR_NODATA,
  75.     PERF_COUNTER_COUNTER, "PERF_COUNTER_COUNTER", PERF_CTR_32BIT,
  76.     PERF_COUNTER_TIMER,   "PERF_COUNTER_TIMER", PERF_CTR_64BIT,
  77.     PERF_COUNTER_QUEUELEN, "PERF_COUNTER_QUEUELEN", PERF_CTR_32BIT, // ??? 
  78.     PERF_COUNTER_BULK_COUNT, "PERF_COUNTER_BULK_COUNT", PERF_CTR_64BIT,
  79.     PERF_COUNTER_TEXT, "PERF_COUNTER_TEXT", PERF_CTR_TEXT,
  80.     PERF_COUNTER_RAWCOUNT, "PERF_COUNTER_RAWCOUNT", PERF_CTR_32BIT,
  81.     PERF_SAMPLE_FRACTION, "PERF_SAMPLE_FRACTION", PERF_CTR_32BIT,
  82.     PERF_SAMPLE_COUNTER, "PERF_SAMPLE_COUNTER", PERF_CTR_32BIT, // ??? 
  83.     PERF_COUNTER_NODATA, "PERF_COUNTER_NODATA", PERF_CTR_NODATA,
  84.     PERF_COUNTER_TIMER_INV, "PERF_COUNTER_TIMER_INV", PERF_CTR_64BIT,
  85.     PERF_SAMPLE_BASE, "PERF_SAMPLE_BASE", PERF_CTR_NODATA,
  86.     PERF_AVERAGE_TIMER, "PERF_AVERAGE_TIMER", PERF_CTR_32BIT, // ??? 
  87.     PERF_AVERAGE_BASE, "PERF_AVERAGE_BASE", PERF_CTR_NODATA,
  88.     PERF_AVERAGE_BULK, "PERF_AVERAGE_BULK", PERF_CTR_NODATA,
  89.     PERF_100NSEC_TIMER, "PERF_100NSEC_TIMER", PERF_CTR_64BIT,
  90.     PERF_100NSEC_TIMER_INV, "PERF_100NSEC_TIMER_INV", PERF_CTR_64BIT,
  91.     PERF_COUNTER_MULTI_TIMER, "PERF_COUNTER_MULTI_TIMER", PERF_CTR_64BIT,
  92.     PERF_COUNTER_MULTI_TIMER_INV, "PERF_COUNTER_MULTI_TIMER_INV", PERF_CTR_64BIT,
  93.     PERF_COUNTER_MULTI_BASE, "PERF_COUNTER_MULTI_BASE", PERF_CTR_NODATA,
  94.     PERF_100NSEC_MULTI_TIMER, "PERF_100NSEC_MULTI_TIMER", PERF_CTR_64BIT,
  95.     PERF_100NSEC_MULTI_TIMER_INV, "PERF_100NSEC_MULTI_TIMER_INV", PERF_CTR_64BIT,
  96.     PERF_RAW_FRACTION, "PERF_RAW_FRACTION", PERF_CTR_32BIT, // ???
  97.     PERF_RAW_BASE, "PERF_RAW_BASE", PERF_CTR_NODATA, // ???
  98.     PERF_COUNTER_HISTOGRAM, "PERF_COUNTER_HISTOGRAM", PERF_CTR_NODATA, } ;
  99.  
  100. //
  101. // WinMain -- entry point for this application from Windows.
  102. //
  103. int APIENTRY WinMain(HANDLE hInstance,
  104.     HANDLE hPrevInstance, PSTR lpCmdLine, int nCmdShow)
  105. {
  106.     MSG msg;                                // scratch message storage
  107.     hInst = hInstance;                      // save this instance handle
  108.  
  109.     if(!InitApp(hInstance, nCmdShow))       // initialize everything
  110.     {
  111.         MessageBox(hFrame, "Initialization failed!", szAppName,
  112.             MB_ICONSTOP | MB_OK);
  113.         return(FALSE);
  114.     }
  115.  
  116.     while(GetMessage(&msg, NULL, 0, 0))     // while message != WM_QUIT
  117.     {
  118.         TranslateMessage(&msg);             // translate virtual key codes
  119.         DispatchMessage(&msg);              // dispatch message to window
  120.     }
  121.  
  122.     TermApp(hInstance);                     // clean up everything
  123.     return(msg.wParam);                     // return code = WM_QUIT value
  124. }
  125.  
  126. //
  127. // InitApp --- global initialization code for this application
  128. //
  129. BOOL InitApp(HANDLE hInstance, int nCmdShow)
  130. {
  131.     HDC hdc;                                // handle for device context
  132.     RECT rect;                              // window position & size
  133.     TEXTMETRIC tm;                          // info about font
  134.     WNDCLASS  wc;                           // window class info
  135.  
  136.     // set parameters for frame window class
  137.     wc.style = CS_HREDRAW|CS_VREDRAW;       // class style
  138.     wc.lpfnWndProc = FrameWndProc;          // class callback function
  139.     wc.cbClsExtra = 0;                      // extra per-class data
  140.     wc.cbWndExtra = 0;                      // extra per-window data
  141.     wc.hInstance = hInstance;               // handle of class owner
  142.     wc.hIcon = LoadIcon(hInst, szIcon);     // application icon
  143.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);       // default cursor
  144.     wc.hbrBackground = GetStockObject(WHITE_BRUSH); // background color 
  145.     wc.lpszMenuName =  szMenuName;          // name of menu resource
  146.     wc.lpszClassName = szFrameClass;        // name of window class
  147.  
  148.     // register frame window class for this app
  149.     if(!RegisterClass(&wc))                 
  150.         return(FALSE);
  151.  
  152.     hFrame = CreateWindow(                  // create frame window
  153.         szFrameClass,                       // window class name
  154.         szAppName,                          // text for title bar
  155.         WS_OVERLAPPEDWINDOW,                // window style
  156.         CW_USEDEFAULT, CW_USEDEFAULT,       // default position
  157.         CW_USEDEFAULT, CW_USEDEFAULT,       // default size
  158.         NULL,                               // no parent window
  159.         NULL,                               // use class default menu
  160.         hInstance,                          // window owner
  161.         NULL);                              // unused pointer
  162.  
  163.     if(!hFrame) return(FALSE);              // error, can't create window
  164.  
  165.     hdc = GetDC(hFrame);                    // get device context
  166.     hFont = GetStockObject(SYSTEM_FIXED_FONT);  // handle for nonprop. font
  167.     SelectObject(hdc, hFont);               // realize the font and get
  168.     GetTextMetrics(hdc, &tm);               // the character dimensions
  169.     CharX = tm.tmAveCharWidth;
  170.     CharY = tm.tmHeight + tm.tmExternalLeading;
  171.     ReleaseDC(hFrame, hdc);                 // release device context
  172.  
  173.     // force nonproportional system font for both child windows
  174.     SendMessage(hLeft, WM_SETFONT, (UINT) hFont, 0);
  175.     SendMessage(hRight, WM_SETFONT, (UINT) hFont, 0);
  176.  
  177.     GetWindowRect(hFrame, &rect);           // current window pos & size
  178.  
  179.     // read profile for frame window from previous invocation, if any
  180.     rect.left   = GetPrivateProfileInt("Frame", "xul", rect.left, szIni);
  181.     rect.top    = GetPrivateProfileInt("Frame", "yul", rect.top, szIni);
  182.     rect.right  = GetPrivateProfileInt("Frame", "xlr", rect.right, szIni);
  183.     rect.bottom = GetPrivateProfileInt("Frame", "ylr", rect.bottom, szIni);
  184.  
  185.     MoveWindow(hFrame, rect.left, rect.top, // force window size & position
  186.         rect.right-rect.left, rect.bottom-rect.top, TRUE);
  187.  
  188.     // allocate initial buffer to receive performance data
  189.     // this buffer will be grown as necessary by GetPerfData
  190.     dwPerfDataLen = MALLOCINCR;
  191.     pPerfData = (PPERFDATA) malloc(dwPerfDataLen);
  192.     if(pPerfData == NULL)
  193.         return(FALSE);
  194.  
  195.     // fetch names of object types from system registry
  196.     if(!GetObjectTitles())
  197.         return(FALSE);
  198.  
  199.     // fetch system performance data from system registry
  200.     if(!GetPerfData())
  201.         return(FALSE);
  202.  
  203.     // set up our 10 sec. (10,000 msec) timer callback
  204.     if (!SetTimer(hFrame, 1, 10000, (WNDPROC) TimerProc))
  205.         return(FALSE);
  206.  
  207.     ShowWindow(hFrame, nCmdShow);           // make frame window visible
  208.  
  209.     // initialize current process name, display process list
  210.     szCurProcess[0] = 0;
  211.     SendMessage(hFrame, WM_COMMAND, IDM_REFRESH, 0);  
  212.  
  213.     return(TRUE);                           // return success flag
  214. }
  215.  
  216. //
  217. // TermApp -- centralized application clean-up code
  218. //            which does nothing in this particular case
  219. //
  220. BOOL TermApp(HANDLE hinstance)
  221. {
  222.     return(TRUE);                           // return success flag
  223. }
  224.  
  225. //
  226. // FrameWndProc --- callback function for application frame window.
  227. // Searches frameMsgs[] for message match, runs corresponding function.
  228. //
  229. LONG CALLBACK FrameWndProc(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  230. {
  231.     int i;                                  // scratch variable
  232.  
  233.     for(i = 0; i < dim(frameMsgs); i++)     // decode window message and
  234.     {                                       // run corresponding function
  235.         if(wMsg == frameMsgs[i].Code)
  236.             return((*frameMsgs[i].Fxn)(hWnd, wMsg, wParam, lParam));
  237.     }
  238.  
  239.     return(DefWindowProc(hWnd, wMsg, wParam, lParam));
  240. }
  241.  
  242. //
  243. // DoCommand -- process WM_COMMAND message for frame window by
  244. // decoding the menubar item with the menuitems[] array, then
  245. // running the corresponding function to process the command.
  246. // 
  247. LONG DoCommand(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  248. {
  249.     int i;                                  // scratch variables
  250.     int iSel;
  251.     PPERFINSTANCE pCurInst;
  252.  
  253.     for(i = 0; i < dim(menuitems); i++)     // decode menu command and
  254.     {                                       // run corresponding function
  255.         if(wParam == menuitems[i].Code)
  256.             return((*menuitems[i].Fxn)(hWnd, wMsg, wParam, lParam));
  257.     }
  258.  
  259.     // Check for user's selection of a process name in the left listbox.
  260.     // If new selection, display counters for process. Otherwise, 
  261.     // pass message to DefWindowProc().
  262.     if(((HWND) lParam == hLeft) && (HIWORD(wParam) == LBN_SELCHANGE))
  263.     {
  264.         // retrieve selected process name from left listbox
  265.         iSel = SendMessage(hLeft, LB_GETCURSEL, 0, 0);
  266.         SendMessage(hLeft, LB_GETTEXT, iSel, (LONG) szCurProcess);
  267.         
  268.         // find instance data for this process name and display it
  269.         pCurInst = FindProcess(szCurProcess);
  270.         ShowCounters(pCurInst);
  271.         return(FALSE);
  272.     }
  273.     else
  274.         return(DefWindowProc(hWnd, wMsg, wParam, lParam));
  275. }
  276.  
  277. //
  278. // DoDestroy -- process WM_DESTROY message for frame window.
  279. // 
  280. LONG DoDestroy(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  281. {
  282.     PostQuitMessage(0);                     // force WM_QUIT message to
  283.     return(0);                              // terminate the event loop
  284. }
  285.  
  286. //
  287. // DoClose -- process WM_CLOSE message for frame window.
  288. // 
  289. LONG DoClose(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  290. {
  291.     UpdateProfile();                        // save window size & position
  292.     DestroyWindow(hWnd);                    // then close down app
  293.     return(FALSE);                              
  294. }
  295.  
  296. //
  297. // DoCreate -- process WM_CREATE message for frame window.  Create two
  298. // listbox controls that are tiled vertically inside the frame window.
  299. // These controls will be used as child windows for listing of processes
  300. // and their counters.
  301. //
  302. LONG DoCreate(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  303. {
  304.     // create left-hand child window as a listbox control
  305.     hLeft = CreateWindow(        
  306.                 "listbox",                  // window class name
  307.                 NULL,                       // text for title bar
  308.                 WS_CHILD | WS_VISIBLE |     // window style
  309.                     WS_VSCROLL | WS_BORDER | LBS_NOTIFY | LBS_DISABLENOSCROLL,    
  310.                 0, 0, 0, 0,                 // position and size
  311.                 hWnd,                       // frame window is parent 
  312.                 0,                          // child window identifier
  313.                 hInst,                      // window owner
  314.                 NULL);                      // unused pointer
  315.  
  316.     // create right-hand child window as a listbox control
  317.     hRight = CreateWindow(        
  318.                 "listbox",                  // window class name
  319.                 NULL,                       // text for title bar
  320.                 WS_CHILD | WS_VISIBLE |     // window style
  321.                     WS_VSCROLL | WS_BORDER | LBS_NOTIFY | LBS_DISABLENOSCROLL,    
  322.                 0, 0, 0, 0,                 // position and size
  323.                 hWnd,                       // frame window is parent 
  324.                 0,                          // child window identifier
  325.                 hInst,                      // window owner
  326.                 NULL);                      // unused pointer
  327.  
  328.     return(FALSE);
  329. }
  330.  
  331. //
  332. // DoSize -- process WM_SIZE message for frame window.  Recalculate
  333. // lines per page, if window has grown and at end of file may need to 
  334. // change first line in window and refresh it.
  335. //
  336. LONG DoSize(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  337. {
  338.     INT i, j;
  339.     INT NonClientY;
  340.     RECT rect;
  341.  
  342.     // if the new window client height is not an integral multiple of the
  343.     // character height, adjust it so that the listboxes will fit neatly.
  344.     if(HIWORD(lParam) % CharY)
  345.     {
  346.         NonClientY = GetSystemMetrics(SM_CYCAPTION) +   // caption bar
  347.                      (GetSystemMetrics(SM_CYFRAME)*2) + // sizable frame
  348.                      GetSystemMetrics(SM_CYMENU);       // menu bar
  349.  
  350.         GetWindowRect(hWnd, &rect);
  351.         i = (HIWORD(lParam) / CharY) * CharY;
  352.         MoveWindow(hWnd, rect.left, rect.top, rect.right-rect.left,
  353.                    i + NonClientY, TRUE);
  354.         return(FALSE);
  355.     }
  356.  
  357.     // make left window just wide enough for longest process name
  358.     i = (CharX * 10) + GetSystemMetrics(SM_CXVSCROLL);
  359.     j = LOWORD(lParam) - i;
  360.     MoveWindow(hLeft, 0, 0, i, HIWORD(lParam), TRUE);
  361.  
  362.     // resize right child window to use remainder of client area
  363.     MoveWindow(hRight, i, 0, j, HIWORD(lParam), TRUE);
  364.  
  365.     return(FALSE);
  366. }
  367.  
  368. //
  369. // DoMenuExit -- process File-Exit command from menu bar.
  370. // 
  371. LONG DoMenuExit(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  372. {
  373.     SendMessage (hWnd, WM_CLOSE, 0, 0L);    // send window close message    
  374.     return(FALSE);                          // to shut down the app
  375. }
  376.  
  377. //
  378. // DoRefresh -- rebuild the information for display according to
  379. // the currently selected display type, then refresh the window.
  380. // 
  381. LONG DoRefresh(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  382. {
  383.     EmptyLeftLines();                       // discard old output
  384.     EmptyRightLines();                       
  385.     szCurProcess[0] = 0;                    // no process selected
  386.  
  387.     // fetch fresh copy of system performance data block
  388.     GetPerfData();
  389.  
  390.     // display process list in the left listbox control
  391.     ShowProcesses();
  392.  
  393.     return(FALSE);
  394. }
  395.  
  396. //
  397. // DoMenuAbout -- process File-About command from menu bar.
  398. // 
  399. LONG DoMenuAbout(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  400. {
  401.     DialogBox(hInst, "AboutBox", hWnd, (WNDPROC) AboutDlgProc);         
  402.     return(FALSE);                              
  403. }
  404.  
  405. //
  406. // AboutDlgProc -- callback routine for About... dialog.  Basically
  407. // ignores all messages except for the OK button, which dismisses dialog.
  408. //
  409. BOOL CALLBACK AboutDlgProc (HWND hwnd, UINT msg, UINT wParam, LONG lParam)
  410. {
  411.     if((msg == WM_COMMAND) && (wParam == IDOK)) 
  412.         EndDialog(hwnd, 0);                 // if OK button, destroy dialog
  413.     else return(FALSE);                     // otherwise ignore message
  414. }
  415.  
  416. // 
  417. // ShowProcesses -- displays list of process instances from system registry
  418. //
  419. VOID ShowProcesses(VOID)
  420. {
  421.     char temp[256];
  422.     PPERFINSTANCE pCurInst;
  423.     PPERFCOUNTER pCurCounter;
  424.     INT iCurInst;
  425.     INT iTotInst;
  426.  
  427.     // bail out now if there is nothing to display
  428.     if(pProcessGroup == NULL)
  429.     {
  430.         MessageBox(hFrame, "No process object group found!", szAppName,
  431.                    MB_ICONSTOP | MB_OK);
  432.         exit(1);
  433.     }
  434.  
  435.     // calculate pointer to first process instance, get total number 
  436.     // of process instances from the process object group header.
  437.     pCurInst = (PPERFINSTANCE) ((PBYTE) pProcessGroup + 
  438.                pProcessGroup->DefinitionLength);
  439.     iTotInst = pProcessGroup->NumInstances;
  440.  
  441.     for(iCurInst = 0; iCurInst < iTotInst; iCurInst++)
  442.     {
  443.         // convert UNICODE process name to ASCIIZ and display it
  444.         wcstombs(temp, (LPWSTR) ((PBYTE) pCurInst + pCurInst->NameOffset),
  445.                  pCurInst->NameLength/sizeof (WCHAR));
  446.         AddLeftLine(temp);
  447.  
  448.         // advance to next process instance 
  449.         pCurCounter = (PPERFCOUNTER) ((PBYTE) pCurInst +
  450.                        pCurInst->ByteLength);
  451.         pCurInst = (PPERFINSTANCE) ((PBYTE) pCurCounter +
  452.                    pCurCounter->ByteLength);
  453.     }
  454. }
  455.  
  456. //
  457. // ShowCounters -- display counters in right child window in response
  458. // to selection of a process name in the left window.  This routine
  459. // is called when a WM_COMMAND message of type LBN_SELCHANGE is
  460. // received.   In order to keep the code simple, we just display the 
  461. // raw counter values along with the counter name and the counter type.
  462. // 
  463. VOID ShowCounters(PPERFINSTANCE pCurInst)
  464. {
  465.     char temp[256];
  466.     INT i, j;
  467.     PPERFCOUNTERDEF pCurCounterDef;
  468.     INT iTotCounters;                           
  469.     PUINT pCurCounter;
  470.  
  471.     EmptyRightLines();                      // discard old output
  472.  
  473.     // this routine might get called either at the time of process
  474.     // selection or on a timer message. If the pointer to process instance
  475.     // info is NULL, the process has disappeared since it was originally
  476.     // selected, and we need to refresh the process list.
  477.     if(pCurInst == NULL)                    
  478.     {
  479.         SendMessage(hFrame, WM_COMMAND, IDM_REFRESH, 0);  
  480.         return;
  481.     }
  482.  
  483.     // calculate pointer to first counter definition for process 
  484.     // objects, save number of counters per process instance 
  485.     pCurCounterDef = (PPERFCOUNTERDEF) ((PBYTE) pProcessGroup + 
  486.                      pProcessGroup->HeaderLength);
  487.     iTotCounters = pProcessGroup->NumCounters;
  488.  
  489.     // loop through counters and display each one in raw form
  490.     for(i = 0; i < iTotCounters; i++)
  491.     {
  492.         // calculate address of counter value
  493.         pCurCounter = (PINT) ((PBYTE) pCurInst + pCurInst->ByteLength
  494.                       + pCurCounterDef->CounterOffset);
  495.  
  496.         j = FindCounterType(pCurCounterDef->CounterType);
  497.  
  498.         // format counter name, raw value, and type for display
  499.         switch(counterNames[j].Size)
  500.         {
  501.             case PERF_CTR_32BIT:
  502.                 wsprintf(temp, "%-32s         %08Xh  %s", 
  503.                          FindTitle(pCurCounterDef->CounterNameTitleIndex),
  504.                          *pCurCounter, 
  505.                          counterNames[j].Name);
  506.                 AddRightLine(temp);
  507.                 break;
  508.  
  509.             case PERF_CTR_64BIT:
  510.                 wsprintf(temp, "%-32s %08X%08Xh  %s", 
  511.                          FindTitle(pCurCounterDef->CounterNameTitleIndex),
  512.                          *(pCurCounter+1), *pCurCounter,
  513.                          counterNames[j].Name);
  514.                 AddRightLine(temp);
  515.                 break;
  516.  
  517.             case PERF_CTR_TEXT:
  518.             case PERF_CTR_NODATA:
  519.                 wsprintf(temp, "%-32s                    %s", 
  520.                          FindTitle(pCurCounterDef->CounterNameTitleIndex),
  521.                          counterNames[j].Name);
  522.                 AddRightLine(temp);
  523.                 break;
  524.  
  525.             default:
  526.                 break;
  527.         }
  528.  
  529.         // advance to next counter definition
  530.         pCurCounterDef = (PPERFCOUNTERDEF) ((PBYTE) pCurCounterDef + 
  531.                          pCurCounterDef->ByteLength);
  532.     }
  533. }
  534.  
  535. //
  536. // AddLeftLine -- called with a pointer to an ASCIIZ string,
  537. // adds the string to the left (process) listbox.
  538. //
  539. VOID AddLeftLine(char * p)
  540. {
  541.     SendMessage(hLeft, LB_ADDSTRING, 0, (LONG) p);
  542. }
  543.  
  544. //
  545. // EmptyLeftLines - clears out the process instance listbox.
  546. //
  547. VOID EmptyLeftLines(VOID)
  548. {
  549.     SendMessage(hLeft, LB_RESETCONTENT, 0, 0);
  550. }
  551.  
  552. //
  553. // AddRightLine -- called with a pointer to an ASCIIZ string, 
  554. // adds the string to the right (counter) listbox.
  555. //
  556. VOID AddRightLine(char * p)
  557. {
  558.     SendMessage(hRight, LB_ADDSTRING, 0, (LONG) p);
  559. }
  560.  
  561. //
  562. // EmptyRightLines - clears out the process counter listbox.
  563. //
  564. VOID EmptyRightLines(VOID)
  565. {
  566.     SendMessage(hRight, LB_RESETCONTENT, 0, 0);
  567. }
  568.  
  569. //
  570. // UpdateProfile() --  saves the current window size and position
  571. // and display type in the application's private INI file.
  572. //
  573. VOID UpdateProfile(VOID)
  574. {
  575.     RECT rect;
  576.     char temp[20];
  577.  
  578.     if(IsIconic(hFrame) || IsZoomed(hFrame)) return;
  579.  
  580.     GetWindowRect(hFrame, &rect);           
  581.  
  582.     wsprintf(temp,"%d", rect.left);
  583.     WritePrivateProfileString("Frame", "xul", temp, szIni);
  584.  
  585.     wsprintf(temp,"%d", rect.top);
  586.     WritePrivateProfileString("Frame", "yul", temp, szIni);
  587.  
  588.     wsprintf(temp,"%d", rect.right);
  589.     WritePrivateProfileString("Frame", "xlr", temp, szIni);
  590.  
  591.     wsprintf(temp,"%d", rect.bottom);
  592.     WritePrivateProfileString("Frame", "ylr", temp, szIni);
  593. }
  594.  
  595. //
  596. // TimerProc() -- Callback for 10 second timer. Refresh display
  597. // of counters for currently selected process else refresh the list
  598. // of process names.
  599. // 
  600. WORD CALLBACK TimerProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
  601. {
  602.     PPERFINSTANCE pCurInst;
  603.  
  604.     // fetch fresh copy of system performance data block
  605.     GetPerfData();
  606.  
  607.     // if any process has been selected, just refresh counter display,
  608.     // otherwise refresh the list of process names.
  609.     if(szCurProcess[0])
  610.     {
  611.         pCurInst = FindProcess(szCurProcess);
  612.         ShowCounters(pCurInst);
  613.     }
  614.     else SendMessage(hFrame, WM_COMMAND, IDM_REFRESH, 0); 
  615.  
  616.     return(FALSE);                          
  617. }
  618.  
  619. //
  620. // GetPerfData() - obtain performance data block from the 
  621. // system registry.  The size of the data cannot be known in advance
  622. // so the buffer is expanded incrementally until it is big enough.
  623. //
  624. BOOL GetPerfData(VOID)
  625. {  
  626.     INT iCurGroup;
  627.     PPERFGROUP pCurGroup;
  628.     DWORD dwBufferSize = dwPerfDataLen;
  629.  
  630.     while(ERROR_MORE_DATA == 
  631.           RegQueryValueEx(HKEY_PERFORMANCE_DATA, "Global", 
  632.             NULL, NULL, (PSTR) pPerfData, &dwBufferSize))
  633.     {
  634.  
  635.         dwPerfDataLen += MALLOCINCR;
  636.         dwBufferSize = dwPerfDataLen;
  637.         pPerfData = (PPERFDATA) realloc(pPerfData, dwPerfDataLen);
  638.  
  639.         if(pPerfData == NULL)
  640.         {
  641.             MessageBox(hFrame, "GetPerfData malloc failed!", szAppName,
  642.                 MB_ICONSTOP | MB_OK);
  643.             return(FALSE);
  644.         }
  645.     }
  646.  
  647.     // point to first object group within the data block and
  648.     // save total number of object groups
  649.     pFirstGroup = (PPERFGROUP) ((PBYTE) pPerfData + pPerfData->HeaderLength);
  650.     iTotGroups = pPerfData->NumObjectTypes;
  651.  
  652.     // point to the first group of objects
  653.     pCurGroup = pFirstGroup;
  654.  
  655.     // look up titles for each object type and save pointer
  656.     // within the object group's header structure
  657.     for(iCurGroup = 0; iCurGroup < iTotGroups; iCurGroup++)
  658.     {
  659.         pCurGroup->ObjectNameTitle = (LPWSTR)
  660.             FindTitle(pCurGroup->ObjectNameTitleIndex);
  661.  
  662.         // advance to next group of objects
  663.         pCurGroup = (PPERFGROUP) ((PBYTE) pCurGroup + 
  664.                     pCurGroup->TotalByteLength);
  665.     }
  666.  
  667.     // find process object group within the performance data
  668.     pProcessGroup = FindGroup("Process");
  669.  
  670.     return(TRUE);
  671. }  
  672.  
  673. //
  674. // GetObjectTitles() - retrieve titles for each of the object
  675. // types from the system registry.  The retrieved data, which is
  676. // referred to as the title database, is in the form of a series of
  677. // pairs of ASCIIZ strings.  The first string of a pair is the object
  678. // type index in decimal, the second string is the title.  The entire 
  679. // set of strings is terminated by an extra null byte.
  680. //
  681. BOOL GetObjectTitles(VOID)
  682. {
  683.     HKEY hKey;
  684.     char  chClass[10];
  685.     DWORD dwType;
  686.     DWORD cSubKeys;    
  687.     DWORD cbMaxSubkey; 
  688.     DWORD cbMaxClass;  
  689.     DWORD cValues; 
  690.     DWORD cbMaxValueName;
  691.     DWORD cbMaxValueData;  
  692.     DWORD cbSecurityDescriptor;    
  693.     DWORD cbClassSize = 10 ; // sizeof(chClass);
  694.     FILETIME ftLastWriteTime;
  695.  
  696.     // get handle for subkey holding object type indexes and titles
  697.     if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  698.         "Software\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\409", 
  699.         0, KEY_QUERY_VALUE, &hKey))
  700.     {
  701.         MessageBox(hFrame, "RegOpenKeyEx failed!", szAppName,
  702.             MB_ICONSTOP | MB_OK);
  703.         return(FALSE);
  704.     }
  705.  
  706.     // get maximum size of value data for values reached thru this subkey
  707.     if(ERROR_SUCCESS != RegQueryInfoKey(hKey, chClass, &cbClassSize, NULL, 
  708.         &cSubKeys, &cbMaxSubkey, &cbMaxClass, &cValues, &cbMaxValueName, 
  709.         &cbMaxValueData, &cbSecurityDescriptor, &ftLastWriteTime))
  710.     {
  711.         MessageBox(hFrame, "RegQueryInfoKey failed!", szAppName,
  712.             MB_ICONSTOP | MB_OK);
  713.         RegCloseKey(hKey);
  714.         return(FALSE);
  715.     }
  716.  
  717.     // bump maximum data size value for safety
  718.     cbMaxValueData++;
  719.  
  720.     // allocate memory to hold the incoming data
  721.     pTitles = malloc(cbMaxValueData * sizeof(TCHAR));
  722.  
  723.     if(pTitles == NULL)
  724.     {
  725.         MessageBox(hFrame, "GetObjectTitles malloc failed!", szAppName,
  726.             MB_ICONSTOP | MB_OK);
  727.         RegCloseKey(hKey);
  728.         return(FALSE);
  729.     }
  730.  
  731.     // now retrieve the index and title data
  732.     if(ERROR_SUCCESS != RegQueryValueEx(hKey, "Counters", NULL, &dwType,
  733.         (LPBYTE) pTitles, &cbMaxValueData))
  734.     {
  735.         MessageBox(hFrame, "RegQueryValueEx failed!", szAppName,
  736.             MB_ICONSTOP | MB_OK);
  737.         RegCloseKey(hKey);
  738.         return(FALSE);
  739.     }
  740.  
  741.     // release the handle for the subkey
  742.     RegCloseKey(hKey);
  743.     return(TRUE);
  744. }
  745.  
  746. // 
  747. // FindTitle() -- look up the object type index in the title database 
  748. // that was read by GetObjectTitles().  Return a pointer to the title
  749. // string if a match is found, otherwise return a pointer to "Unknown".
  750. // 
  751. PSTR FindTitle(INT TitleIndex)
  752. {
  753.     INT i;                                  // scratch object index
  754.     PSTR p = pTitles;                       // start of title database
  755.  
  756.     while(*p)                               
  757.     {
  758.         i = atoi(p);                        // convert index string
  759.         p += strlen(p) + 1;                 // point to title string
  760.         
  761.         if(i == TitleIndex)                 // is this desired index?
  762.             return(p);                      // yes, return addr of title
  763.  
  764.         p += strlen(p) + 1;                 // no, go to next index
  765.     }
  766.  
  767.     return("Unknown");                      // no match was found
  768. }
  769.  
  770. // 
  771. // FindGroup() -- Searches for the object group which has the
  772. // specified title.  Returns pointer to the beginning of the group's
  773. // instance storage if match is found, otherwise a NULL pointer.
  774. //
  775. PPERFGROUP FindGroup(PSTR GroupName)
  776. {
  777.     INT iCurGroup;
  778.     PPERFGROUP pCurGroup;
  779.  
  780.     // point to the first group of objects
  781.     pCurGroup = pFirstGroup;
  782.  
  783.     // compare title for each object type against supplied string
  784.     for(iCurGroup = 0; iCurGroup < iTotGroups; iCurGroup++)
  785.     {
  786.         // if titles match, return pointer to first instance
  787.         if(!strcmp((PSTR) pCurGroup->ObjectNameTitle, GroupName))
  788.             return(pCurGroup);
  789.  
  790.         // advance to next group of objects
  791.         pCurGroup = (PPERFGROUP) ((PBYTE) pCurGroup + 
  792.                     pCurGroup->TotalByteLength);
  793.     }
  794.  
  795.     return(NULL);                           // no match was found
  796. }
  797.  
  798. // 
  799. // FindCounterType() -- look up the magic number for the counter type 
  800. // in the structure counterNames[], return an index to the structure.
  801. // Returns 0 if no match.
  802. // 
  803. INT FindCounterType(DWORD CounterType)
  804. {
  805.     INT i;                                  
  806.  
  807.     for(i = 0; i < dim(counterNames); i++)  // look up counter type
  808.     {
  809.         if(counterNames[i].Code == CounterType)
  810.             return(i);                      // match was found
  811.     }
  812.  
  813.     return(0);                              // no match was found
  814. }
  815.  
  816. //
  817. // FindProcess() -- find process instance by name, return pointer.
  818. // Assumes that pProcessGroup was already set by GetPerfData().
  819. //
  820. PPERFINSTANCE FindProcess(PSTR szProcessName)
  821. {
  822.     PPERFINSTANCE pCurInst;
  823.     PPERFCOUNTER pCurCounter;
  824.     INT iCurInst;
  825.     INT iTotInst;
  826.     char temp[256];
  827.  
  828.     // calculate pointer to first process instance, extract total number 
  829.     // of instances from the header for the process object group
  830.     pCurInst = (PPERFINSTANCE) ((PBYTE) pProcessGroup + 
  831.                     pProcessGroup->DefinitionLength);
  832.     iTotInst = pProcessGroup->NumInstances;
  833.  
  834.     for(iCurInst = 0; iCurInst < iTotInst; iCurInst++)
  835.     {
  836.         // convert UNICODE process name to ASCIIZ, compare it to target,
  837.         // if they match return pointer to process instance data
  838.         wcstombs(temp, (LPWSTR) ((PBYTE) pCurInst + pCurInst->NameOffset),
  839.                  pCurInst->NameLength/sizeof (WCHAR));
  840.         if(!strcmp(temp, szProcessName))
  841.             return(pCurInst);
  842.  
  843.         // advance to next process instance 
  844.         pCurCounter = (PPERFCOUNTER) ((PBYTE) pCurInst +
  845.                        pCurInst->ByteLength);
  846.         pCurInst = (PPERFINSTANCE) ((PBYTE) pCurCounter +
  847.                    pCurCounter->ByteLength);
  848.     }
  849.  
  850.     // search failed, return null pointer as error indicator
  851.     return(NULL);
  852. }
  853.  
  854.  
  855.