第七章 鼠标

7.1 鼠标的基础知识

fMouse = GetSystemMetrics(SM_MOUSEPRESENT); //判断是否使用鼠标

cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS); //判断安装鼠标的个数

GetSystemMetrics(SM_SWAPBUTTON); //判断鼠标按钮是否被切换

SystemParametersInfo获得鼠标相关的参数信息

7.1.1 一些基本术语

IDC_ARROW

IDC_CROSS

IDC_WAIT

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); //加载鼠标指针图标

LBUTTON, MBUTTON, RBUTTON

7.1.2 鼠标的复数形式是什么?

7.2 客户区鼠标消息

只要鼠标点击了窗口,即使非活动也能收到消息。

x = LOWORD(lParam);

y = HIWORD(lParam);

参数wParam表示鼠标按钮Shift和Ctrl的状态

wParam & MK_SHIFT  当收到WM_LBUTTONDOWN 表示按下了左键的同时又按下了SHIFT键钮。

7.2.1 简单的鼠标处理示例

#include <windows.h>#define MAXPOINTS  1000LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static     TCHAR szAppName[] = TEXT("Connect");HWND     hwnd;MSG            msg;WNDCLASS    wndClass;       //The window ClasswndClass.style = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,     //Window class nameTEXT("Connect-the-Points Mouse Demo"),     //Window captionWS_OVERLAPPEDWINDOW,            //Window StyleCW_USEDEFAULT,                    //initial x positionCW_USEDEFAULT,                  //initial y positionCW_USEDEFAULT,                  //initial x sizeCW_USEDEFAULT,                  //initial y sizeNULL,                           //parent window handleNULL,                         //window menu handlehInstance,                      //program instance handleNULL);                         //creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd); //This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static POINT   pt[MAXPOINTS];static int        iCount;HDC              hdc;int             i, j;PAINTSTRUCT        ps;switch (message) //get the message{case WM_LBUTTONDOWN:iCount = 0;InvalidateRect(hwnd, NULL, TRUE);return 0;case WM_MOUSEMOVE:if (wParam & MK_LBUTTON && iCount < 1000){pt[iCount].x = LOWORD(lParam);pt[iCount++].y = HIWORD(lParam);hdc = GetDC(hwnd);SetPixel(hdc, LOWORD(lParam), HIWORD(lParam), 0);ReleaseDC(hwnd, hdc);}return 0;case WM_LBUTTONUP:InvalidateRect(hwnd, NULL, FALSE);return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);SetCursor(LoadCursor(NULL, IDC_WAIT));ShowCursor(TRUE);for(i = 0; i < iCount - 1; i++)for (j = i + 1; j < iCount; j++){MoveToEx(hdc, pt[i].x, pt[i].y, NULL);LineTo(hdc, pt[j].x, pt[j].y);}ShowCursor(FALSE);SetCursor(LoadCursor(NULL, IDC_ARROW));EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return  DefWindowProc(hwnd, message, wParam, lParam);
}

运行结果如下

7.2.2 处理Shift键

