一、滚动条收到的消息:
在用鼠标单击滚动条或者拖动卷动方块时,Windows给窗口消息处理程序发送WM_VSCROLL(供上下移动)和WM_HSCROLL(供左右移动)消息。在滚动条上的每个鼠标动作都至少产生两个消息,一条在按下鼠标按钮时产生,一条在释放按钮时产生。拖动滚动条会持续发送滚动消息。
该消息的wparam的低16位为一个“通知码”,该码为定义的下列值之一,用于区别对滚动条的各种操作。
#define SB_LINEUP 0
#define SB_LINELEFT 0
#define SB_LINEDOWN 1
#define SB_LINERIGHT 1
#define SB_PAGEUP 2
#define SB_PAGELEFT 2
#define SB_PAGEDOWN 3
#define SB_PAGERIGHT 3
#define SB_THUMBPOSITION 4
#define SB_THUMBTRACK 5
#define SB_TOP 6
#define SB_LEFT 6
#define SB_BOTTOM 7
#define SB_RIGHT 7
#define SB_ENDSCROLL 8
lparam可以忽略,其只用于作为子窗口出现的滚动条。
处理 SB_THUMBPOSITION时,wParam的高字组是使用者释放鼠标键后卷动方块的最终位置。对于其它的卷动列操作,wParam的高字组应该被忽略。
程序能够处理SB_THUMBTRACK或SB_THUMBPOSITION消息,但一般不同时处理两者。如果处理SB_THUMBTRACK消息,在使用者拖动卷动方块时您需要移动显示区域的内容。而如果处理SB_THUMBPOSITION消息,则只需在使用者停止拖动卷动方块时移动显示区域的内容。处理SB_THUMBTRACK消息更好一些(但更困难),对于某些型态的数据,您的程序可能很难跟上产生的消息。
二、程序编写者需要做的事情:
在程序内使用滚动条时,程序写作者与Windows共同负责维护滚动条以及更新卷动方块的位置。下面是Windows对滚动条的处理:
- 处理所有滚动条鼠标事件
- 当使用者在滚动条内单击鼠标时,提供一种「反相显示」的闪烁
- 当使用者在滚动条内拖动滚动方块时,移动滚动方块
- 为包含滚动条窗口的窗口消息处理程序发送滚动条消息
以下是程序写作者应该完成的工作:
- 初始化滚动条的范围和位置
- 处理窗口消息处理程序的滚动条消息
- 更新滚动条内滚动方块的位置
- 更改显示区域的内容以响应对滚动条的更改
也就是说,程序需要初始化滚动条,处理消息,并在处理消息的过程中手动更新滚动方块位置、更新显示区域的内容。
具体来说,
- 根据通知码进行不同的操作
- 设置滑块位置等值
- 重新绘制界面
三、操作滚动条的方式(一)
在一个Windows窗口中,实现滚动条有两种方式。下面分别说说这两种方式。
第一种,使用函数:
int SetScrollPos(
HWND hWnd,
int nBar,
int nPos,
BOOL bRedraw );
BOOL SetScrollRange(
HWND hWnd,
int nBar,
int nMinPos,
int nMaxPos,
BOOL bRedraw );
//其它类似函数:
BOOL GetScrollRange(
HWND hWnd,
int nBar,
LPINT lpMinPos,
LPINT lpMaxPos );
int GetScrollPos(
HWND hWnd,
int nBar );
这种方式主要在一些老程序中使用,现在的程序主要使用第二种方式。
此处以WM_VSCROLL为例,列出了处理消息的代码:
//定义的变量:
DOWRD wparam;
static int iVscrollPos; //滚动方块位置
static int cyClient; //窗口高
static int cyChar; //一行文字占用的高度
const int NUMLINES=47; //一个常数,代表总行数
HANDLE hwnd; //窗口句柄
case WM_VSCROLL:
switch (LOWORD (wParam))
{
case SB_LINEUP:
iVscrollPos -= 1 ;
break ;
case SB_LINEDOWN:
iVscrollPos += 1 ;
break ;
case SB_PAGEUP:
iVscrollPos -= cyClient / cyChar ;
break ;
case SB_PAGEDOWN:
iVscrollPos += cyClient / cyChar ;
break ;
case SB_THUMBPOSITION:
iVscrollPos = HIWORD (wParam) ;
break ;
default :
break ;
}
iVscrollPos = max (0, min (iVscrollPos, NUMLINES - 1)) ;
if (iVscrollPos != GetScrollPos (hwnd, SB_VERT))
{
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
InvalidateRect (hwnd, NULL, TRUE) ;
}
return 0 ;
该示例代码展示了如何对消息进行操作。可以看到,程序保存了一个滚动方块位置的变量 iVscrollPos,并通过考虑窗口高度与行宽来实现翻页操作。
四、操作滚动条的方式(二)
其实这种方法主要的特点就是能够设置滑动方块的长度,使之适应屏幕与页面的比例。在处理消息方面,二者并未相差太大,不同主要在于使用的函数。
使用函数:
SetScrollInfo (hwnd, iBar, &si, bRedraw) ;
GetScrollInfo (hwnd, iBar, &si) ;
BOOL ScrollWindow(
HWND hWnd,
int XAmount,
int YAmount,
const RECT *lpRect,
const RECT *lpClipRect
);
参数中的 si 表示 SCROLLINFO 结构,定义为:
typedef struct tagSCROLLINFO
{
UINT cbSize ;// set to sizeof (SCROLLINFO)
UINT fMask ; // values to set or get,一个flag
int nMin ; // minimum range value
int nMax ; // maximum range value
UINT nPage ; // page size
int nPos ; // current position
int nTrackPos ;// current tracking position
}
SCROLLINFO, *PSCROLLINFO ;
这两个函数根据 si 中的fMask所设置的值,在 si 中返回一定的数据或设置特定的值。这个特性,,,很奇怪,在python里从来没有遇到过。
在呼叫SetScrollInfo或GetScrollInfo之前,必须将cbSize字段设定为结构的大小:
si.cbSize = sizeof (si) ;
fMask字段可设置为下列值:
- SIF_ALL
SIF_PAGE、SIF_POS、SIF_RANGE 和 SIF_TRACKPOS 的组合。 - SIF_DISABLENOSCROLL
此值仅在设置滚动条的参数时使用。 如果滚动条的新参数使滚动条变得不必要,请禁用滚动条而不是将其移除。 - SIF_PAGE
nPage 成员包含比例滚动条的页面大小。 - SIF_POS
nPos 成员包含滚动框位置,在用户拖动滚动框时不会更新 - SIF_RANGE
nMin 和 nMax 成员包含滚动范围的最小值和最大值 - SIF_TRACKPOS
nTrackPos 成员包含用户拖动滚动框时的当前位置。
一般在获取scrollbar状态时使用SIF_All是很方便的选择,而设置scrollbar状态时应按需设置。
设置SIF_PAGE值可以改变滑块长度,滑块长度/ 滚动条长度 = RANGE / PAGE
上代码:
//定义的变量
SCROLLINFO si;
static int iVertPos;
HANDLE hwnd;
DWORD wParam;
static int cyChar;
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 ;
后语:写文章还是写的好慢。但是这逼着我熟悉这个知识点。可是会不会导致我学不完?肯定会。下次,搞个概览就好。毕竟一切都是为了逆向。不过,写这种文章真的能够提高我的表达能力么?还是说,我应该去写些垃圾小说?(写小说,就不可能发出来了,太差了)