鼠标下

 

拦截鼠标

一个窗口消息处理程序通常只在鼠标光标位于窗口的显示区域,或非显示区域上时才接收鼠标消息。一个程序也可能需要在鼠标位于窗口外时接收鼠标消息。如果是这样,程序可以自行「拦截」鼠标。别害怕,这么做没什么大不了的。

设计矩形

为了说明拦截鼠标的必要性,请让我们看一下BLOKOUT1程序(如程序7-6所示)。此程序看起来达到了一定的功能,但它却有十分严重的缺陷。

程序7-6 BLOKOUT1
        
BLOKOUT1.C
        
/*----------------------------------------------------------------------------
        
  BLOKOUT1.C --   Mouse Button Demo Program
        
                                         (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 ("BlokOut1") ;
        
           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 ("Mouse Button Demo"),
        
                   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 DrawBoxOutline (HWND hwnd, POINT ptBeg, POINT ptEnd)
        
{
        
           HDC hdc ;
        
           hdc = GetDC (hwnd) ;
        
           SetROP2 (hdc, R2_NOT) ;
        
           SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;
        
           Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ;
        
   
        
           ReleaseDC (hwnd, hdc) ;
        
}
        
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
        
{
        
           static BOOL   fBlocking, fValidBox ;
        
           static POINT  ptBeg, ptEnd, ptBoxBeg, ptBoxEnd ;
        
           HDC                                  hdc ;
        
           PAINTSTRUCT  ps ;
        
           switch (message)
        
           {
        
           case   WM_LBUTTONDOWN :
        
                  ptBeg.x = ptEnd.x = LOWORD (lParam) ;
        
                  ptBeg.y = ptEnd.y = HIWORD (lParam) ;
        
        
        
                  DrawBoxOutline (hwnd, ptBeg, ptEnd) ;
        
        
        
                  SetCursor (LoadCursor (NULL, IDC_CROSS)) ;
        
        
        
                  fBlocking = TRUE ;
        
                  return 0 ;
        

           case   WM_MOUSEMOVE :
        
                  if (fBlocking)
        
                  {
        
                                  SetCursor (LoadCursor (NULL, IDC_CROSS)) ;
        
             
        
                                  DrawBoxOutline (hwnd, ptBeg, ptEnd) ;
        
            
        
                                  ptEnd.x = LOWORD (lParam) ;
        
                                  ptEnd.y = HIWORD (lParam) ;
        
             
        
                                  DrawBoxOutline (hwnd, ptBeg, ptEnd) ;
        
                  }
        
                  return 0 ;
        
        
        
           case   WM_LBUTTONUP :
        
                  if (fBlocking)
        
                  {
        
                                  DrawBoxOutline (hwnd, ptBeg, ptEnd) ;
        
             
        
                                  ptBoxBeg                             = ptBeg ;
        
                                  ptBoxEnd.x                           = LOWORD (lParam) ;
        
                                  ptBoxEnd.y                           = HIWORD (lParam) ;
        
            
        
                                  SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
        
                                  fBlocking                            = FALSE ;
        
                                  fValidBox                           = TRUE ;
        
             
        
                                  InvalidateRect (hwnd, NULL, TRUE) ;
        
                  }
        
                  return 0 ;
        
        
        
           case   WM_CHAR :
        
                  if (fBlocking & wParam == '/x1B')       // i.e., Escape
        
                  {
        
                                  DrawBoxOutline (hwnd, ptBeg, ptEnd) ;
        
             
        
                                SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
        
            
        
                                  fBlocking = FALSE ;
        
                  }
        
                  return 0 ;
        

           case   WM_PAINT :
        
                  hdc = BeginPaint (hwnd, &ps) ;
        

           if (fValidBox)
        
                  {
        
                                         SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ;
        
                                         Rectangle     (      hdc, ptBoxBeg.x, ptBoxBeg.y,
        
                                                                            ptBoxEnd.x, ptBoxEnd.y) ;
        
                  }
        
        
        
                  if (fBlocking)
        
                  {
        
                                  SetROP2 (hdc, R2_NOT) ;
        
                                  SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;
        
                                  Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ;
        
                  }
        
        
        
                  EndPaint (hwnd, &ps) ;
        
                  return 0 ;
        

           case   WM_DESTROY :
        
                  PostQuitMessage (0) ;
        
                 return 0 ;
        
    }
        
           return DefWindowProc (hwnd, message, wParam, lParam) ;
        
}
        

此程序展示了一些,它可以实作在Windows的「画图」程序中的东西。由按下鼠标左键开始确定矩形的一角,然后拖动鼠标。程序将画一个矩形的轮廓,其相对位置是鼠标目前的位置。当您释放鼠标后,程序将填入这个矩形。图7-4显示了一个已经画完的矩形和另一个正在画的矩形。


 

图7-4 BLOKOUT1的屏幕显示

那么,问题在哪里呢?

请试一试下面的操作:在BLOKOUT1的显示区域按下鼠标的左键,然后将光标移出窗口。程序将停止接收WM_MOUSEMOVE消息。现在释放按钮,BLOKOUT1将不再获得WM_BUTTONUP消息,因为光标在显示区域以外。然后将光标移回BLOKOUT1的显示区域,窗口消息处理程序仍然认为按钮处于按下状态。

这样做并不好,因为程序不知道发生了什么事情。

拦截的解决方案

BLOKOUT1显示了一些常见的程序功能,但它的程序代码显然有缺陷。这种问题就是要使用鼠标拦截来对付。如果使用者正在拖曳鼠标,那么当鼠标短时间内被拖出窗口时应该没有什么大问题,程序应该仍然控制着鼠标。

拦截鼠标要比放置一个老鼠夹子容易一些,您只要呼叫:

SetCapture (hwnd) ;
        

在这个函数呼叫之后,Windows将所有鼠标消息发给窗口句柄为hwnd的窗口消息处理程序。之后收到鼠标消息都是以显示区域消息的型态出现,即使鼠标正在窗口的非显示区域。lParam参数将指示鼠标在显示区域坐标中的位置。不过,当鼠标位于显示区域的左边或者上方时,这些x和y坐标可以是负的。当您想释放鼠标时,呼叫:

ReleaseCapture () ;
        

从而使处理恢复正常。

在32位的Windows中,鼠标拦截要比在以前的Windows版本中有多一些限制。特别是,如果鼠标被拦截,而鼠标按键目前并未被按下,并且鼠标光标移到了另一个窗口上,那么将不是由拦截鼠标的那个窗口,而是由光标下面的窗口来接收鼠标消息。对于防止一个程序在拦截鼠标之后不释放它而引起整个系统的混乱,这是必要的。

换句话说,只有当鼠标按键在您的显示区域中被按下时才拦截鼠标;当鼠标按键被释放时,才释放鼠标拦截。

BLOKOUT2程序

展示鼠标拦截的BLOKOUT2程序如程序7-7所示。

程序7-7 BLOKOUT2
        
BLOKOUT2.C
        
/*----------------------------------------------------------------------------
        
  BLOKOUT2.C --         Mouse Button & Capture Demo Program
        
                                          (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 ("BlokOut2") ;
        
           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 ("Mouse Button & Capture Demo"),
        
                                   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 DrawBoxOutline (HWND hwnd, POINT ptBeg, POINT ptEnd)
        
{
        
           HDC hdc ;
        
           hdc = GetDC (hwnd) ;
        
           SetROP2 (hdc, R2_NOT) ;
        
           SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;
        
           Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ;
        
   
        
           ReleaseDC (hwnd, hdc) ;
        
}
        

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
        
{
        
           static BOOL  fBlocking, fValidBox ;
        
           static POINT ptBeg, ptEnd, ptBoxBeg, ptBoxEnd ;
        
           HDC          hdc ;
        
           PAINTSTRUCT  ps ;
        
   
        
           switch (message)
        
           {
        
           case   WM_LBUTTONDOWN :
        
                  ptBeg.x = ptEnd.x = LOWORD (lParam) ;
        
                  ptBeg.y = ptEnd.y = HIWORD (lParam) ;
        
        
        
                  DrawBoxOutline (hwnd, ptBeg, ptEnd) ;
        

                  SetCapture (hwnd) ;
        
                  SetCursor (LoadCursor (NULL, IDC_CROSS)) ;
        
        
        
                  fBlocking = TRUE ;
        
                  return 0 ;
        
        
        
           case   WM_MOUSEMOVE :
        
                  if (fBlocking)
        
                  {
        
                                  SetCursor (LoadCursor (NULL, IDC_CROSS)) ;
        
             
        
                                  DrawBoxOutline (hwnd, ptBeg, ptEnd) ;
        
             
        
                                  ptEnd.x = LOWORD (lParam) ;
        
                                  ptEnd.y = HIWORD (lParam) ;
        
             
        
                                  DrawBoxOutline (hwnd, ptBeg, ptEnd) ;
        
                  }
        
                  return 0 ;
        
        
        
           case   WM_LBUTTONUP :
        
                  if (fBlocking)
        
                  {
        
                                  DrawBoxOutline (hwnd, ptBeg, ptEnd) ;
        
             
        
                                  ptBoxBeg                      = ptBeg ;
        
                                  ptBoxEnd.x                    = LOWORD (lParam) ;
        
                                  ptBoxEnd.y                    = HIWORD (lParam) ;
        
             
        
                                  ReleaseCapture () ;
        
                                  SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
        
             
        
                                  fBlocking                     = FALSE ;
        
                                  fValidBox                     = TRUE ;
        
             
        
                                InvalidateRect (hwnd, NULL, TRUE) ;
        
                  }
        
                  return 0 ;
        
        
        
           case   WM_CHAR :
        
                  if (fBlocking & wParam == '/x1B')       // i.e., Escape
        
                  {
        
                                  DrawBoxOutline (hwnd, ptBeg, ptEnd) ;
        
                                  ReleaseCapture () ;
        
                                  SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
        

                                  fBlocking = FALSE ;
        
                  }
        
                  return 0 ;
        
        
        
           case   WM_PAINT :
        
                 hdc = BeginPaint (hwnd, &ps) ;
        
        
        
                  if (fValidBox)
        
         {
        
                                  SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ;
        
                                  Rectangle (hdc, ptBoxBeg.x, ptBoxBeg.y,
        
                                  ptBoxEnd.x, ptBoxEnd.y) ;
        
                  }
        
        
        
                          if (fBlocking)
        
                  {
        
                                  SetROP2 (hdc, R2_NOT) ;
        
                                 SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;
        
                                  Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ;
        
                 }
        
        
        
                  EndPaint (hwnd, &ps) ;
        
                  return 0 ;
        
        
        
           case   WM_DESTROY :
        
                  PostQuitMessage (0) ;
        
                  return 0 ;
        
           }
        
           return DefWindowProc (hwnd, message, wParam, lParam) ;
        
}
        

BLOKOUT2程序和BLOKOUT1程序一样,只是多了三行新程序代码:在WM_LBUTTONDOWN消息处理期间呼叫SetCapture,而在WM_LBUTTONDOWN和WM_CHAR消息处理期间呼叫ReleaseCapture。检查画出窗口:使窗口小于屏幕大小,开始在显示区域画出一块矩形,然后将鼠标光标移出显示区域的右边或下边,最后释放鼠标按键。程序将获得整个矩形的坐标。但是需要扩大窗口才能看清楚它。

拦截鼠标并非只适用于那些古怪的应用程序。如果您需要鼠标按键在显示区域按下时都能够追踪WM_MOUSEMOVE消息,并直到鼠标按键被释放为止,那么您就应该拦截鼠标。这样将简化您的程序,同时又符合使用者的期望。

鼠标滑轮

与传统的鼠标相比,Microsoft IntelliMouse的特点是在两个键之间多了一个小滑轮。您可以按下这个滑轮,这时它的功能相当于鼠标按键的中键;或者您也可以用食指来转动它,这会产生一条特殊的消息,叫做WM_MOUSEWHEEL。使用鼠标滑轮的程序通过滚动或放大文件来响应此消息。它最初听起来像一个不必要的隐藏机关,但我必须承认,我很快就习惯于使用鼠标滑轮来滚动Microsoft Word和Microsoft Internet Explorer了。

我不想讨论鼠标滑轮的所有使用方法。实际上,我只是想告诉您如何在现有的程序(例如程序SYSMETS4)中添加鼠标滑轮处理程序,以便在显示区域中卷动数据。最终的SYSMETS程序如程序7-8所示。

程序7-8 SYSMETS4
        
SYSMETS.C
        
/*----------------------------------------------------------------------------
        
SYSMETS.C -- Final System Metrics Display Program
        
                                   (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 ("SysMets") ;
        
           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"),
        
                         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 ;
        
    static        int           iDeltaPerLine, iAccumDelta ;         // for mouse wheel logic
        
                  HDC         hdc ;
        
                  int         i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd ;
        
                  PAINTSTRUCT           ps ;
        
                  SCROLLINFO            si ;
        
                  TCHAR                         szBuffer[10] ;
        
                  TEXTMETRIC            tm ;
        
                  ULONG                         ulScrollLines ;       // for mouse wheel logic
        
           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 ;
        

                                                 // Fall through for mouse wheel information
        

           case   WM_SETTINGCHANGE:
        
                  SystemParametersInfo (SPI_GETWHEELSCROLLLINES, 0, &ulScrollLines, 0) ;
        
          
        
                   // ulScrollLines usually equals 3 or 0 (for no scrolling)
        
                   // WHEEL_DELTA equals 120, so iDeltaPerLine will be 40
        

                  if (ulScrollLines)
        
                                         iDeltaPerLine = WHEEL_DELTA / ulScrollLines ;
        
                  else
        
                                         iDeltaPerLine = 0 ;
        

            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 may 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 may 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_MOUSEWHEEL:
        
                  if (iDeltaPerLine == 0)
        
                                        break ;
        

                  iAccumDelta += (short) HIWORD (wParam) ;     // 120 or -120
        

                  while (iAccumDelta >= iDeltaPerLine)
        
                  {             
        
                                         SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0) ;
        
                                         iAccumDelta -= iDeltaPerLine ;
        
                  }
        

                  while (iAccumDelta <= -iDeltaPerLine)
        
                 {
        
                                        SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN, 0) ;
        
                                         iAccumDelta += iDeltaPerLine ;
        
                  }
        

                  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) ;
        
}
        

转动滑轮会导致Windows在有输入焦点的窗口(不是鼠标光标下面的窗口)产生WM_MOUSEWHEEL消息。与平常一样,lParam将获得鼠标的位置,当然坐标是相对于屏幕左上角的,而不是显示区域的。另外,wParam的低字组包含一系列的旗标,用于表示鼠标按键、Shift与Ctrl键的状态。

新的信息保存在wParam的高字组。其中有一个「delta」值,该值目前可以是120或-120,这取决于滑轮的向前转动(也就是说,向鼠标的前面,即带有按钮与电缆的一端)还是向后转动。值120或-120表示文件将分别向上或向下卷动三行。这里的构想是,以后版本的鼠标滑轮能有比现在的鼠标产生更精确的移动速度信息,并且用delta值,例如40和-40,来产生WM_MOUSEWHEEL消息。这些值能使文件只向上或向下卷动一行。

为使程序能在一般化环境执行,SYSMETS将在WM_CREATE和WM_SETTINGCHANGE消息处理时,以SPI_GETWHEELSCROLLLINES作为参数来呼叫SystemParametersInfo。此值说明WHEEL_DELTA的delta值将滚动多少行,WHEEL_DELTA在WINUSER.H中定义。WHEEL_DELTA等于120,并且,在内定情况下SystemParametersInfo传回3,因此与卷动一行相联系的delta值就是40。SYSMETS将此值保存在iDeltaPerLine。

在WM_MOUSEWHEEL消息处理期间,SYSMETS将delta值给静态变量iAccumDelta。然后,如果iAccumDelta大于或等于iDeltaPerLine(或者是小于或等于-iDeltaPerLin),SYSMETS用SB_LINEUP或SB_LINEDOWN值产生WM_VSCROLL消息。对于每一个WM_VSCROLL消息,iAccumDelta由iDeltaPerLine增加(或减少)。此代码允许delta值大于、小于或等于滚动一行所需要的delta值。

下面还有

还有一个引人注目的鼠标问题:建立自订鼠标光标。我将在第十章,与其它Windows资源一起讨论此问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值