if (wParam & MK_SHIFT)
{if(wParam & MK_CONTROL){//press shift+ctrl}else{//press shift}
}
else
{if(wParam & MK_CONTROL){//press ctrl}else{//neither press shift nor ctrl}
}

使用左键配合Shift键等效右键

case WM_LBUTTONDOWN:if(!(wParam & MK_SHIFT)){//process for left button down.return 0;}
case WM_RBUTTONDOWN:// process for the right button down.return 0;

7.2.3 鼠标双击

wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

用户双击以后窗口收到的消息如下:

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDBLCLK  //如果未定义双击 则为 WM_LBUTTONDOWN

WM_LBUTTONUP

7.3 非客户区鼠标消息

非客户区域包括 标题栏,菜单和窗口滚动条

系统一般不需要用户处理非客户区鼠标消息,交给DefWindowProc即可

wParam 表示非客户区鼠标移动或单击的位置 其值是 HT_ 为首的一些值

lParam 低位包含x坐标,高位包含y坐标 都是屏幕坐标

ScreenToClient(hwnd, &pt);

ClientToScreen(hwnd, &pt);

7.3.1 击中测试消息

WM_NCHITTEST  非客户区击中测试 优先级高于其他一切鼠标消息

lParam  表示屏幕坐标 x, y   wParam 无用

DefWindowProc处理改消息会产生wParam的值

HTCLIENT  客户区

HTNOWHERE 不再客户区

HTTRANSPARENT 被另一个窗口覆盖的窗口

HTERROR  使DefWindowProc产生一个警示声

例如捕捉WM_SYSKEYDOWN 使所有系统函数键盘失效

case  WM_NCHITTEST:

return (LRESULT) HITNOWHERE;

可以阻止系统向窗口发送的所有客户区和非客户区鼠标消息。此时所有鼠标按钮操作都失效

7.3.2 消息引发消息

例如双击系统标题栏图标关闭程序

产生WM_NCHITTEST消息  ,  DefWindowProc处理 返回HTSYSMENU, 同时添加WM_NCLBUTTONDBLCLK消息

然后DefWindowProc又处理该消息,参数wParam为HTSYSMENU。 系统会在消息队列家一个WM_SYSCOMMAND消息,其中参数是SC_CLOSE.

然后DefWindowProc又处理该消息,并向窗口发送WM_CLOSE

如果程序在结束前想等待用户确认,可以捕捉WM_CLOSE消息。 否则DefWindowProc捕捉该消息以后会像窗口发送WM_DESTROY:

而WM_DESTROY一般做如下处理:

case WM_DESTROY:
PostQuitMessage(0);
return 0;
}

会是系统在消息队列中加一条WM_QUIT消息,  而消息循环GetMessage捕捉到该消息会返回0.从而程序退出。

7.4 程序中的击中测试

一般是对传递到窗口过程的x,y的一些计算 其中x,y的值在lParam中

7.4.1 一个假想的例子

7.4.2 一个简单的程序

#include <windows.h>#define DIVISIONS  5LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static     TCHAR szAppName[] = TEXT("Checker1");HWND        hwnd;MSG            msg;WNDCLASS    wndClass;       //The window ClasswndClass.style = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,     //Window class nameTEXT("Checker1 Mouse Hit-Test Demo"),      //Window captionWS_OVERLAPPEDWINDOW,            //Window StyleCW_USEDEFAULT,                    //initial x positionCW_USEDEFAULT,                  //initial y positionCW_USEDEFAULT,                  //initial x sizeCW_USEDEFAULT,                  //initial y sizeNULL,                           //parent window handleNULL,                         //window menu handlehInstance,                      //program instance handleNULL);                         //creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd); //This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static BOOL        fState[DIVISIONS][DIVISIONS];static int     cxBlock, cyBlock;HDC                hdc;int             x, y;PAINTSTRUCT        ps;RECT         rect;switch (message) //get the message{case WM_SIZE:cxBlock = LOWORD(lParam) / DIVISIONS;cyBlock = HIWORD(lParam) / DIVISIONS;return 0;case WM_LBUTTONDOWN:x = LOWORD(lParam) / cxBlock;y = HIWORD(lParam) / cyBlock;if (x < DIVISIONS && y < DIVISIONS){fState[x][y] ^= 1;rect.left = x * cxBlock;rect.top = y * cyBlock;rect.right = (x + 1) * cxBlock;rect.bottom = (y + 1) * cyBlock;InvalidateRect(hwnd, &rect, FALSE);}elseMessageBeep(0);return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);for (x = 0; x < DIVISIONS; x++)for (y = 0; y < DIVISIONS; y++){Rectangle(hdc, x * cxBlock, y * cyBlock,(x + 1) * cxBlock, (y + 1) * cyBlock);if (fState[x][y]){MoveToEx(hdc, x * cxBlock, y * cyBlock, NULL);LineTo (hdc, (x + 1) * cxBlock, (y + 1) * cyBlock);MoveToEx(hdc, x * cxBlock, (y + 1) * cyBlock, NULL);LineTo   (hdc, (x + 1) * cxBlock, y * cyBlock);}}EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return  DefWindowProc(hwnd, message, wParam, lParam);
}

运行结果,用户点击屏幕上相应的区块,会绘制一个×, 再次点击×会消失。

7.4.3 使用键盘模仿鼠标操作

增加显示计数器

ShowCursor(TRUE);

减少显示计数器

ShowCursor(FALSE);

获取鼠标指针位置

GetCursorPos(&pt); //用户没有安装鼠标但windows仍然保留鼠标指针的位置,可用此函数获得

也可以设置鼠标指针的位置

SetCursorPos(x, y);

坐标都是屏幕坐标

7.4.4 在Checker中增加键盘接口

#include <windows.h>#define DIVISIONS  5LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static     TCHAR szAppName[] = TEXT("Checker2");HWND        hwnd;MSG            msg;WNDCLASS    wndClass;       //The window ClasswndClass.style = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,     //Window class nameTEXT("Checker2 Mouse Hit-Test Demo"),      //Window captionWS_OVERLAPPEDWINDOW,            //Window StyleCW_USEDEFAULT,                    //initial x positionCW_USEDEFAULT,                  //initial y positionCW_USEDEFAULT,                  //initial x sizeCW_USEDEFAULT,                  //initial y sizeNULL,                           //parent window handleNULL,                         //window menu handlehInstance,                      //program instance handleNULL);                         //creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd); //This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static BOOL        fState[DIVISIONS][DIVISIONS];static int     cxBlock, cyBlock;HDC                hdc;int             x, y;PAINTSTRUCT        ps;POINT            point;RECT          rect;switch (message) //get the message{case WM_SIZE:cxBlock = LOWORD(lParam) / DIVISIONS;cyBlock = HIWORD(lParam) / DIVISIONS;return 0;case WM_SETFOCUS:ShowCursor(TRUE);return 0;case WM_KILLFOCUS:ShowCursor(FALSE);return 0;case WM_KEYDOWN:GetCursorPos(&point);ScreenToClient(hwnd, &point);x = max(0, min(DIVISIONS - 1, point.x / cxBlock));y = max(0, min(DIVISIONS - 1, point.y / cyBlock));switch (wParam){case VK_UP:y--;break;case VK_DOWN:y++;break;case VK_LEFT:x--;break;case VK_RIGHT:x++;break;case VK_HOME:x = y = 0;break;case VK_END:x = y = DIVISIONS - 1;break;case VK_RETURN:case VK_SPACE:SendMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON,MAKELONG(x * cxBlock, y * cyBlock));break;}x = (x + DIVISIONS) % DIVISIONS;y = (y + DIVISIONS) % DIVISIONS;point.x = x * cxBlock + cxBlock / 2;point.y = y * cyBlock + cyBlock / 2;ClientToScreen(hwnd, &point);SetCursorPos(point.x, point.y);return 0;case WM_LBUTTONDOWN:x = LOWORD(lParam) / cxBlock;y = HIWORD(lParam) / cyBlock;if (x < DIVISIONS && y < DIVISIONS){fState[x][y] ^= 1;rect.left = x * cxBlock;rect.top = y * cyBlock;rect.right = (x + 1) * cxBlock;rect.bottom = (y + 1) * cyBlock;InvalidateRect(hwnd, &rect, FALSE);}elseMessageBeep(0);return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);for (x = 0; x < DIVISIONS; x++)for (y = 0; y < DIVISIONS; y++){Rectangle(hdc, x * cxBlock, y * cyBlock,(x + 1) * cxBlock, (y + 1) * cyBlock);if (fState[x][y]){MoveToEx(hdc, x * cxBlock, y * cyBlock, NULL);LineTo   (hdc, (x + 1) * cxBlock, (y + 1) * cyBlock);MoveToEx(hdc, x * cxBlock, (y + 1) * cyBlock, NULL);LineTo   (hdc, (x + 1) * cxBlock, y * cyBlock);}}EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return  DefWindowProc(hwnd, message, wParam, lParam);
}

