二、Windows按键消息—为SYSMETS加上键盘处理功能
在编写第四章中三个版本的SYSMETS程序时,我们还不了解键盘,只能使用滚动条和鼠标来卷动文字。现在我们知道了处理键盘消息的方法,那么不妨在程序中加入键盘接口。显然,这是处理光标移动键的工作。我们将大多数光标键(Home、End、Page Up、Page Down、Up Arrow和Down Arrow)用于垂直卷动,左箭头键和右箭头键用于不太重要的水平卷动。
建立键盘接口的一种简单方法是在窗口消息处理程序中加入与WM_VSCROLL和WM_HSCROLL处理方式相仿,而且本质上相同的WM_KEYDOWN处理方法。不过这样子做是不聪明的,因为如果要修改滚动条的做法,就必须相对应地修改WM_KEYDOWN。
为什么不简单地将每一种WM_KEYDOWN消息都翻译成同等效用的WM_VSCROLL或者WM_HSCROLL消息呢?通过向窗口消息处理程序发送假冒消息,我们可能会让WndProc认为它获得了卷动信息。
在Windows中,这种方法是可行的。发送消息的函数叫做SendMessage,它所用的参数与传递到窗口消息处理程序的参数是相同的:
SendMessage (hwnd, message, wParam, lParam) ;
在呼叫SendMessage时,Windows呼叫窗口句柄为hwnd的窗口消息处理程序,并把这四个参数传给它。当窗口消息处理程序完成消息处理之后,Windows把控制传回到SendMessage呼叫之后的下一道叙述。您发送消息过去的窗口消息处理程序,可以是同一个窗口消息处理程序、同一程序中的其它窗口消息处理程序或者其它应用程序,中的窗口消息处理程序。
下面说明在SYSMETS程序中使用SendMessage处理WM_KEYDOWN代码的方法:
caseWM_KEYDOWN:
        
    switch (wParam)
        
   {
        
    case   VK_HOME:
        
            SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0) ;
        
            break ;
        
    case   VK_END:
        
            SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0) ;
        
            break ;
        
    case   VK_PRIOR:
        
            SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0) ;
        
            break ;
        
至此,您已经有了大概观念了吧。我们的目标是为滚动条添加键盘接口,并且也正在这么做。通过把卷动消息发送到窗口消息处理程序,我们实作了用光标移动键进行卷动列的功能。现在您知道在SYSMETS3中为WM_VSCROLL消息加上SB_TOP和SB_BOTTOM处理码的原因了吧。在那里并没有用到它,但是现在处理Home和End键时就有用了。如程序6-1所示的SYSENTS4就加上了这些变化。编译这个程序时还需要用到第四章的SYSMETS.H文件。
        
SYSMETS4.C
        
/*----------------------------------------------------------------------
        
  SYSMETS4.C -- System Metrics Display Program No. 4
        
                (c) Charles Petzold, 1998
        
------------------------------------------------------------------------*/
        
#include <windows.h>
        
#include "sysmets.h"
        
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
        
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
        
                                   PSTR szCmdLine, int iCmdShow)
        
