第 5讲
Windows应用程序中的键盘与鼠标
扫描码是依赖于具体设备的,为达到 设备无关性 的要求,往往使用与具体设备
无关的 虚拟码,虚拟码是由 Windows系统定义的与设备无关的键的标识
由于键盘的输入 产生一条
消息
扫描码、虚拟码以及其他与击键有关的 消息
设备驱动程序
截取键的扫描码
翻译
虚拟码
它含
键盘上的键 对应 一个唯一的 标识值 (扫描码 )
按下或释放某键时
产
生
取出键盘消息进行处理
消息
设备驱动程序 把消息放到系统的
消息队列中
Windows从系统消息队
列中取出消息
发送到相应的线程消息队列中窗口过程
6.1 键盘在应用程序中的应用
虚拟码 是一种与设备无关的键盘编码,它的值存放在键盘消息的
wParam参数中,用以标识哪一个键被按下或释放,最常用的虚拟码
已经在 Windows.h中定义,
常用的虚拟码
符号常量名称 等价的键盘键或
鼠标按钮
符号常量名称 等价的键盘键或
鼠标按钮
V K _L B U T T O N 鼠标左按钮 V K _B A C K 退格键
V K _R B U T T O N 鼠标右按钮 V K _T A B 制表键
V K _MB U T T O N 鼠标中按钮 V K _R E T U R N 回车键
V K _SHI FT Sh i f t 键 V K _C O N T R O L C trl 键
V K _ME N U A l t 键 V K _P A U S E Pau se 键
V K _C A PI T A L C aps L o ck 键 V K _E S C A P E E sc 键
V K _PR IO R Page U p 键 V K _N E X T Page D o w n 键
V K _E N D E nd 键 V K _H O ME H om e 键
V K _L E FT 左键头键 V K _R IG H T 右箭头键
V K _U P 上箭头键 V K _D O W N 下箭头键
V K _0~ V K _9 0 ~ 9 键 V K _A ~ V K _ Z A ~ Z 键
操作系统在接收到键盘输入后
把消息发送给具有,输入焦点 ( input focus)的 窗口
应用程序一般有几个窗
口,但当按下某一个键
时,只有一个窗口能接
收到该键盘消息,接收
这个键盘消息的窗口称
为有“输入焦点”的窗口
有“输入焦点”
的窗口应是活
动窗口或者活
动窗口的子窗口
窗口正在接
收输入焦点
窗口函数通过捕获 WM_SETFOCUS和 WM_KILLFOCUS消息确
定当前窗口是否具有输入焦点。
窗口失去
输入焦点
键盘消息
按键消息
字符消息
按下或松开一个键时
就产生了一按键消息
一个按键的组合产生了
一个可以显示的字符时,
就产生了一个字符消息
系统按键消息
非系统按键消息
Alt键与相关输入键的组合
产生的消息,这些键一般
由 Windows系统内部直接
处理,应用程序不处理
若应用程序处理了这
些系统键消息,还要调
用 DefWindowsProc()函
数,以便不影响系统对
它们的处理
不使用 Alt键组合
的的按键消息
消息 类型 含义
W M _K E Y D O W N 非系统 按下了非系统键消息
W M _K E Y U P 非系统 松开了非系统键消息
W M _S Y S K E Y D O W N 系统 按下了系统键消息
W M _S Y S K E Y U P 系统 松开了系统键消息
按键消息的
两个变量
wParam
lParam
32位的变量
重复计数位( 0~ 15位)
OEM扫描码( 16~ 23位)
扩展键标志( 24位)
保留位( 25~ 28位)
关联码( 29位)
键的先前状态(位 30)
转换状态( 31位)
包含了识别按下
的键的虚键码
在 WinMain函数的消息循环中包含了 TranslateMessage函数,
其功能是把按键消息转化为字符消息,但只有当键盘驱动程序把键
盘字符映射成 ASCII码后才能产生 WM_CHAR消息
消息 类型 含义
WM_CHAR 非系统 非系统字符
WM_DEADCHAR 非系统 非系统死字符
WM_SYSCHAR 系统 系统字符
WM_SYSDEADCHAR 系统 系统死字符
字符消息
系统
非系统 WM_KEYDOWN和 WM_KEYUP
的按键消息只能产生非系统消息
WM_SYSKEYDOWN和 WM_SYSKEYUP
按键消息只能产生系统消息
【 6-1】 设计一个窗口,在该窗口中练习键盘的响应,要求如下:
(1) 单击键盘上的向上箭头时,窗口中显示,You had hitted the up key”
(2) 单击 <Shift>键时,窗口中显示,You had hitted the SHIFT key”
(3) 单击 <Ctrl>键时,窗口中显示,You had hitted the CTRL key”
(4) 单击 <Ctrl+A>键时,窗口中显示,You had hitted the CTRL A key”
(5) 单击 <Shift+B>键时,窗口中显示,You had hitted the SHIFT B key”
long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam)
{HDC hDC; //定义设备环境句柄,
PAINTSTRUCT ps; //定义包含绘图信息的结构体变量
HPEN hPen; //定义画笔句柄。
HBRUSH hBrush; //定义画刷句柄
//以下定义输出的字符串。
static char cUp[]="You had hitted the UP key";
static char cCtrl[]="You had hitted the Ctrl key";
static char cShift[]="You had hitted the SHIFT key";
static char cCtrl_A[]="You had hitted the CTRL A key";
static char cShift_B[]="You had hitted the SHIFT B key";
//下面定义并初始化按键标志变量。
static BOOL nUpKeyDown=FALSE,
nShiftKeyDown=FALSE,
nCtrlKeyDown=FALSE,
nCtrlAKeyDown=FALSE,
nShiftBKeyDown=FALSE;
switch(iMessage)
{ case WM_KEYDOWN:
{ switch(wParam)
{ case VK_UP,//当按上箭头键时,变量置为真。
nUpKeyDown = TRUE; break;
case VK_SHIFT,//当按 shift键时,变量置为真。
nShiftKeyDown = TRUE; break;
case VK_CONTROL,//当按 control键时,变量置为真
nCtrlKeyDown = TRUE; break;
default,break;
}
}
break;
case WM_KEYUP:
InvalidateRect(hWnd,NULL,FALSE); break;
case WM_CHAR:
if(wParam==(65&VK_CONTROL))
{ if(nCtrlKeyDown == TRUE)
{ nCtrlAKeyDown = TRUE; nCtrlKeyDown = FALSE; }
}
else if(wParam==98||wParam==66) //当按下 b键时
{ if(nShiftKeyDown == TRUE) //检查 shift键是否处于按下状态。
{nShiftBKeyDown = TRUE; //当 SHIFT键按下时,变量置为真
nShiftKeyDown = FALSE;}
}
break;
case WM_PAINT,//处理绘图消息,
hDC=BeginPaint(hWnd,&ps);
hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH); //创建 白画刷。
hPen = (HPEN)GetStockObject(WHITE_PEN); //创建白画笔。
SelectObject(hDC,hPen); //选入白画刷
SelectObject(hDC,hBrush); //选入白画笔
SetTextColor(hDC,RGB(255,0,0)); //设置字体颜色为红色。
//输出信息。
if(nUpKeyDown == TRUE)
{ Rectangle(hDC,0,0,300,200);
TextOut(hDC,0,0,cUp,strlen(cUp));
nUpKeyDown = FALSE;
}
else if(nCtrlAKeyDown == TRUE)
{ Rectangle(hDC,0,0,300,200);
TextOut(hDC,0,100,cCtrl_A,strlen(cCtrl_A));
nCtrlAKeyDown = FALSE;
nCtrlKeyDown = FALSE;
}
else if(nCtrlAKeyDown == TRUE)
else if(nCtrlKeyDown == TRUE&&nCtrlAKeyDown == FALSE)
{ Rectangle(hDC,0,0,300,200);
TextOut(hDC,0,60,cCtrl,strlen(cCtrl));
nCtrlKeyDown = FALSE;
}
else if(nShiftBKeyDown == TRUE)
{ Rectangle(hDC,0,0,300,200);
TextOut(hDC,0,0,cShift_B,strlen(cShift_B));
nShiftBKeyDown = FALSE;
nShiftKeyDown = FALSE; }
else if(nShiftBKeyDown == FALSE&&nShiftKeyDown == TRUE)
{ Rectangle(hDC,0,0,300,200);
TextOut(hDC,0,0,cShift,strlen(cShift));
nShiftKeyDown = FALSE; }
else;
//删除画笔和画刷
DeleteObject(hPen);
DeleteObject(hBrush);
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0); return 0;
default,return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
return 0;
}
用户窗口区输入“这是一
个有关键盘操作的示例程
序”的字符串
程序中缓冲区大小只设 30
个字符,当缓冲区满时若
再输入任何字符,就出现 (1)
号错误提示
用户按下左箭头键直至光
标到达本行起始位置时,
就无法再往左移动,则出
现 (2)号错误信息
若此时您按下 Esc
键,就出现 (3)号错
误提示信息
若当前光标位置处于本行的起
始位置,此时按下回退键
(BackSpace),则出现编号为 (4)
的错误提示信息
若字符缓冲区中已没有任
何字符,此时按下了
Delete键,则出现 (5)号错
误提示信息
6.2 键盘操作
应用举例
例 6-2:本例演示键盘输入时所产生的消息序列并在
窗口的客户区显示对应的字符
long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam)
{
#define BufSize 15 //设置存放字符的缓冲区大小
static char cCharBuf[BufSize]; //设置静态字符数组, 存放输入的字符,
字符个数不能超出缓冲区大小
static int nNumChar=0; //现有字符个数
static int nArrayPos=0; //字符的位置
static int nLnHeight;
static int nCharWidth;
int x;
HDC hDC;
TEXTMETRIC tm;
PAINTSTRUCT PtStr; //定义指向包含绘图信息的结构体变量
switch(iMessage) //处理消息
{case WM_CHAR,//遇到非系统字符所作的处理
{if(wParam==VK_BACK) //处理, 遇到回退键的消息,
{if(nArrayPos==0) //若 已在一行文字的开始处, 则提示用户 "不能回退 "
MessageBox(hWnd,"当前位置是文本的起始位置, 不能回退 ",NULL,MB_OK);
else
{nArrayPos=nArrayPos-1; //每按一次回退键就回退一个字符的位置
nNumChar=nNumChar-1; //对现有字符总数进行计数
InvalidateRect(hWnd,NULL,TRUE); //刷新用户区, 并序发送 WM_PAINT消息
}
break;
}
if(wParam==VK_ESCAPE) //处理按下 Escape键消息
{MessageBox(hWnd,"您现在不能按 ESC键, 请继续其它操作 ",NULL,MB_OK);
break;
}
if(nNumChar>=BufSize) //如果写入的字符数超过缓冲区大小, 则报警
{MessageBox(hWnd,"缓冲区已满, 不能再输入字符了 \n若需要删除字符,
请用 BackSpace键 ",NULL,MB_OK);
break;
}
for(x=nNumChar;x>nArrayPos;x=x-1)
cCharBuf[x]=cCharBuf[x-1];
cCharBuf[nArrayPos]=(unsigned char)wParam;
nArrayPos=nArrayPos+1;
nNumChar=nNumChar+1;
InvalidateRect(hWnd,NULL,TRUE);
}
break;
case WM_CREATE,//处理窗口创建消息
{ hDC=GetDC(hWnd);
GetTextMetrics(hDC,&tm); //获取字体信息
nLnHeight=tm.tmHeight+tm.tmExternalLeading;
nCharWidth=tm.tmAveCharWidth;
ReleaseDC(hWnd,hDC);
}
break;
case WM_KEYDOWN,//处理按下键消息
{switch(wParam)
{case VK_END,//处理按下键为 End时的消息
nArrayPos=nNumChar; //输入位置从本行的末尾开始
break;
case VK_HOME,//处理按下键为 Home时的消息
nArrayPos=0; //输入位置为本行的起始位置
break;
case VK_DELETE,//处理按下键为 Delete时的消息
if(nArrayPos==nNumChar) //输入位置处于本行的末尾
MessageBox(hWnd,"缓冲区已空,没有字符可供删除 ",NULL,MB_OK);
else
{ for(x=nArrayPos;x<nNumChar;x=x+1)
cCharBuf[x]=cCharBuf[x+1]; //每删除一个字符,总字符数减 1
nNumChar=nNumChar-1;
InvalidateRect(hWnd,NULL,TRUE); //用户区刷新
}
break;
case VK_LEFT,//处理按下左方向键时的消息
if(nArrayPos>0)
nArrayPos=nArrayPos-1; //当前输入位置往前移一个位置
else
MessageBox(hWnd,"您已经移动到起始位置,不能再往左移动了 ",NULL,MB_OK);
break;
case VK_RIGHT://处理按下右方向键时的消息
if(nArrayPos<nNumChar)//若当前位置未到缓冲区的末尾,可向右移动
nArrayPos=nArrayPos+1;
else
MessageBox(hWnd,"已经到缓冲区的末尾,不能再向右移动了 ",NULL,MB_OK);
break;
}
}
break;
case WM_PAINT,//处理重画消息
hDC=BeginPaint(hWnd,&PtStr);
TextOut(hDC,nCharWidth,nLnHeight,cCharBuf,nNumChar); //输出文本
EndPaint(hWnd,&PtStr);
break;
case WM_DESTROY,//处理结束应用程序消息
PostQuitMessage(0); //结束应用程序
break;
default,//其他消息处理程序
return(DefWindowProc(hWnd,iMessage,wParam,lParam)) ;
}
return 0;
}
6.3 鼠标在应用程序中的应用
鼠标作为定位输入设备,通过鼠标单击、双击和拖动功能,用户可以
很容易地操作基于 Windows图形界面的应用程序。
代表预定义光标的常量 光标属性描述
IDC_ ARRO W 箭头光标
IDC_ CRO S S 十字光标
IDC_ I B EA M I 形文本光标
IDC_ ICO N 空图标
IDC_ S IZ E ALL 十字箭头光标
IDC_ S IZ ENE S W 带有指向东北方和西南方箭头的光标
IDC_ S IZ ENS 带有指向北方和南方箭头的光标
IDC_ S IZ EN W S E 带有指向西北方和东南方箭头的光标
IS C_ S IZ EW E 带有指向东方和西方箭头的光标
IDC_ UP ARRO W 垂直箭头光标
IDC_ W AIT 计时光标 ( 也称沙漏光标 )
Windows中通过光标来指示当前鼠标的位置,在 Windows操作系统中
预定义了几种光标,并在 Windows.h头文件中加以定义,
例如,下面的语句为窗口类 wndclass
WNDCLASS wndclass;
…
wndclass.hCursor=LoadCursor (hThislnst,IDC_WAIT);
…
此外,还可在应用程序中调用 LoadCursor函数改变光标形式
用户自定义光标
自定义光标保存在扩展名为,cur的文件中
光标名 CURSOR 光标文件 (.cur)
采用自定义光标
时,需在资源文
件中定义光标资源
加载光标资源 (常在定义窗口类时进行 )
LoadCursor(hThisInst,lpszCursorname)
当前实例句柄 当前光标
值 含义
M K_ C O N T R O L 按下键按上的 C t r l 键
M K_ L B U T T O N 按下了鼠标左键,等同与 W M _L B U T T O N D O W N
M K_ M B U T T O N 按下了鼠标中键,等同与 W M _M B U T T O N D O W N
M K_ R B U T T O N 按下了鼠标右键,等同与 W M _R B U T T O N D O W N
M K_ S H I F T 按下键盘上的 S h i f t 键
鼠标
消息
lParam包含了鼠标位置 低位包含鼠标位置的 x坐标值高位包含鼠标位置的 y坐标
wParam包含了指示各种虚键状态的值
相当于窗口的左上
角为原点的坐标值
对于鼠标消息
的处理 要对 Shift和 Ctrl键进行监测
case WM_LBUTTONDOWN,//按下鼠标左键
if((wParam&MK_CONTROL)&&(wParam&MK_SHIFT))
… //Shift和 Ctrl键都被按下
break;
case WM_LBUTTONUP,//释放鼠标左键
…
break; …
case WM_LBUTTONDOWN,//按下鼠标左键
…
break;
case WM_LBUTTONUP,//释放鼠标左键
…
break;
…
不监测
Windows系统默认的时间间隔为 0.5秒,也可以调用
SetDoubleClickTime()重新设定间隔值
若窗口不包含上述属性的定义,即使进行了双击操作,该窗
口也只能接收到两条 WM_BUTTONDOWN消息或两条 WN_BUTTONUP
消息。
鼠标双击
wndclass.style=CS_HEADRAW|CS_VERDRAW|CS_DBLCLKS;
要使窗口函数能接收
鼠标双击产生的消息
在注册窗口类时必须
具有 CS_DBLCLKS属性
由于鼠标移动的随机性,如果要使某一个窗口能不间断地 捕
获鼠标 消息,就必须对 鼠标加以捕获
SetCapture(hWnd),可以向句柄为 hWnd的窗口发送所有的鼠标消息
调用 SetCapture()函数
一旦从窗口捕获了鼠标,
系统的键盘功能就暂时失
效,其它窗口也无法得到
鼠标消息
当该窗口不再需要捕获鼠标消息时,应及时调用 ReleaseCapture()以释
放鼠标,否则,其他窗口无法接收鼠标信息。
菜单、滚动条
工具条和标题条等处
不由应用程序处理
而是送往函数
DefWindowProc
用户区以外
的地方产生
的鼠标事件
产生一个
非用户区
鼠标消息
6.4 鼠标应用程序实例
【 例 6-3】 鼠标输入
示范程序,本例介
绍如何响应鼠标信
息、并改变光标形
状等用法,用户在
窗口的不同区域移
动鼠标时,光标将
显示如下图所标识
的不同箭头的形状
#include<windows.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain( HINSTANCE hInstance,HINSTANCE hPrevInst,LPSTR lpszCmdLine,int nCmdShow)
{ HWND hwnd; MSG Msg;
WNDCLASS wndclass;
char lpszClassName[]="窗口 ";
char lpszTitle[]="My_Windows";
wndclass.style=0;
… …
wndclass.lpszClassName=lpszClassName;
if(!RegisterClass(&wndclass))
{ MessageBeep(0); return FALSE; }
hwnd=CreateWindow(lpszClassName,lpszTitle,WS_VISIBLE|WS_SYSMENU|WS_MINIMIZEB
OX,50,50,450,450,NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,nCmdShow);
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)
{ PAINTSTRUCT ps;
HDC hdc;
TEXTMETRIC tm;
HBRUSH hbrush;
HPEN hpen;
HCURSOR hcursor;
char str[15];
static int x,y;
switch(message)
{
case WM_MOUSEMOVE:
x=LOWORD(lParam);
y=HIWORD(lParam);
if(x>50&&x<150&&y>50&&y<150)
{ hcursor=LoadCursor(NULL,IDC_CROSS);//十字光标
SetCursor(hcursor);
}if(x>150&&x<250&&y>50&&y<150)
{
hcursor=LoadCursor(NULL,IDC_SIZEALL);//十字箭头光标
SetCursor(hcursor);
}
if(x>250&&x<350&&y>50&&y<150)
{ hcursor=LoadCursor(NULL,IDC_SIZENESW);SetCursor(hcursor);}//西南 -东北光标
if(x>50&&x<150&&y>150&&y<250)
{ hcursor=LoadCursor(NULL,IDC_IBEAM);SetCursor(hcursor);}//I字型光标
if(x>50&&x<150&&y>250&&y<350)
{ hcursor=LoadCursor(NULL,IDC_SIZEWE);SetCursor(hcursor); } //东西向
箭头
if(x>150&&x<250&&y>150&&y<250)
{ hcursor=LoadCursor(NULL,IDC_WAIT);SetCursor(hcursor);} //沙漏光标
if(x>250&&x<350&&y>150&&y<250)
{ hcursor=LoadCursor(NULL,IDC_UPARROW);SetCursor(hcursor);} //向上箭头光标
if(x>150&&x<250&&y>250&&y<350)
{ hcursor=LoadCursor(NULL,IDC_SIZENWSE);SetCursor(hcursor);} //西北 -东南箭
头
if(x>250&&x<350&&y>250&&y<350)
{ hcursor=LoadCursor(NULL,IDC_SIZENS);SetCursor(hcursor); } //南北向
箭头
hdc=GetDC(hwnd);
GetTextMetrics(hdc,&tm);
hbrush=(HBRUSH)GetStockObject(WHITE_BRUSH);
SelectObject(hdc,hbrush);
hpen=(HPEN)GetStockObject(WHITE_PEN);
SelectObject(hdc,hpen);
Rectangle(hdc,5,5,tm.tmAveCharWidth*8,tm.tmHeight+3);
sprintf(str,"%d,%d\0",x,y);
TextOut(hdc,5,5,str,lstrlen(str));
EndPaint(hwnd,&ps);
break;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
hpen=(HPEN)GetStockObject(BLACK_PEN);
SelectObject(hdc,hpen);
Rectangle(hdc,50,50,350,350);
MoveToEx(hdc,50,150,NULL); LineTo(hdc,350,150);
MoveToEx(hdc,50,250,NULL); LineTo(hdc,350,250);
MoveToEx(hdc,150,50,NULL); LineTo(hdc,150,350);
MoveToEx(hdc,250,50,NULL); LineTo(hdc,250,350);
EndPaint(hwnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
default,return DefWindowProc(hwnd,message,wParam,lParam);
}
return(0);
}
【 6-4】 编写一个鼠标应用程序, 按下鼠标左键在窗口中移动时, 将按下左键时所在点
和当前点所形成的矩形涂成灰色, 此时光标为十字型 。 当抬起鼠标左键时, 将前面所绘
制度矩形拉伸到整个窗口, 拉伸过程中将光标设置为沙漏型 。 然后, 若双击鼠标的左键,
则灰色消失, 窗口恢复到初始状态 。
void InvertBlock (HWND hwnd,POINT ptBeg,POINT ptEnd)
{
HDC hdc ;
hdc = CreateDC ("DISPLAY",NULL,NULL,NULL) ;
ClientToScreen (hwnd,&ptBeg) ;//转换指定窗口的客户区内的点的坐标
ClientToScreen (hwnd,&ptEnd) ;
PatBlt (hdc,ptBeg.x,ptBeg.y,ptEnd.x - ptBeg.x,ptEnd.y - ptBeg.y,DSTINVERT) ;
//这个函数在矩形区内用当前的画笔重画背景和前景 */
DeleteDC (hdc) ;
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,
WPARAM wParam,LPARAM lParam)
{ static BOOL operate=FALSE,ready=TRUE;
static POINT BeginP,EndP;
static RECT rect={0,0,0,0} ;
HDC hdc ;
PAINTSTRUCT PtStr;
HBRUSH hBrush;
switch (message)
{ case WM_LBUTTONDOWN:
if ((!operate)&&ready)
{ operate=TRUE ; //左键击活俘获
ready=FALSE;
SetCapture (hwnd) ;//把所有的鼠标信息输入到被左键击活的窗口,
SetCursor (LoadCursor (NULL,IDC_CROSS)) ; //载入光标
BeginP.x=LOWORD(lParam);
BeginP.y=HIWORD(lParam);
}
return 0 ;
case WM_MOUSEMOVE:
if (operate)
{ EndP.x=LOWORD(lParam);
EndP.y=HIWORD(lParam);
rect.left=BeginP.x<EndP.x?BeginP.x:EndP.x;
rect.right=BeginP.x>EndP.x?BeginP.x:EndP.x;
rect.top=BeginP.y<EndP.y?BeginP.y:EndP.y;
rect.bottom=BeginP.y>EndP.y?BeginP.y:EndP.y;
SetCursor (LoadCursor (NULL,IDC_WAIT)) ; //载入沙漏光标
InvalidateRect(hwnd,NULL,TRUE);
}
return 0 ;
case WM_PAINT:
if(ready==FALSE)
{ hdc=BeginPaint(hwnd,&PtStr);
hBrush=(HBRUSH)GetStockObject(LTGRAY_BRUSH);
SelectObject(hdc,hBrush);
Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
DeleteObject(hBrush); EndPaint(hwnd,&PtStr);
}
else
{ hdc=BeginPaint(hwnd,&PtStr);
GetClientRect(hwnd,&rect);
Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
EndPaint(hwnd,&PtStr);
}
return 0;
case WM_LBUTTONUP:
if (operate)
{ operate=FALSE;
SetCursor (LoadCursor (NULL,IDC_WAIT));
GetClientRect (hwnd,&rect) ;
InvalidateRect(hwnd,NULL,TRUE);
SetCursor(LoadCursor(NULL,IDC_ARROW));
ReleaseCapture () ; //把鼠标从当前窗口中释放出来
}
return 0 ;
case WM_LBUTTONDBLCLK:
if(ready==FALSE)
{ ready=TRUE;
InvalidateRect(hwnd,NULL,TRUE);
}
return 0;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd,message,wParam,lParam) ;
}
Windows应用程序中的键盘与鼠标
扫描码是依赖于具体设备的,为达到 设备无关性 的要求,往往使用与具体设备
无关的 虚拟码,虚拟码是由 Windows系统定义的与设备无关的键的标识
由于键盘的输入 产生一条
消息
扫描码、虚拟码以及其他与击键有关的 消息
设备驱动程序
截取键的扫描码
翻译
虚拟码
它含
键盘上的键 对应 一个唯一的 标识值 (扫描码 )
按下或释放某键时
产
生
取出键盘消息进行处理
消息
设备驱动程序 把消息放到系统的
消息队列中
Windows从系统消息队
列中取出消息
发送到相应的线程消息队列中窗口过程
6.1 键盘在应用程序中的应用
虚拟码 是一种与设备无关的键盘编码,它的值存放在键盘消息的
wParam参数中,用以标识哪一个键被按下或释放,最常用的虚拟码
已经在 Windows.h中定义,
常用的虚拟码
符号常量名称 等价的键盘键或
鼠标按钮
符号常量名称 等价的键盘键或
鼠标按钮
V K _L B U T T O N 鼠标左按钮 V K _B A C K 退格键
V K _R B U T T O N 鼠标右按钮 V K _T A B 制表键
V K _MB U T T O N 鼠标中按钮 V K _R E T U R N 回车键
V K _SHI FT Sh i f t 键 V K _C O N T R O L C trl 键
V K _ME N U A l t 键 V K _P A U S E Pau se 键
V K _C A PI T A L C aps L o ck 键 V K _E S C A P E E sc 键
V K _PR IO R Page U p 键 V K _N E X T Page D o w n 键
V K _E N D E nd 键 V K _H O ME H om e 键
V K _L E FT 左键头键 V K _R IG H T 右箭头键
V K _U P 上箭头键 V K _D O W N 下箭头键
V K _0~ V K _9 0 ~ 9 键 V K _A ~ V K _ Z A ~ Z 键
操作系统在接收到键盘输入后
把消息发送给具有,输入焦点 ( input focus)的 窗口
应用程序一般有几个窗
口,但当按下某一个键
时,只有一个窗口能接
收到该键盘消息,接收
这个键盘消息的窗口称
为有“输入焦点”的窗口
有“输入焦点”
的窗口应是活
动窗口或者活
动窗口的子窗口
窗口正在接
收输入焦点
窗口函数通过捕获 WM_SETFOCUS和 WM_KILLFOCUS消息确
定当前窗口是否具有输入焦点。
窗口失去
输入焦点
键盘消息
按键消息
字符消息
按下或松开一个键时
就产生了一按键消息
一个按键的组合产生了
一个可以显示的字符时,
就产生了一个字符消息
系统按键消息
非系统按键消息
Alt键与相关输入键的组合
产生的消息,这些键一般
由 Windows系统内部直接
处理,应用程序不处理
若应用程序处理了这
些系统键消息,还要调
用 DefWindowsProc()函
数,以便不影响系统对
它们的处理
不使用 Alt键组合
的的按键消息
消息 类型 含义
W M _K E Y D O W N 非系统 按下了非系统键消息
W M _K E Y U P 非系统 松开了非系统键消息
W M _S Y S K E Y D O W N 系统 按下了系统键消息
W M _S Y S K E Y U P 系统 松开了系统键消息
按键消息的
两个变量
wParam
lParam
32位的变量
重复计数位( 0~ 15位)
OEM扫描码( 16~ 23位)
扩展键标志( 24位)
保留位( 25~ 28位)
关联码( 29位)
键的先前状态(位 30)
转换状态( 31位)
包含了识别按下
的键的虚键码
在 WinMain函数的消息循环中包含了 TranslateMessage函数,
其功能是把按键消息转化为字符消息,但只有当键盘驱动程序把键
盘字符映射成 ASCII码后才能产生 WM_CHAR消息
消息 类型 含义
WM_CHAR 非系统 非系统字符
WM_DEADCHAR 非系统 非系统死字符
WM_SYSCHAR 系统 系统字符
WM_SYSDEADCHAR 系统 系统死字符
字符消息
系统
非系统 WM_KEYDOWN和 WM_KEYUP
的按键消息只能产生非系统消息
WM_SYSKEYDOWN和 WM_SYSKEYUP
按键消息只能产生系统消息
【 6-1】 设计一个窗口,在该窗口中练习键盘的响应,要求如下:
(1) 单击键盘上的向上箭头时,窗口中显示,You had hitted the up key”
(2) 单击 <Shift>键时,窗口中显示,You had hitted the SHIFT key”
(3) 单击 <Ctrl>键时,窗口中显示,You had hitted the CTRL key”
(4) 单击 <Ctrl+A>键时,窗口中显示,You had hitted the CTRL A key”
(5) 单击 <Shift+B>键时,窗口中显示,You had hitted the SHIFT B key”
long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam)
{HDC hDC; //定义设备环境句柄,
PAINTSTRUCT ps; //定义包含绘图信息的结构体变量
HPEN hPen; //定义画笔句柄。
HBRUSH hBrush; //定义画刷句柄
//以下定义输出的字符串。
static char cUp[]="You had hitted the UP key";
static char cCtrl[]="You had hitted the Ctrl key";
static char cShift[]="You had hitted the SHIFT key";
static char cCtrl_A[]="You had hitted the CTRL A key";
static char cShift_B[]="You had hitted the SHIFT B key";
//下面定义并初始化按键标志变量。
static BOOL nUpKeyDown=FALSE,
nShiftKeyDown=FALSE,
nCtrlKeyDown=FALSE,
nCtrlAKeyDown=FALSE,
nShiftBKeyDown=FALSE;
switch(iMessage)
{ case WM_KEYDOWN:
{ switch(wParam)
{ case VK_UP,//当按上箭头键时,变量置为真。
nUpKeyDown = TRUE; break;
case VK_SHIFT,//当按 shift键时,变量置为真。
nShiftKeyDown = TRUE; break;
case VK_CONTROL,//当按 control键时,变量置为真
nCtrlKeyDown = TRUE; break;
default,break;
}
}
break;
case WM_KEYUP:
InvalidateRect(hWnd,NULL,FALSE); break;
case WM_CHAR:
if(wParam==(65&VK_CONTROL))
{ if(nCtrlKeyDown == TRUE)
{ nCtrlAKeyDown = TRUE; nCtrlKeyDown = FALSE; }
}
else if(wParam==98||wParam==66) //当按下 b键时
{ if(nShiftKeyDown == TRUE) //检查 shift键是否处于按下状态。
{nShiftBKeyDown = TRUE; //当 SHIFT键按下时,变量置为真
nShiftKeyDown = FALSE;}
}
break;
case WM_PAINT,//处理绘图消息,
hDC=BeginPaint(hWnd,&ps);
hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH); //创建 白画刷。
hPen = (HPEN)GetStockObject(WHITE_PEN); //创建白画笔。
SelectObject(hDC,hPen); //选入白画刷
SelectObject(hDC,hBrush); //选入白画笔
SetTextColor(hDC,RGB(255,0,0)); //设置字体颜色为红色。
//输出信息。
if(nUpKeyDown == TRUE)
{ Rectangle(hDC,0,0,300,200);
TextOut(hDC,0,0,cUp,strlen(cUp));
nUpKeyDown = FALSE;
}
else if(nCtrlAKeyDown == TRUE)
{ Rectangle(hDC,0,0,300,200);
TextOut(hDC,0,100,cCtrl_A,strlen(cCtrl_A));
nCtrlAKeyDown = FALSE;
nCtrlKeyDown = FALSE;
}
else if(nCtrlAKeyDown == TRUE)
else if(nCtrlKeyDown == TRUE&&nCtrlAKeyDown == FALSE)
{ Rectangle(hDC,0,0,300,200);
TextOut(hDC,0,60,cCtrl,strlen(cCtrl));
nCtrlKeyDown = FALSE;
}
else if(nShiftBKeyDown == TRUE)
{ Rectangle(hDC,0,0,300,200);
TextOut(hDC,0,0,cShift_B,strlen(cShift_B));
nShiftBKeyDown = FALSE;
nShiftKeyDown = FALSE; }
else if(nShiftBKeyDown == FALSE&&nShiftKeyDown == TRUE)
{ Rectangle(hDC,0,0,300,200);
TextOut(hDC,0,0,cShift,strlen(cShift));
nShiftKeyDown = FALSE; }
else;
//删除画笔和画刷
DeleteObject(hPen);
DeleteObject(hBrush);
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0); return 0;
default,return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
return 0;
}
用户窗口区输入“这是一
个有关键盘操作的示例程
序”的字符串
程序中缓冲区大小只设 30
个字符,当缓冲区满时若
再输入任何字符,就出现 (1)
号错误提示
用户按下左箭头键直至光
标到达本行起始位置时,
就无法再往左移动,则出
现 (2)号错误信息
若此时您按下 Esc
键,就出现 (3)号错
误提示信息
若当前光标位置处于本行的起
始位置,此时按下回退键
(BackSpace),则出现编号为 (4)
的错误提示信息
若字符缓冲区中已没有任
何字符,此时按下了
Delete键,则出现 (5)号错
误提示信息
6.2 键盘操作
应用举例
例 6-2:本例演示键盘输入时所产生的消息序列并在
窗口的客户区显示对应的字符
long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam)
{
#define BufSize 15 //设置存放字符的缓冲区大小
static char cCharBuf[BufSize]; //设置静态字符数组, 存放输入的字符,
字符个数不能超出缓冲区大小
static int nNumChar=0; //现有字符个数
static int nArrayPos=0; //字符的位置
static int nLnHeight;
static int nCharWidth;
int x;
HDC hDC;
TEXTMETRIC tm;
PAINTSTRUCT PtStr; //定义指向包含绘图信息的结构体变量
switch(iMessage) //处理消息
{case WM_CHAR,//遇到非系统字符所作的处理
{if(wParam==VK_BACK) //处理, 遇到回退键的消息,
{if(nArrayPos==0) //若 已在一行文字的开始处, 则提示用户 "不能回退 "
MessageBox(hWnd,"当前位置是文本的起始位置, 不能回退 ",NULL,MB_OK);
else
{nArrayPos=nArrayPos-1; //每按一次回退键就回退一个字符的位置
nNumChar=nNumChar-1; //对现有字符总数进行计数
InvalidateRect(hWnd,NULL,TRUE); //刷新用户区, 并序发送 WM_PAINT消息
}
break;
}
if(wParam==VK_ESCAPE) //处理按下 Escape键消息
{MessageBox(hWnd,"您现在不能按 ESC键, 请继续其它操作 ",NULL,MB_OK);
break;
}
if(nNumChar>=BufSize) //如果写入的字符数超过缓冲区大小, 则报警
{MessageBox(hWnd,"缓冲区已满, 不能再输入字符了 \n若需要删除字符,
请用 BackSpace键 ",NULL,MB_OK);
break;
}
for(x=nNumChar;x>nArrayPos;x=x-1)
cCharBuf[x]=cCharBuf[x-1];
cCharBuf[nArrayPos]=(unsigned char)wParam;
nArrayPos=nArrayPos+1;
nNumChar=nNumChar+1;
InvalidateRect(hWnd,NULL,TRUE);
}
break;
case WM_CREATE,//处理窗口创建消息
{ hDC=GetDC(hWnd);
GetTextMetrics(hDC,&tm); //获取字体信息
nLnHeight=tm.tmHeight+tm.tmExternalLeading;
nCharWidth=tm.tmAveCharWidth;
ReleaseDC(hWnd,hDC);
}
break;
case WM_KEYDOWN,//处理按下键消息
{switch(wParam)
{case VK_END,//处理按下键为 End时的消息
nArrayPos=nNumChar; //输入位置从本行的末尾开始
break;
case VK_HOME,//处理按下键为 Home时的消息
nArrayPos=0; //输入位置为本行的起始位置
break;
case VK_DELETE,//处理按下键为 Delete时的消息
if(nArrayPos==nNumChar) //输入位置处于本行的末尾
MessageBox(hWnd,"缓冲区已空,没有字符可供删除 ",NULL,MB_OK);
else
{ for(x=nArrayPos;x<nNumChar;x=x+1)
cCharBuf[x]=cCharBuf[x+1]; //每删除一个字符,总字符数减 1
nNumChar=nNumChar-1;
InvalidateRect(hWnd,NULL,TRUE); //用户区刷新
}
break;
case VK_LEFT,//处理按下左方向键时的消息
if(nArrayPos>0)
nArrayPos=nArrayPos-1; //当前输入位置往前移一个位置
else
MessageBox(hWnd,"您已经移动到起始位置,不能再往左移动了 ",NULL,MB_OK);
break;
case VK_RIGHT://处理按下右方向键时的消息
if(nArrayPos<nNumChar)//若当前位置未到缓冲区的末尾,可向右移动
nArrayPos=nArrayPos+1;
else
MessageBox(hWnd,"已经到缓冲区的末尾,不能再向右移动了 ",NULL,MB_OK);
break;
}
}
break;
case WM_PAINT,//处理重画消息
hDC=BeginPaint(hWnd,&PtStr);
TextOut(hDC,nCharWidth,nLnHeight,cCharBuf,nNumChar); //输出文本
EndPaint(hWnd,&PtStr);
break;
case WM_DESTROY,//处理结束应用程序消息
PostQuitMessage(0); //结束应用程序
break;
default,//其他消息处理程序
return(DefWindowProc(hWnd,iMessage,wParam,lParam)) ;
}
return 0;
}
6.3 鼠标在应用程序中的应用
鼠标作为定位输入设备,通过鼠标单击、双击和拖动功能,用户可以
很容易地操作基于 Windows图形界面的应用程序。
代表预定义光标的常量 光标属性描述
IDC_ ARRO W 箭头光标
IDC_ CRO S S 十字光标
IDC_ I B EA M I 形文本光标
IDC_ ICO N 空图标
IDC_ S IZ E ALL 十字箭头光标
IDC_ S IZ ENE S W 带有指向东北方和西南方箭头的光标
IDC_ S IZ ENS 带有指向北方和南方箭头的光标
IDC_ S IZ EN W S E 带有指向西北方和东南方箭头的光标
IS C_ S IZ EW E 带有指向东方和西方箭头的光标
IDC_ UP ARRO W 垂直箭头光标
IDC_ W AIT 计时光标 ( 也称沙漏光标 )
Windows中通过光标来指示当前鼠标的位置,在 Windows操作系统中
预定义了几种光标,并在 Windows.h头文件中加以定义,
例如,下面的语句为窗口类 wndclass
WNDCLASS wndclass;
…
wndclass.hCursor=LoadCursor (hThislnst,IDC_WAIT);
…
此外,还可在应用程序中调用 LoadCursor函数改变光标形式
用户自定义光标
自定义光标保存在扩展名为,cur的文件中
光标名 CURSOR 光标文件 (.cur)
采用自定义光标
时,需在资源文
件中定义光标资源
加载光标资源 (常在定义窗口类时进行 )
LoadCursor(hThisInst,lpszCursorname)
当前实例句柄 当前光标
值 含义
M K_ C O N T R O L 按下键按上的 C t r l 键
M K_ L B U T T O N 按下了鼠标左键,等同与 W M _L B U T T O N D O W N
M K_ M B U T T O N 按下了鼠标中键,等同与 W M _M B U T T O N D O W N
M K_ R B U T T O N 按下了鼠标右键,等同与 W M _R B U T T O N D O W N
M K_ S H I F T 按下键盘上的 S h i f t 键
鼠标
消息
lParam包含了鼠标位置 低位包含鼠标位置的 x坐标值高位包含鼠标位置的 y坐标
wParam包含了指示各种虚键状态的值
相当于窗口的左上
角为原点的坐标值
对于鼠标消息
的处理 要对 Shift和 Ctrl键进行监测
case WM_LBUTTONDOWN,//按下鼠标左键
if((wParam&MK_CONTROL)&&(wParam&MK_SHIFT))
… //Shift和 Ctrl键都被按下
break;
case WM_LBUTTONUP,//释放鼠标左键
…
break; …
case WM_LBUTTONDOWN,//按下鼠标左键
…
break;
case WM_LBUTTONUP,//释放鼠标左键
…
break;
…
不监测
Windows系统默认的时间间隔为 0.5秒,也可以调用
SetDoubleClickTime()重新设定间隔值
若窗口不包含上述属性的定义,即使进行了双击操作,该窗
口也只能接收到两条 WM_BUTTONDOWN消息或两条 WN_BUTTONUP
消息。
鼠标双击
wndclass.style=CS_HEADRAW|CS_VERDRAW|CS_DBLCLKS;
要使窗口函数能接收
鼠标双击产生的消息
在注册窗口类时必须
具有 CS_DBLCLKS属性
由于鼠标移动的随机性,如果要使某一个窗口能不间断地 捕
获鼠标 消息,就必须对 鼠标加以捕获
SetCapture(hWnd),可以向句柄为 hWnd的窗口发送所有的鼠标消息
调用 SetCapture()函数
一旦从窗口捕获了鼠标,
系统的键盘功能就暂时失
效,其它窗口也无法得到
鼠标消息
当该窗口不再需要捕获鼠标消息时,应及时调用 ReleaseCapture()以释
放鼠标,否则,其他窗口无法接收鼠标信息。
菜单、滚动条
工具条和标题条等处
不由应用程序处理
而是送往函数
DefWindowProc
用户区以外
的地方产生
的鼠标事件
产生一个
非用户区
鼠标消息
6.4 鼠标应用程序实例
【 例 6-3】 鼠标输入
示范程序,本例介
绍如何响应鼠标信
息、并改变光标形
状等用法,用户在
窗口的不同区域移
动鼠标时,光标将
显示如下图所标识
的不同箭头的形状
#include<windows.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain( HINSTANCE hInstance,HINSTANCE hPrevInst,LPSTR lpszCmdLine,int nCmdShow)
{ HWND hwnd; MSG Msg;
WNDCLASS wndclass;
char lpszClassName[]="窗口 ";
char lpszTitle[]="My_Windows";
wndclass.style=0;
… …
wndclass.lpszClassName=lpszClassName;
if(!RegisterClass(&wndclass))
{ MessageBeep(0); return FALSE; }
hwnd=CreateWindow(lpszClassName,lpszTitle,WS_VISIBLE|WS_SYSMENU|WS_MINIMIZEB
OX,50,50,450,450,NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,nCmdShow);
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)
{ PAINTSTRUCT ps;
HDC hdc;
TEXTMETRIC tm;
HBRUSH hbrush;
HPEN hpen;
HCURSOR hcursor;
char str[15];
static int x,y;
switch(message)
{
case WM_MOUSEMOVE:
x=LOWORD(lParam);
y=HIWORD(lParam);
if(x>50&&x<150&&y>50&&y<150)
{ hcursor=LoadCursor(NULL,IDC_CROSS);//十字光标
SetCursor(hcursor);
}if(x>150&&x<250&&y>50&&y<150)
{
hcursor=LoadCursor(NULL,IDC_SIZEALL);//十字箭头光标
SetCursor(hcursor);
}
if(x>250&&x<350&&y>50&&y<150)
{ hcursor=LoadCursor(NULL,IDC_SIZENESW);SetCursor(hcursor);}//西南 -东北光标
if(x>50&&x<150&&y>150&&y<250)
{ hcursor=LoadCursor(NULL,IDC_IBEAM);SetCursor(hcursor);}//I字型光标
if(x>50&&x<150&&y>250&&y<350)
{ hcursor=LoadCursor(NULL,IDC_SIZEWE);SetCursor(hcursor); } //东西向
箭头
if(x>150&&x<250&&y>150&&y<250)
{ hcursor=LoadCursor(NULL,IDC_WAIT);SetCursor(hcursor);} //沙漏光标
if(x>250&&x<350&&y>150&&y<250)
{ hcursor=LoadCursor(NULL,IDC_UPARROW);SetCursor(hcursor);} //向上箭头光标
if(x>150&&x<250&&y>250&&y<350)
{ hcursor=LoadCursor(NULL,IDC_SIZENWSE);SetCursor(hcursor);} //西北 -东南箭
头
if(x>250&&x<350&&y>250&&y<350)
{ hcursor=LoadCursor(NULL,IDC_SIZENS);SetCursor(hcursor); } //南北向
箭头
hdc=GetDC(hwnd);
GetTextMetrics(hdc,&tm);
hbrush=(HBRUSH)GetStockObject(WHITE_BRUSH);
SelectObject(hdc,hbrush);
hpen=(HPEN)GetStockObject(WHITE_PEN);
SelectObject(hdc,hpen);
Rectangle(hdc,5,5,tm.tmAveCharWidth*8,tm.tmHeight+3);
sprintf(str,"%d,%d\0",x,y);
TextOut(hdc,5,5,str,lstrlen(str));
EndPaint(hwnd,&ps);
break;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
hpen=(HPEN)GetStockObject(BLACK_PEN);
SelectObject(hdc,hpen);
Rectangle(hdc,50,50,350,350);
MoveToEx(hdc,50,150,NULL); LineTo(hdc,350,150);
MoveToEx(hdc,50,250,NULL); LineTo(hdc,350,250);
MoveToEx(hdc,150,50,NULL); LineTo(hdc,150,350);
MoveToEx(hdc,250,50,NULL); LineTo(hdc,250,350);
EndPaint(hwnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
default,return DefWindowProc(hwnd,message,wParam,lParam);
}
return(0);
}
【 6-4】 编写一个鼠标应用程序, 按下鼠标左键在窗口中移动时, 将按下左键时所在点
和当前点所形成的矩形涂成灰色, 此时光标为十字型 。 当抬起鼠标左键时, 将前面所绘
制度矩形拉伸到整个窗口, 拉伸过程中将光标设置为沙漏型 。 然后, 若双击鼠标的左键,
则灰色消失, 窗口恢复到初始状态 。
void InvertBlock (HWND hwnd,POINT ptBeg,POINT ptEnd)
{
HDC hdc ;
hdc = CreateDC ("DISPLAY",NULL,NULL,NULL) ;
ClientToScreen (hwnd,&ptBeg) ;//转换指定窗口的客户区内的点的坐标
ClientToScreen (hwnd,&ptEnd) ;
PatBlt (hdc,ptBeg.x,ptBeg.y,ptEnd.x - ptBeg.x,ptEnd.y - ptBeg.y,DSTINVERT) ;
//这个函数在矩形区内用当前的画笔重画背景和前景 */
DeleteDC (hdc) ;
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,
WPARAM wParam,LPARAM lParam)
{ static BOOL operate=FALSE,ready=TRUE;
static POINT BeginP,EndP;
static RECT rect={0,0,0,0} ;
HDC hdc ;
PAINTSTRUCT PtStr;
HBRUSH hBrush;
switch (message)
{ case WM_LBUTTONDOWN:
if ((!operate)&&ready)
{ operate=TRUE ; //左键击活俘获
ready=FALSE;
SetCapture (hwnd) ;//把所有的鼠标信息输入到被左键击活的窗口,
SetCursor (LoadCursor (NULL,IDC_CROSS)) ; //载入光标
BeginP.x=LOWORD(lParam);
BeginP.y=HIWORD(lParam);
}
return 0 ;
case WM_MOUSEMOVE:
if (operate)
{ EndP.x=LOWORD(lParam);
EndP.y=HIWORD(lParam);
rect.left=BeginP.x<EndP.x?BeginP.x:EndP.x;
rect.right=BeginP.x>EndP.x?BeginP.x:EndP.x;
rect.top=BeginP.y<EndP.y?BeginP.y:EndP.y;
rect.bottom=BeginP.y>EndP.y?BeginP.y:EndP.y;
SetCursor (LoadCursor (NULL,IDC_WAIT)) ; //载入沙漏光标
InvalidateRect(hwnd,NULL,TRUE);
}
return 0 ;
case WM_PAINT:
if(ready==FALSE)
{ hdc=BeginPaint(hwnd,&PtStr);
hBrush=(HBRUSH)GetStockObject(LTGRAY_BRUSH);
SelectObject(hdc,hBrush);
Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
DeleteObject(hBrush); EndPaint(hwnd,&PtStr);
}
else
{ hdc=BeginPaint(hwnd,&PtStr);
GetClientRect(hwnd,&rect);
Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
EndPaint(hwnd,&PtStr);
}
return 0;
case WM_LBUTTONUP:
if (operate)
{ operate=FALSE;
SetCursor (LoadCursor (NULL,IDC_WAIT));
GetClientRect (hwnd,&rect) ;
InvalidateRect(hwnd,NULL,TRUE);
SetCursor(LoadCursor(NULL,IDC_ARROW));
ReleaseCapture () ; //把鼠标从当前窗口中释放出来
}
return 0 ;
case WM_LBUTTONDBLCLK:
if(ready==FALSE)
{ ready=TRUE;
InvalidateRect(hwnd,NULL,TRUE);
}
return 0;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd,message,wParam,lParam) ;
}