现在可以使用方向键来移动鼠标,并且通过按space或者enter来模拟鼠标左键

7.4.5 在击中测试中使用子窗口

利用子窗口 来绘制屏幕上的各种小区域,每个小区域有自己的击中测试。子窗口将整个客户区划分成几个更小的矩形区域。每个子窗口都有属于自己的句柄,窗口过程和客户区,并处理自己的鼠标消息。lParam的坐标是子窗口客户区左上角的。而不是父窗口的客户区。如果子窗口使用不同的窗口类,每个子窗口都会有自己的窗口过程。

7.4.6 CHECKER程序中的子窗口

#include <windows.h>#define DIVISIONS  5
TCHAR szChildClass[] = TEXT("Checker3_Child");LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM); //sub window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static     TCHAR szAppName[] = TEXT("Checker3");HWND        hwnd;MSG            msg;WNDCLASS    wndClass;       //The window ClasswndClass.style            = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//we create a sub windows use the new wndclass.wndClass.lpfnWndProc   = ChildWndProc;wndClass.cbWndExtra     = sizeof(long);wndClass.hIcon          = NULL;wndClass.lpszClassName  = szChildClass;//Register the sub classRegisterClass(&wndClass);//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,     //Window class nameTEXT("Checker3 Mouse Hit-Test Demo"),      //Window captionWS_OVERLAPPEDWINDOW,            //Window StyleCW_USEDEFAULT,                    //initial x positionCW_USEDEFAULT,                  //initial y positionCW_USEDEFAULT,                  //initial x sizeCW_USEDEFAULT,                  //initial y sizeNULL,                           //parent window handleNULL,                         //window menu handlehInstance,                      //program instance handleNULL);                         //creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd); //This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static HWND hwndChild[DIVISIONS][DIVISIONS]; //The array to store the window handle of the sub window.int      cxBlock, cyBlock, x, y;switch (message) //get the message{case WM_CREATE:for (x = 0; x < DIVISIONS; x++)for (y = 0; y < DIVISIONS; y++)hwndChild[x][y] = CreateWindow(szChildClass, NULL,WS_CHILDWINDOW | WS_VISIBLE,0, 0, 0, 0,hwnd, (HMENU)(y << 8 | x),(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),NULL);return 0;case WM_SIZE:cxBlock = LOWORD(lParam) / DIVISIONS;cyBlock = HIWORD(lParam) / DIVISIONS;for (x = 0; x < DIVISIONS; x++)for (y = 0; y < DIVISIONS; y++)MoveWindow(hwndChild[x][y], x * cxBlock, y * cyBlock,cxBlock, cyBlock, TRUE);return 0;case WM_LBUTTONDOWN:MessageBeep(0);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return  DefWindowProc(hwnd, message, wParam, lParam);
}LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{HDC            hdc;PAINTSTRUCT ps;RECT     rect;switch (message){case WM_CREATE:SetWindowLong(hwnd, 0, 0); //on/off flag;return 0;case WM_LBUTTONDOWN:SetWindowLong(hwnd, 0, 1 ^ GetWindowLong(hwnd, 0));InvalidateRect(hwnd, NULL, FALSE);return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);GetClientRect(hwnd, &rect);Rectangle(hdc, 0, 0, rect.right, rect.bottom);if (GetWindowLong(hwnd, 0)){MoveToEx (hdc, 0,          0, NULL);LineTo  (hdc, rect.right,  rect.bottom);MoveToEx (hdc, 0,          rect.bottom, NULL);LineTo   (hdc, rect.right,   0);}EndPaint(hwnd, &ps);return 0;}return DefWindowProc(hwnd, message, wParam, lParam);
}

使用子窗口来代替之前的各个矩形区域,省去了坐标的计算。每个窗口有自己的窗口函数来处理各种消息。

在调试该程序的时候,我不小心把主窗口类的名字填成和子窗口类一样了,之后程序创建了1+25个“主”窗口,造成了假死状态。调查了好久才发现这样的低级错误。囧

在创建带有子窗口的程序时,特别要小心不要把窗口类的名字和子窗口类的名字搞混了。

wndClass.lpszClassName

对于子窗口类和主窗口类 有4个字段不同

lpfnWndProc  子窗口函数

cbWndExtra  字段被设定为4个字节 sizeof(long) 通知windows在内部结构中给基于这个窗口类的每个窗口预留4个字节的额外空间。用户可以利用这些空间为每个窗口保持不同信息

hIcon 字段设置为NULL, 子窗口不需要图标

pszClassName 子窗口类的名称,!!!!!非常重要,不要和主窗口类搞混

下表是创建主窗口和子窗口调用参数的对照表 CreateWindow

(HMENU)(y<<8 | x) 子窗口的ID,用来标识子窗口的数值。在处理对话框的子窗口控件时,子ID显得更加重要。

(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE); 获得父窗口的hInstance参数

使用MoveWindow 移动每个子窗口的位置

7.4.7 子窗口和键盘

#include <windows.h>#define DIVISIONS  5
TCHAR szChildClass[] = TEXT("Checker3_Child");
int idFocus = 0;       //It is used to store the sub window ID.LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM); //sub window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static     TCHAR szAppName[] = TEXT("Checker3");HWND        hwnd;MSG            msg;WNDCLASS    wndClass;       //The window ClasswndClass.style            = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//we create a sub windows use the new wndclass.wndClass.lpfnWndProc   = ChildWndProc;wndClass.cbWndExtra     = sizeof(long);wndClass.hIcon          = NULL;wndClass.lpszClassName  = szChildClass;//Register the sub classRegisterClass(&wndClass);//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,     //Window class nameTEXT("Checker3 Mouse Hit-Test Demo"),      //Window captionWS_OVERLAPPEDWINDOW,            //Window StyleCW_USEDEFAULT,                    //initial x positionCW_USEDEFAULT,                  //initial y positionCW_USEDEFAULT,                  //initial x sizeCW_USEDEFAULT,                  //initial y sizeNULL,                           //parent window handleNULL,                         //window menu handlehInstance,                      //program instance handleNULL);                         //creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd); //This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProc
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static HWND hwndChild[DIVISIONS][DIVISIONS]; //The array to store the window handle of the sub window.int      cxBlock, cyBlock, x, y;switch (message) //get the message{case WM_CREATE:for (x = 0; x < DIVISIONS; x++)for (y = 0; y < DIVISIONS; y++)hwndChild[x][y] = CreateWindow(szChildClass, NULL,WS_CHILDWINDOW | WS_VISIBLE,0, 0, 0, 0,hwnd, (HMENU)(y << 8 | x),(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),NULL);return 0;case WM_SIZE:cxBlock = LOWORD(lParam) / DIVISIONS;cyBlock = HIWORD(lParam) / DIVISIONS;for (x = 0; x < DIVISIONS; x++)for (y = 0; y < DIVISIONS; y++)MoveWindow(hwndChild[x][y], x * cxBlock, y * cyBlock,cxBlock, cyBlock, TRUE);return 0;case WM_LBUTTONDOWN:MessageBeep(0);return 0;case WM_SETFOCUS://On set-focus message, set focus to child windowSetFocus(GetDlgItem(hwnd, idFocus));return 0;case WM_KEYDOWN:x = idFocus & 0xFF;y = idFocus >> 8;switch (wParam){case VK_UP:       y--;                    break;case VK_DOWN: y++;                  break;case VK_LEFT: x--;                    break;case VK_RIGHT:    x++;                  break;case VK_HOME: x = y = 0;                break;case VK_END:  x = y = DIVISIONS - 1;    break;default:      return 0;}x = (x + DIVISIONS) % DIVISIONS;y = (y + DIVISIONS) % DIVISIONS;idFocus = y << 8 | x;SetFocus(GetDlgItem(hwnd, idFocus));  //set the focus to the sub window.return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return  DefWindowProc(hwnd, message, wParam, lParam);
}LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{HDC            hdc;PAINTSTRUCT ps;RECT     rect;switch (message){case WM_CREATE:SetWindowLong(hwnd, 0, 0); //on/off flag;return 0;case WM_KEYDOWN://send most key processes to the parent windowif (wParam != VK_RETURN && wParam != VK_SPACE){SendMessage(GetParent(hwnd), message, wParam, lParam);return 0;}//For return and space, fall through to goggle the squrecase WM_LBUTTONDOWN:SetWindowLong(hwnd, 0, 1 ^ GetWindowLong(hwnd, 0));SetFocus(hwnd);InvalidateRect(hwnd, NULL, FALSE);return 0;//For focus messages, invalidate the window for repaintcase WM_SETFOCUS:idFocus = GetWindowLong(hwnd, GWL_ID);//fall throughcase WM_KILLFOCUS:InvalidateRect(hwnd, NULL, TRUE);return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);GetClientRect(hwnd, &rect);Rectangle(hdc, 0, 0, rect.right, rect.bottom);if (GetWindowLong(hwnd, 0)){MoveToEx (hdc, 0,           0, NULL);LineTo  (hdc, rect.right,  rect.bottom);MoveToEx (hdc, 0,          rect.bottom, NULL);LineTo   (hdc, rect.right,   0);}// Draw the "focus" rectangleif (hwnd == GetFocus()){rect.left += rect.right / 10;rect.right -= rect.left;rect.top += rect.bottom / 10;rect.bottom -= rect.top;SelectObject(hdc, GetStockObject(NULL_BRUSH));SelectObject(hdc, CreatePen(PS_DASH, 0, 0));Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));}EndPaint(hwnd, &ps);return 0;}return DefWindowProc(hwnd, message, wParam, lParam);
}

运行结果如下

idChild = GetWindowLong(hwndChild, GWL_ID); 获得子窗口ID

或者这样

idChild = GetDlgCtrlID(hwnChild);

知道子窗口的ID还能获得子窗口的句柄

hwndChild = GetDlgItem(hwnd, idChild);

在整个程序失去焦点以后重新获得焦点的时候,默认不会自动聚焦子窗口。所以要添加主窗口的聚焦响应让其自动聚焦到子窗口上。

子窗口在接收到鼠标左键消息的时候会自动响应聚焦

7.5 捕获鼠标

7.5.1 设计一个矩形

#include <windows.h>LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static     TCHAR szAppName[] = TEXT("BlockOut1");HWND       hwnd;MSG            msg;WNDCLASS    wndClass;       //The window ClasswndClass.style            = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,        //Window class nameTEXT("Mouser Button Demo"),        //Window captionWS_OVERLAPPEDWINDOW,            //Window StyleCW_USEDEFAULT,                    //initial x positionCW_USEDEFAULT,                  //initial y positionCW_USEDEFAULT,                  //initial x sizeCW_USEDEFAULT,                  //initial y sizeNULL,                           //parent window handleNULL,                         //window menu handlehInstance,                      //program instance handleNULL);                         //creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd); //This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/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);//Pixel is the inverse of the screen color. //So we could erase the drawing the previous time.SetROP2(hdc, R2_NOT); SelectObject(hdc, GetStockObject(NULL_BRUSH));Rectangle(hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y);ReleaseDC(hwnd, hdc);
}//define the Window Procedure WndProc
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) //get the 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 == TEXT('\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);
}

一个类似画图的简单程序

但是该程序有问题,一旦用户在鼠标按下的过程中将鼠标移出客户区,再移回窗口。程序会以为鼠标仍然在按下的状态。

可以做一些修改

将WM_MOUSEMOVE 消息的代码该一下,在鼠标回客户区以后就会自动判断绘图完成了

 case WM_MOUSEMOVE:if (fBlocking){if (wParam & MK_LBUTTON){SetCursor(LoadCursor(NULL, IDC_CROSS));DrawBoxOutLine(hwnd, ptBeg, ptEnd);ptEnd.x = LOWORD(lParam);ptEnd.y = HIWORD(lParam);DrawBoxOutLine(hwnd, ptBeg, ptEnd);}else{SendMessage(hwnd, WM_LBUTTONUP, wParam, lParam);}}return 0;

7.5.2 捕获解决方案

SetCapture(hwnd); //鼠标捕获器

之后windows会将所有鼠标消息都发给HWND窗口。鼠标消息总是以客户区消息形式出现,即使鼠标位于非客户区。

如果想释放捕获鼠标就使用

ReleaseCapture();

一切恢复正常

为了防止混乱应在在鼠标在客户区按下时才捕获鼠标,当释放按钮时,应该同时停止捕获。

7.5.3 Blockout2 程序

