五、GDI映像方式—WHATSIZE程序
Windows的小历史:第一篇如何写作Windows程序的介绍文章出现在《Microsoft Systems Journal》1986年12月号上。在那篇文章中,范例程序叫做WSZ(「what size:什么尺寸」),它以图素、英寸和毫米为单位显示了显示区域的大小。那个程序的更简易版本是WHATSIZE,如程序5-6所示。程序显示了以五种度量映像方式显示的窗口显示区域的大小。
程序5-6  WHATSIZE
        
WHATSIZE.C
        
/*------------------------------------------------------------
        
  WHATSIZE.C -- What Size is the Window?
        
           (c) Charles Petzold, 1998
        
----------------------------------------------------------*/
        
#include <windows.h>
        
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
        
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
        
            PSTR szCmdLine, int iCmdShow)
        
{
        
    static TCHAR szAppName[] = TEXT ("WhatSize") ;
        
    HWND          hwnd ;
        
    MSG           msg ;
        
    WNDCLASS      wndclass ;
        
   
        
    wndclass.style        = CS_HREDRAW | CS_VREDRAW;
        
    wndclass.lpfnWndProc= WndProc ;
        
    wndclass.cbClsExtra   = 0 ;
        
    wndclass.cbWndExtra   = 0 ;
        
    wndclass.hInstance    = hInstance ;
        
    wndclass.hIcon= LoadIcon (NULL, IDI_APPLICATION) ;
        
    wndclass.hCursor= LoadCursor (NULL, IDC_ARROW) ;
        
    wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH) ;
        
    wndclass.lpszMenuName = NULL ;
        
    wndclass.lpszClassName= szAppName ;
        
    if (!RegisterClass (&wndclass))
        
    {
        
            MessageBox (NULL, TEXT ("This program requires Windows NT!"),
        
           szAppName, MB_ICONERROR) ;
        
            return 0 ;
        
    }
        
   
        
    hwnd = CreateWindow (szAppName, TEXT ("What Size is the Window?"),
        
                           WS_OVERLAPPEDWINDOW,
        
                          CW_USEDEFAULT, CW_USEDEFAULT,
        
                           CW_USEDEFAULT, CW_USEDEFAULT,
        
                           NULL, NULL, hInstance, NULL) ;
        
   
        
    ShowWindow (hwnd, iCmdShow) ;
        
    UpdateWindow (hwnd) ;
        
    while (GetMessage (&msg, NULL, 0, 0))
        
    {
        
            TranslateMessage (&msg) ;
        
            DispatchMessage (&msg) ;
        
    }
        
    return msg.wParam ;
        
}
        
void Show (HWND hwnd, HDC hdc, int xText, int yText, int iMapMode,
        
            TCHAR * szMapMode)
        
{
        
    TCHAR szBuffer [60] ;
        
    RECT  rect ;
        
   
        
    SaveDC (hdc) ;
        
    SetMapMode (hdc, iMapMode) ;
        
    GetClientRect (hwnd, &rect) ;
        
    DPtoLP (hdc, (PPOINT) &rect, 2) ;
        
   
        
    RestoreDC (hdc, -1) ;
        
    TextOut (     hdc, xText, yText, szBuffer,
        
                  wsprintf (szBuffer, TEXT ("%-20s %7d %7d %7d %7d"), szMapMode,
        
                   rect.left, rect.right, rect.top, rect.bottom)) ;
        
}
        
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        
{
        
    static TCHAR szHeading [] =
        
            TEXT ("Mapping Mode   Left   Right  Top  Bottom") ;
        
    static TCHAR szUndLine [] =
        
            TEXT ("------------   ----   -----   ---  ------") ;
        
    static int   cxChar, cyChar ;
        
    HDC           hdc ;
        
    PAINTSTRUCT   ps ;
        
    TEXTMETRIC    tm ;
        
   
        
    switch (message)
        
    {
        
    case   WM_CREATE:
        
            hdc = GetDC (hwnd) ;
        
            SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
        
        
        
            GetTextMetrics (hdc, &tm) ;
        
            cxChar = tm.tmAveCharWidth ;
        
            cyChar = tm.tmHeight + tm.tmExternalLeading ;
        
        
        
            ReleaseDC (hwnd, hdc) ;
        
            return 0 ;
        
        
        
    case   WM_PAINT:
        
            hdc = BeginPaint (hwnd, &ps) ;
        
            SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
        
            SetMapMode (hdc, MM_ANISOTROPIC) ;
        
            SetWindowExtEx (hdc, 1, 1, NULL) ;
        
            SetViewportExtEx (hdc, cxChar, cyChar, NULL) ;
        
        
        
            TextOut (hdc, 1, 1, szHeading, lstrlen (szHeading)) ;
        
            TextOut (hdc, 1, 2, szUndLine, lstrlen (szUndLine)) ;
        
        
        
            Show (hwnd, hdc, 1, 3, MM_TEXT,      TEXT ("TEXT (pixels)")) ;
        
            Show (hwnd, hdc, 1, 4, MM_LOMETRIC,  TEXT ("LOMETRIC (.1 mm)")) ;
        
            Show (hwnd, hdc, 1, 5, MM_HIMETRIC,  TEXT ("HIMETRIC (.01 mm)")) ;
        
            Show (hwnd, hdc, 1, 6, MM_LOENGLISH, TEXT ("LOENGLISH (.01 in)")) ;
        
            Show (hwnd, hdc, 1, 7, MM_HIENGLISH,TEXT ("HIENGLISH (.001 in)")) ;
        
            Show (hwnd, hdc, 1, 8, MM_TWIPS,     EXT ("TWIPS (1/1440 in)")) ;
        
        
        
            EndPaint (hwnd, &ps) ;
        
            return 0 ;
        
        
        
    case   WM_DESTROY:
        
            PostQuitMessage (0) ;
        
            return 0 ;
        
    }
        
    return DefWindowProc (hwnd, message, wParam, lParam) ;
        
}
        
为了便于用TextOut函数显示信息,WHATSIZE使用了一种固定间距的字体。下面一条简单的叙述就可以切换为固定间距的字体(在Windows 3.0中它是优先使用的):
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
        
有两个同样的函数用于选取画笔和画刷。像前面提到的,WHATSIZE也使用MM_ANISTROPIC映像方式将逻辑单位设定为字符大小。
当WHATSIZE需要取得六种映像方式之一的显示区域的大小时,它保存目前的设备内容,设定一种新的映像方式,取得显示区域坐标,将它们转换为逻辑坐标,然后在显示信息之前,恢复原映像方式。底下这些程序代码在WHATSIZE的Show函数里:
SaveDC (hdc) ;
        
SetMapMode (hdc, iMapMode) ;
        
GetClientRect (hwnd, &rect) ;
        
DptoLP (hdc, (PPOINT) &rect, 2) ;
        
RestoreDC (hdc, -1) ;
        
图5-19显示了WHATSIZE的典型输出。
	