{
        
    static TCHAR szAppName[]      = TEXT ("SysMets4") ;
        
    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 ("Program requires Windows NT!"),
        
                    szAppName, MB_ICONERROR) ;
        
            return 0 ;
        
    }
        
   
        
    hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 4"),
        
                                                        WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
        
                                          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 ;
        
}
        
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        
{
        
    static int  cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth ;
        
    HDC                   hdc ;
        
    int                   i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd ;
        
    PAINTSTRUCT   ps ;
        
    SCROLLINFO    si ;
        
    TCHAR                 szBuffer[10] ;
        
    TEXTMETRIC    tm ;
        
   
        
    switch (message)
        
    {
        
    case   WM_CREATE:
        
            hdc = GetDC (hwnd) ;
        
        
        
            GetTextMetrics (hdc, &tm) ;
        
            cxChar= tm.tmAveCharWidth ;
        
            cxCaps= (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
        
            cyChar= tm.tmHeight + tm.tmExternalLeading ;
        
            ReleaseDC (hwnd, hdc) ;
        
                   // Save the width of the three columns
        
        
        
            iMaxWidth = 40 * cxChar + 22 * cxCaps ;
        
            return 0 ;
        
        
        
    case   WM_SIZE:
        
            cxClient              = LOWORD (lParam) ;
        
            cyClient              = HIWORD (lParam) ;
        
                   // Set vertical scroll bar range and page size
        
            si.cbSize     = sizeof (si) ;
        
            si.fMask      = SIF_RANGE | SIF_PAGE ;
        
            si.nMin       = 0 ;
        
            si.nMax       = NUMLINES - 1 ;
        
            si.nPage      = cyClient / cyChar ;
        
            SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
        
                           // Set horizontal scroll bar range and page size
        
            si.cbSize     = sizeof (si) ;
        
            si.fMask      = SIF_RANGE | SIF_PAGE ;
        
            si.nMin       = 0 ;
        
            si.nMax       = 2 + iMaxWidth / cxChar ;
        
            si.nPage      = cxClient / cxChar ;
        
            SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
        
            return 0 ;
        
        
        
    case WM_VSCROLL:
        
                           // Get all the vertical scroll bar information
        
            si.cbSize     = sizeof (si) ;
        
            si.fMask      = SIF_ALL ;
        
            GetScrollInfo (hwnd, SB_VERT, &si) ;
        
                           // Save the position for comparison later on
        
                   iVertPos = si.nPos ;
        
                   switch (LOWORD (wParam))
        
            {
        
    case SB_TOP:
        
            si.nPos = si.nMin ;
        
            break ;
        
             
        
    case SB_BOTTOM:
        
            si.nPos = si.nMax ;
        
            break ;
        
             
        
    case SB_LINEUP:
        
            si.nPos -= 1 ;
        
            break ;
        
            
        
    case SB_LINEDOWN:
        
            si.nPos += 1 ;
        
            break ;
        
             
        
    case SB_PAGEUP:
        
            si.nPos -= si.nPage ;
        
            break ;
        
             
        
    case SB_PAGEDOWN:
        
            si.nPos += si.nPage ;
        
            break ;
        
             
        
    case SB_THUMBTRACK:
        
            si.nPos = si.nTrackPos ;
        
            break ;
        
             
        
            default:
        
            break ;       
        
            }
        
                   // Set the position and then retrieve it.  Due to adjustments
        
                   //   by Windows it might not be the same as the value set.
        
            si.fMask = SIF_POS ;
        
            SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
        
            GetScrollInfo (hwnd, SB_VERT, &si) ;
        
                   // If the position has changed, scroll the window and update it
        
            if (si.nPos != iVertPos)
        
            {                  
        
                   ScrollWindow (hwnd, 0, cyChar * (iVertPos - si.nPos),
        
                                                                                 NULL, NULL) ;
        
                   UpdateWindow (hwnd) ;
        
            }
        
            return 0 ;
        
        
        
    case WM_HSCROLL:
        
                           // Get all the vertical scroll bar information
        
            si.cbSize             = sizeof (si) ;
        
            si.fMask              = SIF_ALL ;
        
                           // Save the position for comparison later on
        
                   GetScrollInfo (hwnd, SB_HORZ, &si) ;
        
                   iHorzPos      = si.nPos ;
        
            switch (LOWORD (wParam))
        
            {
        
    case SB_LINELEFT:
        
            si.nPos -= 1 ;
        
            break ;
        
             
        
    case SB_LINERIGHT:
        
            si.nPos += 1 ;
        
            break ;
        
             
        
    case SB_PAGELEFT:
        
            si.nPos -= si.nPage ;
        
            break ;
        
             
        
    case SB_PAGERIGHT:
        
            si.nPos += si.nPage ;
        
            break ;
        
             
        
    case SB_THUMBPOSITION:
        
            si.nPos = si.nTrackPos ;
        
            break ;
        
             
        
            default:
        
            break ;
        
            }
        
                           // Set the position and then retrieve it.  Due to adjustments
        
                           //   by Windows it might not be the same as the value set.
        
            si.fMask = SIF_POS ;
        
            SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
        
            GetScrollInfo (hwnd, SB_HORZ, &si) ;
        
        
        
                           // If the position has changed, scroll the window
        
            if (si.nPos != iHorzPos)
        
            {
        
                   ScrollWindow (hwnd, cxChar * (iHorzPos - si.nPos), 0,
        
                   NULL, NULL) ;
        
            }
        
            return 0 ;
        
    case WM_KEYDOWN:
        
            switch (wParam)
        
    {
        
    case VK_HOME:
        
            SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0) ;
        
            break ;
        
             
        
    case VK_END:
        
            SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0) ;
        
            break ;
        
             
        
    case VK_PRIOR:
        
            SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0) ;
        
            break ;
        
             
        
    case VK_NEXT:
        
            SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0) ;
        
            break ;
        
             
        
    case VK_UP:
        
            SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0) ;
        
            break ;
        
    case VK_DOWN:
        
            SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN, 0) ;
        
            break ;
        
             
        
    case VK_LEFT:
        
            SendMessage (hwnd, WM_HSCROLL, SB_PAGEUP, 0) ;
        
            break ;
        
             
        
    case VK_RIGHT:
        
            SendMessage (hwnd, WM_HSCROLL, SB_PAGEDOWN, 0) ;
        
            break ;
        
            }
        
            return 0 ;
        
    case WM_PAINT:
        
            hdc = BeginPaint (hwnd, &ps) ;
        
                           // Get vertical scroll bar position
        
            si.cbSize             = sizeof (si) ;
        
            si.fMask              = SIF_POS ;
        
            GetScrollInfo (hwnd, SB_VERT, &si) ;
        
            iVertPos              = si.nPos ;
        
                           // Get horizontal scroll bar position
        
            GetScrollInfo (hwnd, SB_HORZ, &si) ;
        
            iHorzPos              = si.nPos ;
        
                           // Find painting limits
        
            iPaintBeg             = max (0, iVertPos + ps.rcPaint.top / cyChar) ;
        
            iPaintEnd             = min (NUMLINES - 1,
        
            iVertPos + ps.rcPaint.bottom / cyChar) ;
        
            for (i = iPaintBeg ; i <= iPaintEnd ; i++)
        
            {
        
                   x = cxChar * (1 - iHorzPos) ;
        
                   y = cyChar * (i - iVertPos) ;
        
             
        
            TextOut (hdc, x, y,
        
            sysmetrics[i].szLabel,
        
            lstrlen (sysmetrics[i].szLabel)) ;
        
             
        
            TextOut (hdc, x + 22 * cxCaps, y,
        
            sysmetrics[i].szDesc,
        
            lstrlen (sysmetrics[i].szDesc)) ;
        
             
        
            SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
        
            TextOut (hdc, x + 22 * cxCaps + 40 * cxChar, y, szBuffer,
        
           wsprintf (szBuffer, TEXT ("%5d"),
        
            GetSystemMetrics (sysmetrics[i].iIndex))) ;
        
             
        
            SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
        
            }
        
            EndPaint (hwnd, &ps) ;
        
            return 0 ;
        
        
        
    case   WM_DESTROY:
        
            PostQuitMessage (0) ;
        
            return 0 ;
        
    }
        
            return DefWindowProc (hwnd, message, wParam, lParam) ;
        
}