使用了鼠标捕获这样在按下左键的同时鼠标移出客户区仍然能够捕获鼠标消息

#include <windows.h>LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static     TCHAR szAppName[] = TEXT("BlockOut2");HWND       hwnd;MSG            msg;WNDCLASS    wndClass;       //The window ClasswndClass.style            = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,        //Window class nameTEXT("Mouser Button & Capture Demo"),      //Window captionWS_OVERLAPPEDWINDOW,            //Window StyleCW_USEDEFAULT,                    //initial x positionCW_USEDEFAULT,                  //initial y positionCW_USEDEFAULT,                  //initial x sizeCW_USEDEFAULT,                  //initial y sizeNULL,                           //parent window handleNULL,                         //window menu handlehInstance,                      //program instance handleNULL);                         //creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd); //This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/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);//Pixel is the inverse of the screen color. //So we could erase the drawing the previous time.SetROP2(hdc, R2_NOT); SelectObject(hdc, GetStockObject(NULL_BRUSH));Rectangle(hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y);ReleaseDC(hwnd, hdc);
}//define the Window Procedure WndProc
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) //get the 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 == TEXT('\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);
}

7.6 鼠标的滚轮

对SYSMETS程序的进一步改进,增加了鼠标的各种操作和滚轮支持

sysmets.h 文件代码

