前面说过,windows程序是由消息驱动的,我们对程序的操作也可以被包装成一个消息,投递到程序的消息队列中,窗口过程函数通过对这个消息处理,达到响应输入的目的。
常见的键盘消息有以下几种:
WM_KEYDOWN //键盘按下
WM_KEYUP //键盘松开
WM_SYSKEYDOWN //系统键按下
WM_SYSKEYUP //系统键松开
WM_CHAR //字符消息
在这里,KEYDOWN和KEYUP都是成对发生的,比如要判断一个瞬时输入,那么用KEYDOWN和KEYUP是一样的,如果要判断连续性的按下,那么就需要同时使用KEYDOWN和KEYUP了,但是还有个更加简单的方法,GetMesageTime可以获取消息的相对时间,详情请查阅MSDN
但是,窗口过程函数接受的只是WM_XXX消息,仅仅判断到了键盘有输入,怎么确定是哪个键被按下了呢?
前面说过,窗口过程函数有接受两个参数,wParam和lParam,消息的结构体里面也有这两个参数。按键的信息就存储在这两个参数里面:
wParam参数存储的就是按键的虚拟键码,lParam存储的是按键的相关信息。在windows中,虚拟键码对应键盘上的一个实际按键,”虚拟“也是windows操作系统常用的东东,比如虚拟存储,虚拟设备,这样有什么好处呢?一方面,就是我们在编程的时候,不需要考虑底层的一些硬件信息,只需要调用windows为我们写好的api就可以操作底层的硬件,大大的方便了我们这些程序猿编写程序,另一方面,我们所编写的程序对操作系统而言是“不可信的”,操作系统需要将我们和底层硬件隔离,以免破坏计算机,假如我们这些菜鸟都可以直接操作硬件,那我们的计算机不知要爆炸多少次了,哈哈!
下面看一个例子:
/* time: 2014.10.25 topic: windows键盘输入 */ #include <windows.h> #include "TCHAR.h" //窗口过程函数 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); void Paint(HWND, LPCWSTR); //winmain函数 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { //设计窗口 WNDCLASSEX wndClass = { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0L, 0L, hInstance, NULL, LoadCursor(NULL, IDC_ARROW), (HBRUSH)GetStockObject(GRAY_BRUSH), NULL, L"hello windows", NULL }; //注册窗口 RegisterClassEx(&wndClass); //创建窗口 HWND hWnd = CreateWindow(L"hello windows", L"hello windows", WS_EX_OVERLAPPEDWINDOW, 0, 0, 400, 400, NULL, NULL, hInstance, NULL); //调整设定位置大小函数 MoveWindow(hWnd, 100, 100, 400, 400, true); //显示窗口 ShowWindow(hWnd, nCmdShow); //更新窗口 UpdateWindow(hWnd); //getmessage消息循环 MSG msg = {0}; //从消息队列中取消息 while(GetMessage(&msg, NULL, 0, 0)) { //只有WM_QUIT消息才会让Getmessage函数返回0,其他时间非0,消息循环判断条件一直为真,直至有WM_QUIT消息 //将虚拟键消息翻译为字符消息 TranslateMessage(&msg); //分发一个消息给窗口程序 DispatchMessage(&msg); } return 0; } //窗口过程函数 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_KEYDOWN://处理键盘消息 switch (wParam) { //如果是ESC键,关闭程序 case VK_ESCAPE: //销毁窗口 DestroyWindow(hwnd); //退出程序 PostQuitMessage(0); break; //如果是FI键,输出F1被按下 case VK_F1: //Paint函数,输出按键信息 Paint(hwnd, L"F1 is pressed!"); break; //当按键是字母键时,可以直接用字母的字符当做case处理 case 'A': //Paint函数,输出按键信心 Paint(hwnd, L"A is pressed!"); break; default: break; } break; default: return DefWindowProc(hwnd, message, wParam, lParam);//默认窗口过程函数 } return 0; } //输出函数 void Paint(HWND hwnd, LPCWSTR buffer) { //获取设备环境句柄 HDC hdc = GetDC(hwnd); //输出按键信息 TextOut(hdc, 100, 100, buffer, wcslen(buffer)); //释放设备环境 ReleaseDC(hwnd, hdc); }运行结果:
和键盘消息一样,鼠标消息也是WM_XXX消息,常见的鼠标消息如下:
WM_LBUTTONDBLCLCK
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_RBUTTONDBLCLK
WM_RBUTTONDOWN]
WM_RBUTTONUP
WM_MBUTTONDOWN
WM_MBUTTONUP
WM_MOUSEMOVE
WM_MOUSEWHEEL
其实windows的消息从字面上也都很好理解,WM_是windows message的缩写,LBUTTON,MBUTTON,RBUTTON分别代表左键,滚轮,右键,UP,DOWN,DBLCLCK代表松开,按下,双击
MOUSEMOVE是鼠标移动,MOUSEWHEEL是滚轮滚动
同键盘消息处理一样,WM_XXX消息只是代表了鼠标有特定的操作,具体的信息还是保存在wParam和lParam参数中:
wParam参数存储的是鼠标按键以及Ctrl和Shift键的状态信息,这个参数经常用于检测某个键是否被按下,比如:
if(wParam & MK_LBUTTON) { //鼠标左键被按下处理代码 }最常用的还是lParam参数,这个参数里面存储的是鼠标的位置
但是要读取这个参数,还需要两个宏定义,LOWORD和HIWORD。我们知道,lParam和wParam都是32位整型变量,LOWORD和HIWORD表示的意思就是取低字节位和取高字节位,在lParam参数中,低字节位存储的为X坐标值,高字节位存储的为Y坐标值。
关于字节位,扩展一下知识:一个字节为8个二进制位,有的机器为低字节序(Little Endian),有的机器为高字节序(Big Endian)
所谓低字节序,就是低地址存放低字节数据,高地址存放高字节数据;高字节序则相反。
例如:
把9025H存入1000H地址中 低字节序的存法为 00001000H 25H 00001001H 90H 高字节序存法为 00001000H 90H 00001001H 25H
我们一般使用的Intel CPU采用的是低字节在前的字节序。
好像有点儿远了,还是说LOWORD和HIWORD,对于鼠标消息,我们可以这样获取鼠标的坐标:
case WM_LBUTTONDOWN: { int x = LOWORD(lParam); int y = HIWORD(lParam); wchar_t str[20] = {0}; wsprintf(str, L"坐标为%d.%d", x, y); Paint(hwnd, str); break; }这里我们还是调用键盘消息那里所写的Paint函数来输出信息,程序运行结果如下: