滚轮的滑动是windows产生WM_MOUSEWHEEL消息,并发送给具有输入焦点的窗口,而不是鼠标指针下面的窗口。和通常一样,参数lParam包含鼠标的位置信息,但是这些坐标是相对屏幕左上角的坐标,而不是相对客户区的坐标。同样,参数wParam的低位字包含一系列标志(flag),用于指明鼠标按钮,shift键和Ctrl键的状态。
新的信息出现在参数wParam的高位字,这是一个“增量”(delta)的数值,当前它可能等于120或者-120,取决于滑轮向前移动(朝鼠标的前段),数值120或-120表示文档分别向上或向下滚动三行。
在此介绍几个相关函数:
BOOL WINAPI SystemParametersInfo(
_In_ UINT uiAction,
_In_ UINT uiParam,
_Inout_ PVOID pvParam,
_In_ UINT fWinIni
);
uiAction: 顾名思义,指定的行为参数
uiParam: set_时指定的值,无需要就指定为0
pvParam: get_时获取的值,无需要就指定为0
fWinIni: 如有需要,三个值中选其一,SPIF_UPDATEINIFILE(更新win.ini),SPIF_SENDCHANGE(更行win.ini后并广播WM_SETTINGCHANGE),
SPIF_SENDWININICHANGE(同SPIF_SENDCHANGE效果),如不需要设定,则置为0
详见https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947%28v=vs.85%29.aspx
示例代码:
#define WINVER 0x0500
#define _WIN32_WINNT 0x0500
#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("sysmet4");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style=CS_VREDRAW |CS_HREDRAW;
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("unhnow error occurs"),szAppName,
MB_ICONERROR);
return 0;
}
hwnd=CreateWindow(szAppName,TEXT("mousewheel of sysmets4"),
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;
}
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);
iMaxWidth=40*cxChar+22*cxCaps;
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;</span>
case WM_SIZE:
cxClient=LOWORD(lParam);
cyClient=HIWORD(lParam);
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);
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:
si.cbSize=sizeof(si);
si.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&si);
iVertPos=si.nPos; //save the position for comparison later on
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;
}
si.fMask=SIF_POS;
SetScrollInfo(hwnd,SB_VERT,&si,TRUE);
GetScrollInfo(hwnd,SB_VERT,&si);
if(si.nPos!=iVertPos)
{
ScrollWindow(hwnd,0,cyChar*(iVertPos-si.nPos),
NULL,NULL);
UpdateWindow(hwnd);
}
return 0;
case WM_HSCROLL:
si.cbSize=sizeof(si);
si.fMask=SIF_ALL; //指定获取或设定哪些信息
GetScrollInfo(hwnd,SB_HORZ,&si);
iHorzPos=si.nPos; //save the position for comparison later on
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;
}
si.fMask=SIF_POS;
SetScrollInfo(hwnd,SB_HORZ,&si,TRUE);
GetScrollInfo(hwnd,SB_HORZ,&si);
if(si.nPos!=iHorzPos)
{
ScrollWindow(hwnd,cxChar*(iHorzPos-si.nPos),0,
NULL,NULL);
//UpdateWindow(hwnd);
}
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);
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;
//鼠标滚一下,增量为120或-120
iAccumDelta+=(short)HIWORD(wParam);
while(iAccumDelta>=iDeltaPerLine)
{
SendMessage(hwnd,WM_VSCROLL,SB_LINEUP,0);
iAccumDelta-=iDeltaPerLine;
}
//每一行的增量为30或-30
while(iAccumDelta<=-iDeltaPerLine)
{
SendMessage(hwnd,WM_VSCROLL,SB_LINEDOWN,0);
iAccumDelta+=iDeltaPerLine;
}</span>
return 0;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
si.cbSize=sizeof(si);
si.fMask=SIF_POS;
GetScrollInfo(hwnd,SB_VERT,&si);
iVertPos=si.nPos;
GetScrollInfo(hwnd,SB_HORZ,&si);
iHorzPos=si.nPos;
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);
}
程序在处理WM_SETTINGCHANGE消息时调用了参数为SPI_GETEWHEELSCROLLLINES的SystemParametersInfo函数。这个参数表示了每个增量数值能够滚动多少行,其中增量数值用定义在WINUSER.h的头文件中的WHEEL_DELTA来标识。WHEEL_DELTA等于120,且默认情况下SystemParametersInfo返回值为3,因此,没滚动一行的增量是40,也就是说滚轮每动一个单位,需要发送三次SB_LINEUP或者SB_LINEDOWN,程序中将这个单位增量保存在iDeltaPerLine中。