#define NUMLINES ((int)(sizeof sysmetrics / sizeof sysmetrics[0]))struct
{int        iIndex;TCHAR*   szLabel;TCHAR*  szDesc;
}sysmetrics[] =
{SM_CXSCREEN, TEXT("SM_CXSCREEN"),TEXT("Screen width in pixels"),SM_CYSCREEN, TEXT("SM_CYSCREEN"),TEXT("Screen height in pixels"),SM_CXVSCROLL, TEXT("SM_CXVSCROLL"),TEXT("Vertical scroll width"),SM_CYHSCROLL, TEXT("SM_CYHSCROLL"),TEXT("Horizontal scroll height"),SM_CYCAPTION, TEXT("SM_CYCAPTION"),TEXT("Caption bar height"),SM_CXBORDER, TEXT("SM_CXBORDER"),TEXT("Window border width"),SM_CYBORDER, TEXT("SM_CYBORDER"),TEXT("Window border height"),SM_CXFIXEDFRAME, TEXT("SM_CXFIXEDFRAME"),TEXT("Dialog window frame width"),SM_CYFIXEDFRAME, TEXT("SM_CYFIXEDFRAME"),TEXT("Dialog window frame height"),SM_CYVTHUMB, TEXT("SM_CYVTHUMB"),TEXT("Vertical scroll thumb height"),SM_CXHTHUMB, TEXT("SM_CXHTHUMB"),TEXT("Horizontal scroll thumb width"),SM_CXICON, TEXT("SM_CXICON"),TEXT("Icon width"),SM_CYICON, TEXT("SM_CYICON"),TEXT("Icon height"),SM_CXCURSOR, TEXT("SM_CXCURSOR"),TEXT("Cursor width"),SM_CYCURSOR, TEXT("SM_CYCURSOR"),TEXT("Cursor height"),SM_CYMENU, TEXT("SM_CYMENU"),TEXT("Menu bar height"),SM_CXFULLSCREEN, TEXT("SM_CXFULLSCREEN"),TEXT("Full screen client area width"),SM_CYFULLSCREEN, TEXT("SM_CYFULLSCREEN"),TEXT("Full screen client area height"),SM_CYKANJIWINDOW, TEXT("SM_CYKANJIWINDOW"),TEXT("Kanji window height"),SM_MOUSEPRESENT, TEXT("SM_MOUSEPRESENT"),TEXT("Mouse present flag"),SM_CYVSCROLL, TEXT("SM_CYVSCROLL"),TEXT("Vertical scroll arrow height"),SM_CXHSCROLL, TEXT("SM_CXHSCROLL"),TEXT("Horizontal scroll arrow width"),SM_DEBUG, TEXT("SM_DEBUG"),TEXT("Debug version flag"),SM_SWAPBUTTON, TEXT("SM_SWAPBUTTON"),TEXT("Mouse buttons swapped flag"),SM_CXMIN, TEXT("SM_CXMIN"),TEXT("Minimum window width"),SM_CYMIN, TEXT("SM_CYMIN"),TEXT("Minimum window height"),SM_CXSIZE, TEXT("SM_CXSIZE"),TEXT("Min/Max/Close button width"),SM_CYSIZE, TEXT("SM_CYSIZE"),TEXT("Min/Max/Close button height"),SM_CXSIZEFRAME, TEXT("SM_CXSIZEFRAME"),TEXT("Window sizing frame width"),SM_CYSIZEFRAME, TEXT("SM_CYSIZEFRAME"),TEXT("Window sizing frame height"),SM_CXMINTRACK, TEXT("SM_CXMINTRACK"),TEXT("Minimum window tracking width"),SM_CYMINTRACK, TEXT("SM_CYMINTRACK"),TEXT("Minimum window tracking height"),SM_CXDOUBLECLK, TEXT("SM_CXDOUBLECLK"),TEXT("Double click x tolerance"),SM_CYDOUBLECLK, TEXT("SM_CYDOUBLECLK"),TEXT("Double click y tolerance"),SM_CXICONSPACING, TEXT("SM_CXICONSPACING"),TEXT("Horizontal icon spacing"),SM_CYICONSPACING, TEXT("SM_CYICONSPACING"),TEXT("Vertical icon spacing"),SM_MENUDROPALIGNMENT, TEXT("SM_MENUDROPALIGNMENT"),TEXT("Left or right menu drop"),SM_PENWINDOWS, TEXT("SM_PENWINDOWS"),TEXT("Pen extensions installed"),SM_DBCSENABLED, TEXT("SM_DBCSENABLED"),TEXT("Double-Byte Char Set enabled"),SM_CMOUSEBUTTONS, TEXT("SM_CMOUSEBUTTONS"),TEXT("Number of mouse buttons"),SM_SECURE, TEXT("SM_SECURE"),TEXT("Security present flag"),SM_CXEDGE, TEXT("SM_CXEDGE"),TEXT("3-D border width"),SM_CYEDGE, TEXT("SM_CYEDGE"),TEXT("3-D border height"),SM_CXMINSPACING, TEXT("SM_CXMINSPACING"),TEXT("Minimized window spacing width"),SM_CYMINSPACING, TEXT("SM_CYMINSPACING"),TEXT("Minimized window spacing height"),SM_CXSMICON, TEXT("SM_CXSMICON"),TEXT("Small icon width"),SM_CYSMICON, TEXT("SM_CYSMICON"),TEXT("Small icon height"),SM_CYSMCAPTION, TEXT("SM_CYSMCAPTION"),TEXT("Small caption height"),SM_CXSMSIZE, TEXT("SM_CXSMSIZE"),TEXT("Small caption button width"),SM_CYSMSIZE, TEXT("SM_CYSMSIZE"),TEXT("Small caption button height"),SM_CXMENUSIZE, TEXT("SM_CXMENUSIZE"),TEXT("Menu bar button width"),SM_CYMENUSIZE, TEXT("SM_CYMENUSIZE"),TEXT("Menu bar button height"),SM_ARRANGE, TEXT("SM_ARRANGE"),TEXT("How minimized windows arranged"),SM_CXMINIMIZED, TEXT("SM_CXMINIMIZED"),TEXT("Minimized window width"),SM_CYMINIMIZED, TEXT("SM_CYMINIMIZED"),TEXT("Minimized window height"),SM_CXMAXTRACK, TEXT("SM_CXMAXTRACK"),TEXT("Maximum draggable width"),SM_CYMAXTRACK, TEXT("SM_CYMAXTRACK"),TEXT("Maximum draggable height"),SM_CXMAXIMIZED, TEXT("SM_CXMAXIMIZED"),TEXT("Width of maximized window"),SM_CYMAXIMIZED, TEXT("SM_CYMAXIMIZED"),TEXT("Height of maximized window"),SM_NETWORK, TEXT("SM_NETWORK"),TEXT("Network present flag"),SM_CLEANBOOT, TEXT("SM_CLEANBOOT"),TEXT("How system was booted"),SM_CXDRAG, TEXT("SM_CXDRAG"),TEXT("Avoid drag x tolerance"),SM_CYDRAG, TEXT("SM_CYDRAG"),TEXT("Avoid drag y tolerance"),SM_SHOWSOUNDS, TEXT("SM_SHOWSOUNDS"),TEXT("Present sounds visually"),SM_CXMENUCHECK, TEXT("SM_CXMENUCHECK"),TEXT("Menu check-mark width"),SM_CYMENUCHECK, TEXT("SM_CYMENUCHECK"),TEXT("Menu check-mark height"),SM_SLOWMACHINE, TEXT("SM_SLOWMACHINE"),TEXT("Slow processor flag"),SM_MIDEASTENABLED, TEXT("SM_MIDEASTENABLED"),TEXT("Hebrew and Arabic enabled flag"),SM_MOUSEWHEELPRESENT, TEXT("SM_MOUSEWHEELPRESENT"),TEXT("Mouse wheel present flag"),SM_XVIRTUALSCREEN, TEXT("SM_XVIRTUALSCREEN"),TEXT("Virtual screen x origin"),SM_YVIRTUALSCREEN, TEXT("SM_YVIRTUALSCREEN"),TEXT("Virtual screen y origin"),SM_CXVIRTUALSCREEN, TEXT("SM_CXVIRTUALSCREEN"),TEXT("Virtual screen width"),SM_CYVIRTUALSCREEN, TEXT("SM_CYVIRTUALSCREEN"),TEXT("Virtual screen height"),SM_CMONITORS, TEXT("SM_CMONITORS"),TEXT("Number of monitors"),SM_SAMEDISPLAYFORMAT, TEXT("SM_SAMEDISPLAYFORMAT"),TEXT("Same color format flag")
};

