home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / gdi / complexscript / cstext.c next >
C/C++ Source or Header  |  1997-08-21  |  21KB  |  612 lines

  1. ////////////////////////////////////////////////////////////////////////
  2. //
  3. // CSTEXT
  4. // A sample application that shows how to display complex scripts such
  5. // as Arabic, Hebrew, Thai, or Indic scripts.
  6. //
  7. // This sample works best if executed on a platform enabled for complex 
  8. // scripts, such as Arabic, Hebrew, or Thai Windows NT 4.0 or Windows 95,
  9. // or Windows NT 5.0 with the appropriate locale installed.
  10. //
  11. // The WndProc function puts out text typed by the user to the client area
  12. // in two ways: 1) using ExtTextOut to put out each character as it 
  13. // is typed by the user, and 2) saving each character typed in a 
  14. // buffer and putting out the whole buffer using ExtTextOut.
  15. //
  16. // If you run the program on a system that supports complex scripts, such as
  17. // Arabic, Hebrew, or Thai Windows NT, and switch the keyboard to one of 
  18. // those languages, you will see different results in the two output lines.
  19. // For example, on Arabic Windows NT, the line of characters displayed one 
  20. // by one will show only the stand-alone Arabic characters, without joining, 
  21. // and without the proper bidirectional layout. The line displayed using 
  22. // the whole buffer will be shown correctly
  23. //
  24. // Choose the help menu for guidelines on programming for complex scripts.
  25. //
  26. // Written by F. Avery Bishop, with advice from David C. W. Brown.
  27. //
  28. // Copyright (c) 1997, Microsoft Corporation. All rights reserved.
  29. //
  30. /////////////////////////////////////////////////////////////////////////
  31.  
  32. #include "cstext.h"
  33. #include <stdarg.h>
  34. #include <windows.h>
  35. #include <tchar.h>
  36. #include "resource.h"
  37.  
  38. #ifndef UNICODE
  39. #pragma message("Warning: This sample works best as a Unicode application")
  40. #endif
  41.  
  42. // Global variables
  43. int         g_xStartOneChar = XSTART ;  // X position to display next character
  44. HINSTANCE   g_hInstance ;
  45. TCHAR       g_szTitle[MAX_MESSAGE] ;    // Title bar text
  46.  
  47. // Function Prototypes, in the order defined
  48. LRESULT CALLBACK WndProc         (HWND , UINT , WPARAM , LPARAM) ;
  49. BOOL CALLBACK    EditDialogProc  (HWND , UINT , WPARAM , LPARAM) ;
  50. BOOL CALLBACK    HelpDialogProc  (HWND , UINT , WPARAM , LPARAM) ;
  51. void             InitializeFont  (HWND , LONG, LPCHOOSEFONT , LPLOGFONT) ;
  52. BOOL             InitApplication (HINSTANCE , LPTSTR) ;
  53. BOOL             InitInstance    (HINSTANCE , LPTSTR , int) ;
  54. int              RcMessageBox    (HWND , int , int , ...) ;
  55.  
  56. //
  57. //   FUNCTION: WndProc (HWND, UINT, WPARAM, LPARAM)
  58. //
  59. //   PURPOSE: Window Procedure
  60. //
  61. //   COMMENTS:
  62. //          This function puts out text typed by the user to the client area
  63. //          in two ways: 1) using ExtTextOut to put out each character as it 
  64. //          is typed by the user, and 2) saving each character typed in a 
  65. //          buffer and putting out the whole buffer using ExtTextOut. 
  66. //
  67. LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  68. {
  69.     static HFONT    hTextFont ;        // Font for text typed by user
  70.     static TCHAR    szOutputBuffer[BUFFER_SIZE] ; // Buffer of characters typed
  71.     static int      nChars = 0 ;       // Current size of buffer
  72.     static RECT     rcBufferLine = {0,0,0,0} ;// Rectangle for the text to be displayed
  73.     static UINT     uiAlign = TA_LEFT ;// Alignment and reading order flag
  74.     static CHOOSEFONT cf ;             // This is static to keep last values as defaults
  75.     static LOGFONT  lf ;               // Same for this.
  76.     
  77.     PAINTSTRUCT     ps ;               // Standard paint structure
  78.     HDC             hDc ;              // HDC used in two ways. See WM_CHAR and WM_PAINT below
  79.     SIZE            sOneChar ;         // Used in GetTextExtentPoint32.
  80.     // Normally these should be in resources so they can be localized.
  81.     TCHAR           szCharLabel[] 
  82.                     = TEXT("Characters displayed one by one, as typed:") ;
  83.     TCHAR           szBuffLabel[] 
  84.                     = TEXT("All text in the line displayed in one call to ExtTextOut:") ;
  85.     TEXTMETRIC      tm ;
  86.     
  87.     int nxStartBuffer, nyStartBuffer ;  // x and y positions to display text.
  88.     int cCharWidths ;                   // Count of character widths, used in GetCharWidth32
  89.     
  90.     switch (message)
  91.     {
  92.     case WM_CREATE :
  93.         
  94.         InitializeFont (hWnd, 20, &cf, &lf) ; // Routine defined below
  95.         hTextFont = CreateFontIndirect (&lf) ;
  96.         
  97.         if (NULL == hTextFont) {
  98.             RcMessageBox (hWnd, IDS_CHOOSEFONT_FAILED, MB_ICONEXCLAMATION | MB_OK);
  99.             break ;
  100.         } ;
  101.         
  102.         return 0 ;
  103.         
  104.     case WM_CHAR :  // Process keyboard input
  105.         
  106.         switch ((TCHAR) wParam)
  107.         {
  108.         case VK_BACK :
  109.             
  110.             if (nChars > 0){
  111.                 nChars-- ;
  112.                 
  113.                 // Reset starting point for next character output
  114.                 hDc = GetDC(hWnd) ;
  115.                 SelectObject(hDc, hTextFont) ;
  116.                 GetTextExtentPoint32(hDc, szOutputBuffer, nChars, &sOneChar) ;
  117.                 ReleaseDC(hWnd, hDc) ;
  118.                 g_xStartOneChar = sOneChar.cx + XSTART;
  119.                 
  120.                 InvalidateRect (hWnd, NULL, TRUE) ;
  121.             }
  122.             break ;
  123.             
  124.         case VK_RETURN :    
  125.             
  126.             // This sample ignores return. Most apps will add CR/LF or exit.
  127.             break ;
  128.             
  129.         case VK_ESCAPE :
  130.         case VK_END :
  131.         case VK_HOME :
  132.             
  133.             // Processing other non-printable characters is left as an exercise
  134.             break ;
  135.             
  136.         default:
  137.             
  138.             // Process all normal characters
  139.             
  140.         // First use the old (incorrect) method to display only the last
  141.         // character typed
  142.         
  143.             // NOTE: This is an example of what *not* to do, because
  144.             // characters that should join or otherwise interact 
  145.             // typographically will show as separate, stand alone characters.
  146.             hDc = GetDC (hWnd) ;
  147.             SelectObject (hDc, hTextFont) ;
  148.             SetBkMode (hDc, TRANSPARENT) ;
  149.             ExtTextOut (hDc, g_xStartOneChar, YSTART, 0,
  150.                 NULL, (LPCTSTR) &wParam, 1, NULL) ;
  151.             
  152.             // Get the next character position
  153.             GetCharWidth (hDc, (UINT) wParam, (UINT) wParam, &cCharWidths) ;
  154.             // This assumes left to right scripts, so it will break on
  155.             // Arabic and Hebrew!
  156.             g_xStartOneChar += cCharWidths ; 
  157.  
  158.             ReleaseDC (hWnd, hDc) ;
  159.             
  160.         // Next, display the whole buffer of all characters typed in so far
  161.  
  162.             szOutputBuffer[nChars] = (TCHAR) wParam ;
  163.             if (nChars < BUFFER_SIZE-1) {
  164.                 nChars++ ;
  165.             }
  166.             // This will generate a WM_PAINT message. In the processing 
  167.             // of WM_PAINT below, we display the text buffer in the 
  168.             // rectangle rcBufferLine. This is the recommended approach.
  169.             InvalidateRect (hWnd, &rcBufferLine, TRUE) ;
  170.         }
  171.         
  172.         return 0 ;
  173.     
  174.     case WM_PAINT :
  175.         
  176.         hDc = BeginPaint (hWnd, &ps) ;
  177.         
  178.         SelectObject (hDc, hTextFont) ;
  179.         
  180.         // Get line positions for the current font
  181.         GetTextMetrics (hDc, &tm) ;
  182.         
  183.         nyStartBuffer = YSTART + 3*tm.tmHeight ;
  184.         
  185.         GetClientRect (hWnd, &rcBufferLine) ;
  186.         
  187.         rcBufferLine.top = nyStartBuffer ;
  188.         rcBufferLine.bottom = nyStartBuffer + tm.tmHeight ;
  189.         
  190.         // Set the x position for right or left aligned text
  191.         if (uiAlign & TA_RIGHT) {
  192.             nxStartBuffer = rcBufferLine.right - XSTART ;
  193.         } else {
  194.             nxStartBuffer = XSTART ;
  195.         }
  196.         
  197.         SetTextAlign (hDc, uiAlign) ;
  198.         
  199.         // Write the whole text buffer in the "character by character" rectangle
  200.         // Note how the complex script comes out right this time.
  201.         // This only happens when the whole client area is invalidated, i.e., 
  202.         // when the user types backspace, clears the buffer, or changes the 
  203.         // font, or when the window is covered and uncovered.
  204.         ExtTextOut (hDc, nxStartBuffer, YSTART, ETO_OPAQUE, 
  205.             NULL, szOutputBuffer, nChars, NULL) ;
  206.         
  207.         // Write the whole text buffer in the line buffer rectangle
  208.         // This happens every time the user enters a character, which 
  209.         // is why this line always looks right, even for complex scripts.
  210.         ExtTextOut (hDc, nxStartBuffer, nyStartBuffer, ETO_OPAQUE, 
  211.             &rcBufferLine, szOutputBuffer, nChars, NULL) ;
  212.         
  213.         // Write out labels describing the text
  214.         SelectObject(hDc, GetStockObject(ANSI_VAR_FONT)) ;
  215.         SetTextAlign(hDc, TA_LEFT) ;
  216.         GetTextMetrics (hDc, &tm) ;
  217.         
  218.         ExtTextOut (hDc, XSTART, YSTART-tm.tmHeight, ETO_OPAQUE, 
  219.             NULL, szCharLabel, lstrlen(szCharLabel), NULL) ;
  220.  
  221.         ExtTextOut (hDc, XSTART, nyStartBuffer-tm.tmHeight, ETO_OPAQUE, 
  222.             NULL, szBuffLabel, lstrlen(szBuffLabel), NULL) ;
  223.         
  224.         EndPaint (hWnd, &ps) ;
  225.         
  226.         return 0 ;
  227.         
  228.     case WM_COMMAND :
  229.         
  230.         switch (LOWORD(wParam)) 
  231.         {
  232.         case IDM_EDIT_CLEAR :
  233.             // Clear the character buffer
  234.             nChars = 0 ;
  235.             
  236.             // Reset starting point for next character output
  237.             g_xStartOneChar = XSTART ;
  238.             InvalidateRect (hWnd, NULL, TRUE) ;
  239.             
  240.             break ;
  241.             
  242.         case IDM_EDITCONTROL :
  243.             // Use an edit control to enter and display text.
  244.             // This is the recommended approach, because all
  245.             // complex script processing is handled by the system.
  246.             // Complex script support is also available in the 
  247.             // Richedit control (not shown here).
  248.             DialogBox (g_hInstance, MAKEINTRESOURCE(IDD_CSSAMPLE), NULL, EditDialogProc) ;
  249.             
  250.             break ;
  251.             
  252.         case IDM_EDIT_TOGGLEALIGN :
  253.             // This aligns the text to the left or right
  254.             // edge of the client area. It is NOT the same
  255.             // as reading order.
  256.             if (uiAlign & TA_RIGHT){
  257.                 uiAlign = uiAlign & ~TA_RIGHT ;
  258.             } else {
  259.                 uiAlign = uiAlign | TA_RIGHT & ~TA_LEFT ;
  260.             }
  261.             
  262.             InvalidateRect (hWnd, NULL, TRUE) ;
  263.             
  264.             break ;
  265.             
  266.         case IDM_EDIT_TOGGLEREADING :
  267.             // This will set the reading order as LTR, as is used
  268.             // for European and Asian scripts, or RTL, the most common
  269.             // reading area in Middle Eastern scripts such as Arabic
  270.             // and Hebrew.
  271.             uiAlign ^= TA_RTLREADING ;
  272.             InvalidateRect (hWnd, NULL, TRUE) ;
  273.             
  274.             break ;
  275.             
  276.         case IDM_EDIT_SETFONT :
  277.             // Let the user change font.
  278.             ChooseFont (&cf) ;
  279.             if (hTextFont){
  280.                 DeleteObject (hTextFont) ;
  281.             }
  282.             
  283.             hTextFont = CreateFontIndirect (&lf) ;
  284.             
  285.             if (NULL == hTextFont) {
  286.                 RcMessageBox (hWnd, IDS_CHOOSEFONT_FAILED, MB_ICONEXCLAMATION | MB_OK) ;
  287.                 break ;
  288.             }
  289.             
  290.             RcMessageBox(hWnd, IDS_FONTCHANGED , MB_OK, lf.lfFaceName) ;
  291.             
  292.             // Reset starting point for next character output
  293.             hDc = GetDC(hWnd) ;
  294.             SelectObject(hDc, hTextFont) ;
  295.             GetTextExtentPoint32(hDc, szOutputBuffer, nChars, &sOneChar) ;
  296.             ReleaseDC(hWnd, hDc) ;
  297.             g_xStartOneChar = sOneChar.cx + XSTART ;
  298.             
  299.             InvalidateRect (hWnd, NULL, TRUE) ;
  300.             
  301.             break ;
  302.             
  303.         case IDM_ABOUT_HELP :
  304.             
  305.             DialogBox (g_hInstance, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDialogProc) ;
  306.             
  307.             break;
  308.             
  309.         case IDM_ABOUT_ABOUT :
  310.             
  311.             DialogBox (g_hInstance, MAKEINTRESOURCE(IDD_ABOUT), NULL, HelpDialogProc);
  312.             
  313.             break ;
  314.             
  315.         case IDM_EXIT :
  316.             
  317.             DestroyWindow (hWnd) ;
  318.             
  319.             break ;
  320.             
  321.         default :
  322.             
  323.             return 0 ;  
  324.         }
  325.         
  326.         return 0 ;
  327.         
  328.         case WM_DESTROY :
  329.             
  330.             PostQuitMessage (0) ;
  331.             
  332.             return 0 ;
  333.             
  334.         default : 
  335.             return (DefWindowProc(hWnd, message, wParam, lParam)) ;
  336.    }
  337. }
  338.  
  339. //
  340. //   FUNCTION: BOOL CALLBACK EditDialogProc (HWND , UINT , WPARAM , LPARAM)
  341. //
  342. //   PURPOSE: Dialog procedure for the edit control dialog box.
  343. //
  344. //   COMMENTS:
  345. //        This is standard processing for edit controls.
  346. //
  347. BOOL CALLBACK EditDialogProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  348. {
  349.     static HFONT        hEditFont ;
  350.     static CHOOSEFONT   cf ; 
  351.     static LOGFONT      lf ;
  352.  
  353.     HWND                hWndEdit ;
  354.     
  355.     switch (uMsg)
  356.     {
  357.     case WM_INITDIALOG :
  358.         
  359.         InitializeFont (hDlg, 24, &cf, &lf) ;
  360.         hEditFont = CreateFontIndirect (&lf) ;
  361.         
  362.         // Set font of edit control
  363.         SendDlgItemMessage (hDlg, ID_EDITCONTROL, WM_SETFONT, 
  364.             (WPARAM) hEditFont,  MAKELPARAM(TRUE, 0)) ;
  365.         
  366.         return TRUE ;
  367.         
  368.     case WM_CLOSE :
  369.         
  370.         EndDialog (hDlg, wParam) ; 
  371.         
  372.         return 0 ;
  373.         
  374.     case WM_COMMAND :
  375.         
  376.         switch (wParam)
  377.         {
  378.         case IDE_EDIT_FONT :
  379.             
  380.             if (hEditFont) {
  381.                 DeleteObject (hEditFont) ;
  382.             }
  383.             
  384.             ChooseFont (&cf) ;
  385.             hEditFont = CreateFontIndirect (&lf) ;
  386.             
  387.             SendDlgItemMessage (hDlg, ID_EDITCONTROL, WM_SETFONT, 
  388.                 (WPARAM) hEditFont,  MAKELPARAM(TRUE, 0)) ;
  389.             
  390.             break ;
  391.             
  392.         case IDE_CLEAR :
  393.             
  394.             hWndEdit = GetDlgItem (hDlg, ID_EDITCONTROL) ;
  395.             SetWindowText (hWndEdit, TEXT("")) ;
  396.             
  397.             break ;
  398.             
  399.         case IDE_CLOSE :
  400.             
  401.             DeleteObject (hEditFont) ;
  402.             EndDialog (hDlg, wParam) ; 
  403.         }
  404.     }
  405.     
  406.     return FALSE ;
  407. }
  408.  
  409. //
  410. //   FUNCTION: BOOL CALLBACK HelpDialogProc (HWND , UINT , WPARAM , LPARAM)
  411. //
  412. //   PURPOSE: Dialog procedure for the Help dialog box.
  413. //
  414. //   COMMENTS:
  415. //        This does nothing but close the dialog box.
  416. //
  417. BOOL CALLBACK HelpDialogProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  418. {
  419.     switch (uMsg)
  420.     {
  421.     case WM_CLOSE :
  422.         
  423.         EndDialog (hDlg, wParam) ; 
  424.         return 0 ;
  425.         
  426.     case WM_COMMAND :
  427.         
  428.         switch (wParam)
  429.         {
  430.         case IDOK :
  431.             
  432.             EndDialog (hDlg, wParam) ; 
  433.         }
  434.     }
  435.     
  436.     return FALSE ;
  437. }
  438.  
  439. //
  440. //   FUNCTION: _tWinMain (HINSTANCE , HINSTANCE , LPTSTR , int)
  441. //
  442. //   PURPOSE: Standard WinMain function
  443. //
  444. //   COMMENTS:
  445. //        The prototype is _tWinMain, so it can be compiled either as
  446. //        a Unicode or an ANSI application. 
  447. //
  448. int WINAPI _tWinMain (HINSTANCE hInstance, HINSTANCE hPrev, 
  449.                       LPSTR pszCmdLine, int nCmdShow) 
  450. {
  451.     TCHAR szAppName[] = TEXT("Complex Script Display") ;
  452.     MSG msg ;
  453.     HANDLE hAccelTable ;
  454.     
  455.     // Perform application initialization:
  456.     if (!InitApplication (hInstance, szAppName)) {
  457.         return FALSE ;
  458.     }
  459.     // Perform instance initialization:
  460.     if (!InitInstance (hInstance, szAppName, nCmdShow)) {
  461.         return FALSE ;
  462.     }
  463.     
  464.     hAccelTable = LoadAccelerators (hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1)) ;
  465.     
  466.     if (!hAccelTable) {
  467.         return 0 ;
  468.     }
  469.     
  470.     // Main message loop:
  471.     while (GetMessage (&msg, NULL, 0, 0)) 
  472.     {
  473.         if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg)) {
  474.             TranslateMessage (&msg) ;
  475.             DispatchMessage (&msg) ;
  476.         }
  477.     }
  478.     return 0 ;
  479.  
  480. //
  481. //   FUNCTION: InitializeFont (HWND , LONG , LPCHOOSEFONT , LPLOGFONT)
  482. //
  483. //   PURPOSE:  Fills in font structures with initial values. 
  484. //
  485. //   REMARKS:  Since it contains only assignment statements, this function does no
  486. //             error checking, has no return value..
  487. //
  488. void InitializeFont (HWND hWnd, LONG lHeight, LPCHOOSEFONT lpCf, LPLOGFONT lpLf)
  489. {
  490.     lpCf->lStructSize   = sizeof (CHOOSEFONT) ;
  491.     lpCf->hwndOwner     = hWnd ;
  492.     lpCf->hDC           = NULL ;
  493.     lpCf->lpLogFont     = lpLf;
  494.     lpCf->iPointSize    = 10;
  495.     lpCf->Flags         = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT
  496.         | CF_NOSIZESEL ;
  497.     lpCf->rgbColors     = RGB (0,0,0);
  498.     lpCf->lCustData     = 0;
  499.     lpCf->lpfnHook      = NULL;
  500.     lpCf->lpTemplateName= NULL;
  501.     lpCf->hInstance     = g_hInstance;
  502.     lpCf->lpszStyle     = NULL;
  503.     lpCf->nFontType     = SIMULATED_FONTTYPE;
  504.     lpCf->nSizeMin      = 0;
  505.     lpCf->nSizeMax      = 0;
  506.     
  507.     lpLf->lfHeight      = lHeight ; 
  508.     lpLf->lfWidth       = 0 ; 
  509.     lpLf->lfEscapement  = 0 ; 
  510.     lpLf->lfOrientation = 0 ; 
  511.     lpLf->lfWeight      = FW_NORMAL ; 
  512.     lpLf->lfItalic      = FALSE ; 
  513.     lpLf->lfUnderline   = FALSE ; 
  514.     lpLf->lfStrikeOut   = FALSE ; 
  515.     lpLf->lfCharSet     = DEFAULT_CHARSET ; 
  516.     lpLf->lfOutPrecision= OUT_DEFAULT_PRECIS ; 
  517.     lpLf->lfClipPrecision = CLIP_DEFAULT_PRECIS ; 
  518.     lpLf->lfQuality     = DEFAULT_QUALITY ; 
  519.     lpLf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE ; 
  520.     lstrcpy(lpLf->lfFaceName, TEXT("Arial")) ;
  521.     lpLf->lfFaceName[LF_FACESIZE] = 0 ; 
  522. }
  523.  
  524. //
  525. //   FUNCTION: InitApplication(HINSTANCE, LPTSTR)
  526. //
  527. //   PURPOSE:  Fills in window class structure with parameters that describe
  528. //   the main window, and registers the window.
  529. //
  530. BOOL InitApplication (HINSTANCE hInstance, LPTSTR szAppName)
  531. {
  532.     
  533.     WNDCLASS  wc ; 
  534.     
  535.     wc.style         = CS_HREDRAW | CS_VREDRAW ;
  536.     wc.lpfnWndProc   = (WNDPROC) WndProc ;
  537.     wc.cbClsExtra    = 0 ;
  538.     wc.cbWndExtra    = 0 ;
  539.     wc.hInstance     = hInstance ;
  540.     wc.hIcon         = LoadIcon  (NULL, IDI_APPLICATION) ; 
  541.     wc.hCursor       = LoadCursor  (NULL, IDC_ARROW) ;
  542.     wc.hbrBackground =  (HBRUSH) (COLOR_WINDOW+1) ;
  543.     wc.lpszMenuName  = MAKEINTRESOURCE (IDR_MENU1) ;
  544.     wc.lpszClassName = szAppName ;
  545.     
  546.     return  (RegisterClass(&wc)) ;
  547. }
  548.  
  549. //
  550. //   FUNCTION: InitInstance (HANDLE, LPTSTR, int)
  551. //
  552. //   PURPOSE: Saves instance handle and creates main window
  553. //
  554. //   COMMENTS:
  555. //
  556. //        This function saved the instance handle in a global variable and
  557. //        created and displays the main program window.
  558. //
  559.  
  560. BOOL InitInstance (HINSTANCE hInstance, LPTSTR szAppName, int nCmdShow)
  561. {
  562.     HWND hWnd ;
  563.     
  564.     g_hInstance = hInstance ; // Store instance handle in global variable
  565.     
  566.     LoadString (hInstance, IDS_TITLE, g_szTitle, MAX_MESSAGE) ;
  567.     
  568.     hWnd = CreateWindow (szAppName, g_szTitle, WS_OVERLAPPEDWINDOW,
  569.         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
  570.         NULL, NULL, hInstance, NULL) ;
  571.     
  572.     if (!hWnd) {
  573.         return FALSE ;
  574.     }
  575.     
  576.     ShowWindow (hWnd, nCmdShow) ;
  577.     UpdateWindow (hWnd) ;
  578.     
  579.     return TRUE ;
  580. }
  581. //
  582. //  Function RcMessageBox (HWND, INT, INT, ...)
  583. //
  584. //  Purpose: Display a message box with formated output similar to sprintf
  585. //
  586. //  Remarks:
  587. //      This function loads the string identified by nMessageID from the 
  588. //      resource segment, uses vsprintf to format it using the variable
  589. //      parameters, and displays it to the user in a message box using the
  590. //      icon specified by nIcons.
  591. //
  592.  
  593. int RcMessageBox (HWND hWnd, int nMessageID, int nIcons, ...)
  594. {
  595.     // This can be changed to get the current lang id.
  596.     WORD wCurrentLang = MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US) ;
  597.     TCHAR szLoadBuff[MAX_MESSAGE], szOutPutBuff[3*MAX_MESSAGE] ;
  598.     va_list valArgs ;
  599.     
  600.     va_start (valArgs, nIcons) ;
  601.     
  602.     LoadString (g_hInstance, nMessageID, szLoadBuff, MAX_MESSAGE) ;
  603.     
  604.     wvsprintf (szOutPutBuff, szLoadBuff, valArgs) ;
  605.     
  606.     va_end (valArgs) ;
  607.     
  608.     return (MessageBoxEx (hWnd, szOutPutBuff, g_szTitle, nIcons, wCurrentLang)) ;
  609. }
  610.  
  611.