home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / begin / generic / generic.c < prev    next >
C/C++ Source or Header  |  1996-07-21  |  23KB  |  681 lines

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