sysmets.cpp

#include <windows.h>
#include "sysmets.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static     TCHAR szAppName[] = TEXT("SysMets");HWND     hwnd;MSG            msg;WNDCLASS    wndClass;       //The window ClasswndClass.style            = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,        //Window class nameTEXT("Get System Metrics"),        //Window captionWS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,          //Window StyleCW_USEDEFAULT,                    //initial x positionCW_USEDEFAULT,                  //initial y positionCW_USEDEFAULT,                  //initial x sizeCW_USEDEFAULT,                  //initial y sizeNULL,                           //parent window handleNULL,                         //window menu handlehInstance,                      //program instance handleNULL);                         //creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd); //This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProc
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 logicHDC          hdc;int         i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd;PAINTSTRUCT   ps;SCROLLINFO   si;TCHAR        szBuffer[10];TEXTMETRIC tm;ULONG        ulScrollLines;      //for mouse wheel logicswitch (message) //get the 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 columsiMaxWidth = 40 * cxChar + 22 * cxCaps;//fall through for mouse wheel informationcase 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 40if (ulScrollLines)iDeltaPerLine = WHEEL_DELTA / ulScrollLines;elseiDeltaPerLine = 0;return 0;case WM_SIZE:cxClient = LOWORD(lParam);cyClient = HIWORD(lParam);//Set vertical scroll bar range and page sizesi.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 the horizontal scroll bar range and page sizesi.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 informationsi.cbSize = sizeof(si);si.fMask = SIF_ALL;GetScrollInfo(hwnd, SB_VERT, &si);//Save the position for comparison later oniVertPos = 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_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_VERT, &si, TRUE);GetScrollInfo(hwnd, SB_VERT, &si);// if the position has changed, scroll the window and update itif (si.nPos != iVertPos){ScrollWindow(hwnd, 0, cyChar*(iVertPos - si.nPos),NULL, NULL);UpdateWindow(hwnd); //instead of the invalidateRect() it will update the rect immediately.}return 0;case WM_HSCROLL:// Get all the horizental scroll bar informationsi.cbSize = sizeof(si);si.fMask = SIF_ALL;//Save the position for comparison later onGetScrollInfo(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 been changed, scroll the windowif (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 -120while (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 the vertical scroll bar positionsi.cbSize = sizeof(si);;si.fMask = SIF_POS;GetScrollInfo(hwnd, SB_VERT, &si);iVertPos = si.nPos;//Get horizontal scroll bar positionGetScrollInfo(hwnd, SB_HORZ, &si);iHorzPos = si.nPos;//Find painting limitsiPaintBeg = 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_MOUSEWHEEL消息

