LEAKYBIN.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:23k
源码类别:

Windows编程

开发平台:

Visual C++

  1. //THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. //ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. //THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright 1996 - 1997 Microsoft Corporation.  All Rights Reserved.
  7. //
  8. // PROGRAM: Leakybin.c
  9. //
  10. // PURPOSE: Illustrates the use of Windows NT application performance
  11. //              counters to measure GlobalAlloc usage
  12. //
  13. // PLATFORMS:  Windows NT only
  14. //
  15. // FUNCTIONS:
  16. //    WinMain() - calls initialization function, processes message loop
  17. //    InitApplication() - Initializes window data nd registers window
  18. //    InitInstance() -saves instance handle and creates main window
  19. //    WindProc() Processes messages
  20. //    About() - Process menssages for "About" dialog box
  21. //    MyRegisterClass() - Registers the application's window class
  22. //    CenterWindow() -  Centers one window over another
  23. //
  24. // SPECIAL INSTRUCTIONS: N/A
  25. //
  26. #define APPNAME "Leakybin"
  27. // Windows Header Files:
  28. #include <windows.h>
  29. // C RunTime Header Files
  30. #include <stdlib.h>
  31. #include <malloc.h>
  32. #include <memory.h>
  33. #include <stdio.h>
  34. // Local Header Files
  35. #include "leakybin.h"
  36. // Makes it easier to determine appropriate code paths:
  37. #define IS_WIN32    TRUE
  38. #define IS_NT       IS_WIN32 && (BOOL)(GetVersion() < 0x80000000)
  39. // Global Variables:
  40. HINSTANCE hInst;      // current instance
  41. char szAppName[100];  // Name of the app
  42. char szTitle[100];    // The title bar text
  43. static int      TimerID = 0;
  44. static BOOL     TimerRunning = FALSE;
  45. static HMENU    hAppMenu, hTestMenu;
  46. static MEMORY_ALLOC_BLOCK   mabListHead = {NULL};
  47. // Foward declarations of functions included in this code module:
  48. ATOM MyRegisterClass(CONST WNDCLASS*);
  49. BOOL InitApplication(HINSTANCE);
  50. BOOL InitInstance(HINSTANCE, int);
  51. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  52. LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
  53. BOOL CenterWindow (HWND, HWND);
  54. LPTSTR   GetStringRes (int id);
  55. //
  56. //  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
  57. //
  58. //  PURPOSE: Entry point for the application.
  59. //
  60. //  COMMENTS:
  61. //
  62. // This function initializes the application and processes the
  63. // message loop.
  64. //
  65. int APIENTRY WinMain(HINSTANCE hInstance,
  66.                      HINSTANCE hPrevInstance,
  67.                      LPSTR     lpCmdLine,
  68.                      int       nCmdShow)
  69. {
  70.    MSG msg;
  71.    HANDLE hAccelTable;
  72.    // Initialize global strings
  73.    lstrcpy (szAppName, APPNAME);
  74.    LoadString (hInstance, IDS_APP_TITLE, szTitle, 100);
  75.    if (!hPrevInstance) {
  76.       // Perform instance initialization:
  77.       if (!InitApplication(hInstance)) {
  78.          return (FALSE);
  79.       }
  80.    }
  81.    // Perform application initialization:
  82.    if (!InitInstance(hInstance, nCmdShow)) {
  83.       return (FALSE);
  84.    }
  85.    hAccelTable = LoadAccelerators (hInstance, szAppName);
  86.    // Main message loop:
  87.    while (GetMessage(&msg, NULL, 0, 0)) {
  88.       if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg)) {
  89.          TranslateMessage(&msg);
  90.          DispatchMessage(&msg);
  91.       }
  92.    }
  93.    return (msg.wParam);
  94.    lpCmdLine; // This will prevent 'unused formal parameter' warnings
  95. }
  96. //
  97. //  FUNCTION: MyRegisterClass(CONST WNDCLASS*)
  98. //
  99. //  PURPOSE: Registers the window class.
  100. //
  101. //  COMMENTS:
  102. //
  103. //    This function and its usage is only necessary if you want this code
  104. //    to be compatible with Win32 systems prior to the 'RegisterClassEx'
  105. // function that was added to Windows 95. It is important to call this function
  106. //    so that the application will get 'well formed' small icons associated
  107. //    with it.
  108. //
  109. ATOM MyRegisterClass(CONST WNDCLASS *lpwc)
  110. {
  111.    HANDLE  hMod;
  112.    FARPROC proc;
  113.    WNDCLASSEX wcex;
  114.    hMod = GetModuleHandle ("USER32");
  115.    if (hMod != NULL) {
  116. #if defined (UNICODE)
  117.       proc = GetProcAddress (hMod, "RegisterClassExW");
  118. #else
  119.       proc = GetProcAddress (hMod, "RegisterClassExA");
  120. #endif
  121.       if (proc != NULL) {
  122.          wcex.style         = lpwc->style;
  123.          wcex.lpfnWndProc   = lpwc->lpfnWndProc;
  124.          wcex.cbClsExtra    = lpwc->cbClsExtra;
  125.          wcex.cbWndExtra    = lpwc->cbWndExtra;
  126.          wcex.hInstance     = lpwc->hInstance;
  127.          wcex.hIcon         = lpwc->hIcon;
  128.          wcex.hCursor       = lpwc->hCursor;
  129.          wcex.hbrBackground = lpwc->hbrBackground;
  130.                      wcex.lpszMenuName  = lpwc->lpszMenuName;
  131.          wcex.lpszClassName = lpwc->lpszClassName;
  132.          // Added elements for Windows 95:
  133.          wcex.cbSize = sizeof(WNDCLASSEX);
  134.          wcex.hIconSm = LoadIcon(wcex.hInstance, "SMALL");
  135.          return (*proc)(&wcex);//return RegisterClassEx(&wcex);
  136.       }
  137.    }
  138.    return (RegisterClass(lpwc));
  139. }
  140. //
  141. //  FUNCTION: InitApplication(HANDLE)
  142. //
  143. //  PURPOSE: Initializes window data and registers window class
  144. //
  145. //  COMMENTS:
  146. //
  147. //       In this function, we initialize a window class by filling out a data
  148. //       structure of type WNDCLASS and calling either RegisterClass or
  149. //       the internal MyRegisterClass.
  150. //
  151. BOOL InitApplication(HINSTANCE hInstance)
  152. {
  153.     WNDCLASS  wc;
  154.     HWND      hwnd;
  155.     // Fill in window class structure with parameters that describe
  156.     // the main window.
  157.     wc.style         = CS_HREDRAW | CS_VREDRAW;
  158.     wc.lpfnWndProc   = (WNDPROC)WndProc;
  159.     wc.cbClsExtra    = 0;
  160.     wc.cbWndExtra    = 0;
  161.     wc.hInstance     = hInstance;
  162.     wc.hIcon         = LoadIcon (hInstance, szAppName);
  163.     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  164.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  165.     wc.lpszMenuName  = szAppName;
  166.     wc.lpszClassName = szAppName;
  167.     // Register the window class and return success/failure code.
  168.     return RegisterClass(&wc);
  169. }
  170. //
  171. //   FUNCTION: InitInstance(HANDLE, int)
  172. //
  173. //   PURPOSE: Saves instance handle and creates main window
  174. //
  175. //   COMMENTS:
  176. //
  177. //        In this function, we save the instance handle in a global variable and
  178. //        create and display the main program window.
  179. //
  180. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
  181. {
  182.    HWND hWnd;
  183.    hInst = hInstance; // Store instance handle in our global variable
  184.    hWnd = CreateWindow(szAppName, szTitle, WS_OVERLAPPEDWINDOW,
  185.       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
  186.       NULL, NULL, hInstance, NULL);
  187.    if (!hWnd) {
  188.       return (FALSE);
  189.    }
  190.    ShowWindow(hWnd, nCmdShow);
  191.    UpdateWindow(hWnd);
  192.    return (TRUE);
  193. }
  194. void FreeAllocatedMemory()
  195. {
  196.     {
  197.     PMEMORY_ALLOC_BLOCK pNextMab, pMab;
  198.     pMab = mabListHead.pNext;
  199.     while (pMab != NULL) {
  200.     pNextMab = pMab->pNext;
  201.     G_FREE (pMab);
  202.     pMab = pNextMab;
  203.     }
  204.     mabListHead.pNext = NULL;
  205.     }
  206. }
  207. //
  208. //  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
  209. //
  210. //  PURPOSE:  Processes messages for the main window.
  211. //
  212. //  MESSAGES:
  213. //
  214. // WM_COMMAND - process the application menu
  215. // WM_PAINT - Paint the main window
  216. // WM_DESTROY - post a quit message and return
  217. //    WM_DISPLAYCHANGE - message sent to Plug & Play systems when the display changes
  218. //    WM_RBUTTONDOWN - Right mouse click -- put up context menu here if appropriate
  219. //    WM_NCRBUTTONUP - User has clicked the right button on the application's system menu
  220. //
  221. //
  222. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  223. {
  224.    int wmId, wmEvent;
  225.    PAINTSTRUCT ps;
  226.    HDC hdc;
  227.       POINT pnt;
  228.    HMENU hMenu;
  229.       BOOL bGotHelp;
  230.    switch (message) {
  231.       case WM_CREATE:
  232.         // clear timer flags 
  233.         TimerID = 0;
  234.         TimerRunning = FALSE;
  235.         // enable "Start" menu selection
  236.         hAppMenu = GetMenu (hWnd);
  237.         hTestMenu  = GetSubMenu (hAppMenu, 1);
  238.         EnableMenuItem (hTestMenu, IDM_STOP, MF_BYCOMMAND | MF_GRAYED);
  239.         EnableMenuItem (hTestMenu, IDM_START, MF_BYCOMMAND | MF_ENABLED);
  240.         break;
  241.       case WM_COMMAND:
  242.          wmId    = LOWORD(wParam); // Remember, these are...
  243.          wmEvent = HIWORD(wParam); // ...different for Win32!
  244.          //Parse the menu selections:
  245.          switch (wmId) {
  246.             case IDM_EXIT:
  247.                DestroyWindow (hWnd);
  248.                break;
  249.             case IDM_START:
  250.                 if (!TimerRunning) {
  251.                     TimerID = SetTimer (hWnd, LEAK_TIMER, TIME_INTERVAL, NULL);
  252.                     if (TimerID != 0) {
  253.                         TimerRunning = TRUE;
  254.                         EnableMenuItem (hTestMenu, IDM_START,
  255.                             MF_BYCOMMAND | MF_GRAYED);
  256.                         EnableMenuItem (hTestMenu, IDM_STOP,
  257.                             MF_BYCOMMAND | MF_ENABLED);
  258.                     } else {
  259.                         //unable to start timer
  260.                         MessageBeep (MB_ICONEXCLAMATION);
  261.                     }
  262.                 }
  263.                 InvalidateRect (hWnd, NULL, TRUE);
  264.                 break;
  265.             case IDM_STOP:
  266.                 if (TimerRunning) {
  267.                     KillTimer (hWnd, LEAK_TIMER);
  268.                     TimerID = 0;
  269.                     TimerRunning = FALSE;
  270.                     EnableMenuItem (hTestMenu, IDM_STOP,
  271.                         MF_BYCOMMAND | MF_GRAYED);
  272.                     EnableMenuItem (hTestMenu, IDM_START,
  273.                         MF_BYCOMMAND | MF_ENABLED);
  274.                 }
  275.                 InvalidateRect (hWnd, NULL, TRUE);
  276.                 break;
  277.             case IDM_RESET:
  278.                 FreeAllocatedMemory();
  279.                 InvalidateRect (hWnd, NULL, TRUE);
  280.                 break;
  281.             case IDM_ABOUT:
  282.                DialogBox(hInst, "AboutBox", hWnd, (DLGPROC)About);
  283.                break;
  284.             case IDM_HELPTOPICS: // Only called in Windows 95
  285.                bGotHelp = WinHelp (hWnd, APPNAME".HLP", HELP_FINDER,(DWORD)0);
  286.                if (!bGotHelp)
  287.                {
  288.                   MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP),
  289.                               szAppName, MB_OK|MB_ICONHAND);
  290.                }
  291.                break;
  292.             default:
  293.                return (DefWindowProc(hWnd, message, wParam, lParam));
  294.          }
  295.          break;
  296.       case WM_TIMER:
  297.         {
  298.         PMEMORY_ALLOC_BLOCK pMab, pNewMab;
  299.         pNewMab = (PMEMORY_ALLOC_BLOCK)G_ALLOC (GPTR, ALLOCATION_SIZE);
  300.         if (pNewMab != NULL) {
  301.         // save this pointer 
  302.         pNewMab->pNext = NULL;
  303.         if (mabListHead.pNext == NULL) {
  304.         // this is the first entry
  305.         mabListHead.pNext = pNewMab;
  306.         } else {
  307.         // go to end of list
  308.         pMab = mabListHead.pNext;
  309.         while (pMab->pNext != NULL) pMab = pMab->pNext;
  310.         pMab->pNext = pNewMab;
  311.         }
  312.                 InvalidateRect (hWnd, NULL, TRUE);
  313.         }
  314.         }
  315.         break;
  316.       case WM_RBUTTONDOWN: // RightClick in windows client area...
  317.             pnt.x = LOWORD(lParam);
  318.             pnt.y = HIWORD(lParam);
  319.             ClientToScreen(hWnd, (LPPOINT) &pnt);
  320.       // This is where you would determine the appropriate 'context'
  321.       // menu to bring up. Since this app has no real functionality,
  322.       // we will just bring up the 'Help' menu:
  323.             hMenu = GetSubMenu (GetMenu (hWnd), 2);
  324.             if (hMenu) {
  325.                 TrackPopupMenu (hMenu, 0, pnt.x, pnt.y, 0, hWnd, NULL);
  326.             } else {
  327.             // Couldn't find the menu...
  328.                 MessageBeep(0);
  329.             }
  330.             break;
  331.       case WM_DISPLAYCHANGE: // Only comes through on plug'n'play systems
  332.       {
  333.          SIZE  szScreen;
  334.          DWORD dwBitsPerPixel = (DWORD)wParam;
  335.          szScreen.cx = LOWORD(lParam);
  336.          szScreen.cy = HIWORD(lParam);
  337.          MessageBox (GetFocus(), GetStringRes(IDS_DISPLAYCHANGED),
  338.                      szAppName, 0);
  339.       }
  340.       break;
  341.       case WM_PAINT:
  342.         {
  343.         MEMORYSTATUS MemoryStatusData;
  344.             LONGLONG llInUse;
  345.         DWORD dwPercentUsed;
  346.             int     nX, nY;
  347.             LONG    lTextOutReturn;
  348.             int     nStringLength;
  349.             CHAR            szOutputString[100];
  350.             hdc = BeginPaint (hWnd, &ps);
  351.             // Add any drawing code here...
  352.         GlobalMemoryStatus (&MemoryStatusData);
  353.         llInUse = (LONGLONG)(MemoryStatusData.dwTotalPageFile -
  354.                 MemoryStatusData.dwAvailPageFile + 5 );
  355.         llInUse *= 1000;
  356.         llInUse /= MemoryStatusData.dwTotalPageFile;
  357.         llInUse /= 10;
  358.         dwPercentUsed = (DWORD)llInUse;
  359.             nX = 0;
  360.             nY = 0;
  361.             sprintf (szOutputString, "Reported Memory Load: t%3.1d%%",
  362.                 MemoryStatusData.dwMemoryLoad);
  363.             nStringLength = lstrlen (szOutputString) * sizeof (CHAR);
  364.             lTextOutReturn = TabbedTextOut (hdc, nX, nY,
  365.                 szOutputString, nStringLength, 0, NULL, 0);
  366.             nY += HIWORD (lTextOutReturn);
  367.             sprintf (szOutputString, "Page file in use:  t%3.1d%%",
  368.                 dwPercentUsed);
  369.             nStringLength = lstrlen (szOutputString) * sizeof (CHAR);
  370.             lTextOutReturn = TabbedTextOut (hdc, nX, nY,
  371.                 szOutputString, nStringLength, 0, NULL, 0);
  372.             nY += HIWORD (lTextOutReturn);
  373.             EndPaint (hWnd, &ps);
  374.         }
  375.         break;
  376.       case WM_DESTROY:
  377.          FreeAllocatedMemory();
  378.          // Tell WinHelp we don't need it any more...
  379.                WinHelp (hWnd, APPNAME".HLP", HELP_QUIT,(DWORD)0);
  380.          PostQuitMessage(0);
  381.          break;
  382.       default:
  383.          return (DefWindowProc(hWnd, message, wParam, lParam));
  384.    }
  385.    return (0);
  386. }
  387. //
  388. //  FUNCTION: About(HWND, unsigned, WORD, LONG)
  389. //
  390. //  PURPOSE:  Processes messages for "About" dialog box
  391. //       This version allows greater flexibility over the contents of the 'About' box,
  392. //       by pulling out values from the 'Version' resource.
  393. //
  394. //  MESSAGES:
  395. //
  396. // WM_INITDIALOG - initialize dialog box
  397. // WM_COMMAND    - Input received
  398. //
  399. //
  400. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  401. {
  402.    static  HFONT hfontDlg;    // Font for dialog text
  403.    static   HFONT hFinePrint; // Font for 'fine print' in dialog
  404.    DWORD   dwVerInfoSize;     // Size of version information block
  405.    LPSTR   lpVersion;         // String pointer to 'version' text
  406.    DWORD   dwVerHnd=0;        // An 'ignored' parameter, always '0'
  407.    UINT    uVersionLen;
  408.    WORD    wRootLen;
  409.    BOOL    bRetCode;
  410.    int     i;
  411.    char    szFullPath[256];
  412.    char    szResult[256];
  413.    char    szGetName[256];
  414.    DWORD dwVersion;
  415.    char  szVersion[40];
  416.    DWORD dwResult;
  417.    switch (message) {
  418.         case WM_INITDIALOG:
  419.          ShowWindow (hDlg, SW_HIDE);
  420.          if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE)
  421.          {
  422.             hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, 0,
  423.                                   VARIABLE_PITCH | FF_DONTCARE, "");
  424.             hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, 0,
  425.                                     VARIABLE_PITCH | FF_DONTCARE, "");
  426.          }
  427.          else
  428.          {
  429.             hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  430.                                   VARIABLE_PITCH | FF_SWISS, "");
  431.             hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  432.                                     VARIABLE_PITCH | FF_SWISS, "");
  433.          }
  434.          CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
  435.          GetModuleFileName (hInst, szFullPath, sizeof(szFullPath));
  436.          // Now lets dive in and pull out the version information:
  437.          dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd);
  438.          if (dwVerInfoSize) {
  439.             LPSTR   lpstrVffInfo;
  440.             HANDLE  hMem;
  441.             hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize);
  442.             lpstrVffInfo  = GlobalLock(hMem);
  443.             GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpstrVffInfo);
  444.             // The below 'hex' value looks a little confusing, but
  445.             // essentially what it is, is the hexidecimal representation
  446.             // of a couple different values that represent the language
  447.             // and character set that we are wanting string values for.
  448.             // 040904E4 is a very common one, because it means:
  449.             //   US English, Windows MultiLingual characterset
  450.             // Or to pull it all apart:
  451.             // 04------        = SUBLANG_ENGLISH_USA
  452.             // --09----        = LANG_ENGLISH
  453.             // --11----        = LANG_JAPANESE
  454.             // ----04E4 = 1252 = Codepage for Windows:Multilingual
  455.             lstrcpy(szGetName, GetStringRes(IDS_VER_INFO_LANG));
  456.             wRootLen = lstrlen(szGetName); // Save this position
  457.             // Set the title of the dialog:
  458.             lstrcat (szGetName, "ProductName");
  459.             bRetCode = VerQueryValue((LPVOID)lpstrVffInfo,
  460.                (LPSTR)szGetName,
  461.                (LPVOID)&lpVersion,
  462.                (UINT *)&uVersionLen);
  463.             // Notice order of version and string...
  464.             if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE)
  465.             {
  466.                lstrcpy(szResult, lpVersion);
  467.                lstrcat(szResult, " 偺僶乕僕儑儞忣曬");
  468.             }
  469.             else
  470.             {
  471.                lstrcpy(szResult, "About ");
  472.                lstrcat(szResult, lpVersion);
  473.             }
  474.             // -----------------------------------------------------
  475.             SetWindowText (hDlg, szResult);
  476.             // Walk through the dialog items that we want to replace:
  477.             for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) {
  478.                GetDlgItemText(hDlg, i, szResult, sizeof(szResult));
  479.                szGetName[wRootLen] = (char)0;
  480.                lstrcat (szGetName, szResult);
  481.                uVersionLen   = 0;
  482.                lpVersion     = NULL;
  483.                bRetCode      =  VerQueryValue((LPVOID)lpstrVffInfo,
  484.                   (LPSTR)szGetName,
  485.                   (LPVOID)&lpVersion,
  486.                   (UINT *)&uVersionLen);
  487.                if ( bRetCode && uVersionLen && lpVersion) {
  488.                // Replace dialog item text with version info
  489.                   lstrcpy(szResult, lpVersion);
  490.                   SetDlgItemText(hDlg, i, szResult);
  491.                }
  492.                else
  493.                {
  494.                   dwResult = GetLastError();
  495.                   wsprintf(szResult, GetStringRes(IDS_VERSION_ERROR), dwResult);
  496.                   SetDlgItemText (hDlg, i, szResult);
  497.                }
  498.                SendMessage (GetDlgItem (hDlg, i), WM_SETFONT,
  499.                   (UINT)((i==DLG_VERLAST)?hFinePrint:hfontDlg),
  500.                   TRUE);
  501.             } // for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++)
  502.             GlobalUnlock(hMem);
  503.             GlobalFree(hMem);
  504.          } else {
  505.             // No version information available.
  506.          } // if (dwVerInfoSize)
  507.             SendMessage (GetDlgItem (hDlg, IDC_LABEL), WM_SETFONT,
  508.             (WPARAM)hfontDlg,(LPARAM)TRUE);
  509.          // We are  using GetVersion rather then GetVersionEx
  510.          // because earlier versions of Windows NT and Win32s
  511.          // didn't include GetVersionEx:
  512.          dwVersion = GetVersion();
  513.          if (dwVersion < 0x80000000) {
  514.             // Windows NT
  515.             wsprintf (szVersion, "Microsoft Windows NT %u.%u (Build: %u)",
  516.                (DWORD)(LOBYTE(LOWORD(dwVersion))),
  517.                (DWORD)(HIBYTE(LOWORD(dwVersion))),
  518.                     (DWORD)(HIWORD(dwVersion)) );
  519.          } else if (LOBYTE(LOWORD(dwVersion))<4) {
  520.             // Win32s
  521.                 wsprintf (szVersion, "Microsoft Win32s %u.%u (Build: %u)",
  522.                (DWORD)(LOBYTE(LOWORD(dwVersion))),
  523.                (DWORD)(HIBYTE(LOWORD(dwVersion))),
  524.                     (DWORD)(HIWORD(dwVersion) & ~0x8000) );
  525.          } else {
  526.             // Windows 95
  527.                 wsprintf (szVersion, "Microsoft Windows 95 %u.%u",
  528.                     (DWORD)(LOBYTE(LOWORD(dwVersion))),
  529.                     (DWORD)(HIBYTE(LOWORD(dwVersion))) );
  530.          }
  531.           SetWindowText (GetDlgItem(hDlg, IDC_OSVERSION), szVersion);
  532.          ShowWindow (hDlg, SW_SHOW);
  533.          return (TRUE);
  534.       case WM_COMMAND:
  535.          if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
  536.             EndDialog(hDlg, TRUE);
  537.             DeleteObject (hfontDlg);
  538.             DeleteObject (hFinePrint);
  539.             return (TRUE);
  540.          }
  541.          break;
  542.    }
  543.     return FALSE;
  544. }
  545. //
  546. //   FUNCTION: CenterWindow(HWND, HWND)
  547. //
  548. //   PURPOSE: Centers one window over another.
  549. //
  550. //   COMMENTS:
  551. //
  552. //        In this function, we save the instance handle in a global variable and
  553. //        create and display the main program window.
  554. //
  555. //       This functionwill center one window over another ensuring that
  556. //    the placement of the window is within the 'working area', meaning
  557. //    that it is both within the display limits of the screen, and not
  558. //    obscured by the tray or other framing elements of the desktop.
  559. BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
  560. {
  561.    RECT    rChild, rParent, rWorkArea;
  562.    int     wChild, hChild, wParent, hParent;
  563.    int     xNew, yNew;
  564.    BOOL  bResult;
  565.    // Get the Height and Width of the child window
  566.    GetWindowRect (hwndChild, &rChild);
  567.    wChild = rChild.right - rChild.left;
  568.    hChild = rChild.bottom - rChild.top;
  569.    // Get the Height and Width of the parent window
  570.    GetWindowRect (hwndParent, &rParent);
  571.    wParent = rParent.right - rParent.left;
  572.    hParent = rParent.bottom - rParent.top;
  573.    // Get the limits of the 'workarea'
  574.    bResult = SystemParametersInfo(
  575.       SPI_GETWORKAREA,  // system parameter to query or set
  576.       sizeof(RECT),
  577.       &rWorkArea,
  578.       0);
  579.    if (!bResult) {
  580.       rWorkArea.left = rWorkArea.top = 0;
  581.       rWorkArea.right = GetSystemMetrics(SM_CXSCREEN);
  582.       rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
  583.    }
  584.    // Calculate new X position, then adjust for workarea
  585.    xNew = rParent.left + ((wParent - wChild) /2);
  586.    if (xNew < rWorkArea.left) {
  587.       xNew = rWorkArea.left;
  588.    } else if ((xNew+wChild) > rWorkArea.right) {
  589.       xNew = rWorkArea.right - wChild;
  590.    }
  591.    // Calculate new Y position, then adjust for workarea
  592.    yNew = rParent.top  + ((hParent - hChild) /2);
  593.    if (yNew < rWorkArea.top) {
  594.       yNew = rWorkArea.top;
  595.    } else if ((yNew+hChild) > rWorkArea.bottom) {
  596.       yNew = rWorkArea.bottom - hChild;
  597.    }
  598.    // Set it, and return
  599.    return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
  600. }
  601. //---------------------------------------------------------------------------
  602. //
  603. // FUNCTION:    GetStringRes (int id INPUT ONLY)
  604. //
  605. // COMMENTS:    Load the resource string with the ID given, and return a
  606. //              pointer to it.  Notice that the buffer is common memory so
  607. //              the string must be used before this call is made a second time.
  608. //
  609. //---------------------------------------------------------------------------
  610. LPTSTR   GetStringRes (int id)
  611. {
  612.   static TCHAR buffer[MAX_PATH];
  613.   buffer[0]=0;
  614.   LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH);
  615.   return buffer;
  616. }