wParam的地位是一些按键消息

wParam的高位时一个增量

在WM_CREATE 和WM_SETTINGCHANGE 消息调用了参数为 SPI_GETWHEELSCROLLLINES的SystemParametersInfo函数

表示每个增量值能滚动多少行

《Windows程序设计》读书笔七 鼠标相关推荐

  1. PHP程序设计读书笔记七

    一.比较字符串: 1."=="和"==="的区别: "=="在比较之前会先把非字符串类型的操作数转换成字符串,所以"3" ...

  2. Windows程序设计 读书笔记(3)

    第三章 窗口与消息 3.1 窗口的创建 用户对窗口的输入以'消息'的形式传递给窗口,而窗口也借助消息来与其他窗口进行通信.例如当用户改变窗口的尺寸时,Windows便向应用程序发送一条携带新窗口尺寸相 ...

  3. MFC Windows程序设计 读书笔记2

    Windows中负责图形输出的是Graphics Device Interface(图形设备接口),或称GDI.本节主要讲述CDC类和它的继承类,并介绍三种最常用的GDI基本元素,画笔.画刷.和字体, ...

  4. windows程序设计读书笔记一

    用VC MFC有一段时间了,总觉得还没掌握到精髓,项目代码基本是拼凑起来的.总是少点什么.前段时间,看完 快乐鹦鹉 的程序人生, 里面提到 全局变量 的一段.突然想到自己用 vc+opencv的一个小 ...

  5. windows程序设计读书笔记四

    前面的程序碰到刷新时碰到闪烁问题,百度一搜,一堆关于双缓冲解决闪烁的文章. 具体实施方案为: 1.建立一个内存DC hdcMem = CreateCompatibleDC(hdc); 此时,内存DC的 ...

  6. java pda开发_《Java 手机/PDA 程序设计入门》读书笔

    JDBC针对桌面平台或企业用户设计,不适合移动通讯设备. MIDP为的永久性数据储存提供了面向纪录的数据库(Record Orieted Database),称作"记录管理系统"R ...

  7. MFC Windows 程序设计[七十三]之图表的魅力(附源码)

    MFC Windows 程序设计[七十三]之图表的魅力 程序之美 前言 主体 运行效果 核心代码 逻辑分析 结束语 程序之美 前言 MFC是微软公司提供的一个类库(class libraries),以 ...

  8. 第七章 目录监视 windows程序设计王艳平版

    // DirDialog.h文件 #ifndef __DIRDIALOG_H_ #define __DIRDIALOG_H_ #include <shlobj.h> class CDirD ...

  9. 《Windows 程序设计(第3版)》——6.7 【实例】窗口查看器

    本节书摘来自异步社区<Windows 程序设计(第3版)>一书中的第6章,第6.7节,作者:王艳平 , 张铮著,更多章节内容可以访问云栖社区"异步社区"公众号查看 6. ...

最新文章

  1. win10怎么设置开机启动项目_苹果mac开机启动项怎么设置
  2. 谷歌程序员少输一个“”,差点让全球Chrome笔记本变砖
  3. C语言复杂的学生成绩管理系统,哭诉、拜求C语言学生成绩管理系统
  4. 自动化测试框架搭建-配置-1
  5. 直接排序python实现
  6. 0xbc指令 st75256_DDOS终极加速列车算法
  7. 四十六、和我一起看看,国外的Python考试到底是怎么样(下篇)
  8. GDCM:处理JAI-JPEGLS错误的测试程序
  9. c语言给定一个字符串匹配,使用C语言解决字符串匹配问题的方法
  10. A trip through the Graphics Pipeline 2011_06_(Triangle) rasterization and setup
  11. svn   /lib64/libz.so.1: no version information available
  12. html大作业【NBA篮球介绍 22个页面】学生网页设计源码
  13. Android流光动画,流光相机app_流光相机官网-多特软件站安卓网
  14. Chrome插件 - FireShot捕捉网页截图(可截取完整页面)
  15. Winrar制作自解压安装程序
  16. 商场三十六计——第22计 “关门捉贼”
  17. linux设备驱动(3)--LED字符驱动
  18. 训练集损失值loss、测试集val_loss、验证集loss相关问题总结
  19. 点,破折号和花花公子表现得很差
  20. 年度发布|华为云2021应用构建技术实践精选集,免费下载

热门文章

  1. 智能车载终端项目方案
  2. 硬件 CPU Intel® Xeon® Processor E5520
  3. 238. 银河英雄传说(并查集,扩展域)
  4. 《学术研究,你的成功之路》阅读笔记
  5. 怎么通过微信开发者工具打开一个项目
  6. Android开发板 MTK 4g/5g 安卓开发板定制
  7. Android开发-创建安卓工程项目+安卓程序的运行
  8. Qt5.5-msvc2013-x64编译的程序在其它机器上无法运行,提示0xc000007b错误
  9. python-比较两个文件交集、并集、差集
  10. ubuntu进入系统黑屏问题