Win32编程之消息机制(二)

一、Windows的消息概念

1.消息组成:

  • 窗口句柄
  • 消息ID
  • 消息的两个参数(两个附带信息)
  • 消息产生的时间
  • 消息产生时鼠标位置
1
2
3
4
5
6
7
8
typedef struct tagMSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
} MSG  

2.消息的作用

当系统通知窗口工作时,就采用消息的方式派发给窗口

二、消息循环

1
2
3
4
5
6
//消息循环
MSG nMsg = { 0 };
while (GetMessage(&nMsg, NULL, 0, 0)) {
    TranslateMessage(&nMsg);//将虚拟消息转换为字符消息
    DispatchMessage(&nMsg);//将消息分发给窗口处理函数
}

1.GetMessage():获取本进程的消息

1
2
3
4
5
6
WINUSERAPI BOOL WINAPI
GetMessageA(
    _Out_ LPMSG lpMsg,//存放获取到的消息BUFF
    _In_opt_ HWND hWnd,//窗口句柄
    _In_ UINT wMsgFilterMin,//获取消息的最小ID
    _In_ UINT wMsgFilterMax//获取消息的最大ID<br>);  

lpMsg:当获取到消息后,将消息的参数存放到MSG结构中

hWnd:获取到hWnd所指定窗口的消息

wMsgFilterMin和wMsgFilterMax:只能获取到由它们指定的消息范围内的消息,如果都为0,表示没有范围

2.TranslateMessage():翻译消息,将按键消息翻译成字符消息

1
2
3
4
BOOL WINAPI
TranslateMessage(
    _In_ CONST MSG *lpMsg //要翻译的消息地址
);

检查消息是否有按键消息,如果不是按键消息,不做任何处理,继续执行

3.DispatchMessage()函数执行流程:

1
2
3
4
5
6
DispatchMessage(&nMsg) {
    nMsg.hwnd --> 通过窗口句柄找到保存窗口数据的内存 -->然后找到窗口消息处理函数
    WndProc(nMsg.hWnd, nMsg.msgID, nMsg.wParam, nMsg.lParam) {
       //执行消息处理函数 
    }   
}

 每个窗口都必须具有窗口处理函数:

1
2
3
4
5
6
LRESULT CALLBACK  WindProc(
  HWND hWnd, //窗口句柄
  UINT msgID, //消息ID
  WPARAM wParam, //消息参数
  LPARAM lParam //消息参数
); 

当系统通知窗口时,会调用窗口处理函数,同时将消息ID和消息参数传递给窗口处理函数,在窗口处理函数中,不处理的消息,使用缺省窗口处理函数,例如:DefWindowProc

 三、创建消息

1.WM_DESTROY

  • 产生时间:窗口被销毁时消息
  • 附带消息:wParam:为0,IParam:为0
  • 一般用法:常用于在窗口被销毁之前,做相应的善后处理,例如资源,内存等

2.WM_SYSCOMMAND消息

  • 产生时间:当点击窗口的最大化,最小化,关闭等
  • 附带消息:wParam:具体点击的位置,例如关闭SC_CLOSE等,lParam:鼠标光标的位置,LOWORD(lParam)为水平位置,HIWORD(lParam)为垂直位置,lParam低两字节是水平坐标,高两字节是垂直坐标
  • 一般用法:常用在窗口关闭时,提示用户处理

3.WM_CREATE消息

  • 产生时间:在窗口创建成功但还未显示时
  • 附带信息:wParam:为0,lParam:为CREATESTRUCT类型的指针,通过这个指针可以获取CreateWindowEx中的全部12个参数
  • 一般用法:常用于初始化窗口的参数,资源等等,包括创建子窗口等

4.WM_SIZE消息

  • 产生时间:在窗口的大小发生变化后
  • 附带消息:wParam:窗口大小变化的原因,lParam:窗口变化后的大小,LOWORD(lParam)为变化后的宽度,HIWORD(lParam)为变化后的高度
  • 一般用法:常用于窗口大小变化后,调整窗口内各个部分的布局

 5.WM_QUIT消息

  • 产生时间:程序员发送
  • 附带消息:wParam:PostQuitMessage函数传递的参数,lParam:0
  • 一般用法:用于结束消息循环,当GetMessage收到这个消息后,会返回FALSE,结束while处理,退出消息循环

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <Windows.h>
#include <stdio.h>
 
HANDLE g_hOutout = 0;//接收标准输出句柄
 
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    switch (msgID) {
    case WM_SIZE: {
        short nWidth = LOWORD(lParam);
        short nHight = HIWORD(lParam);
 
        char szText[256] = { 0 };
        sprintf(szText, "WN_SZIE:宽度:%d,高度:%d\n", nWidth, nHight);
        WriteConsole(g_hOutout, szText, strlen(szText), NULL, NULL);
 
        break;
    }  
    case WM_CREATE: {
        CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
        char* pszText = (char*)pcs->lpCreateParams;     
        MessageBox(NULL, pszText, TEXT("提示"), MB_OK);
 
        CreateWindowEx(0, "EDIT", "Hello", WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 0, 200, 200, hWnd, NULL, 0, NULL);
 
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_SYSCOMMAND: {
        if (wParam == SC_CLOSE) {
            int nRet = MessageBox(hWnd, TEXT("是否退出"), TEXT("提示"), MB_YESNO);
            if (nRet == IDYES) {
                //什么都不写
            }
            else {
                return 0;
            }
        }
        break;
    }      
    default:
        break;
    }  
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}
 
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    AllocConsole();//增加DOS窗口
    g_hOutout = GetStdHandle(STD_OUTPUT_HANDLE);
     
    //设计窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = WindProc;
    wc.lpszClassName = TEXT("Main");
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;
 
    //注册窗口类
    RegisterClass(&wc);//将以上所有赋值全部写入操作系统
 
    const char* pszText = "hello data";
    //在内存中创建窗口
    HWND hWnd = CreateWindowEx(0, wc.lpszClassName, TEXT("Window"), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, (LPVOID)pszText);
    <br>   //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
 
    //消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0)) {
        TranslateMessage(&nMsg);//将虚拟消息转换为字符消息
        DispatchMessage(&nMsg);//将消息分发给窗口处理函数
    }
 
    return 0;
}

6.WM_PAINT消息

  • 产生时间:当窗口需要绘制的时候
  • 附带消息:wParam:0,lParam:0
  • 专职用法:用于绘图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
消息处理步骤
1.开始绘图
HDC BeginPaint(
    HWND hWnd; //绘制窗口
    LPPAINTSTRUCT lpPaint //绘制参数的BUFF
);//返回绘图设备句柄HDC
2.正式绘图
3.结束绘图
BOOL EndPaint(
     HWND hWnd, //绘制窗口
     CONST PAINTSTRUCT *lpPaint //绘图参数的指针BeginPaint返回
);
*/
 
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    switch (msgID) {
    case WM_PAINT: {
        const char* pszText = "WM_PAINT\n";
        WriteConsole(g_hOutput, pszText, strlen(pszText), NULL, NULL);
 
        //下述绘图代码,必须放在处理WM_PAINT消息时调用
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        TextOut(hdc, 100, 100, "hello", 5);
        EndPaint(hWnd, &ps);
 
        break;
    }
    case WM_LBUTTONDOWN: {
        /*
        窗口无效区域:需要重新绘制的区域
        BOOL InvalidateRect(
             HHWND hWnd, //窗口句柄
             CONST RECT* lpRect, //区域的矩形坐标
             BOOL bErase//重绘前是否先擦除
        );
        */
        InvalidateRect(hWnd, NULL, TRUE);
        break;
    }
    default:
        break;
    }
         
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}  

 四、消息循环原理

GetMessage:从系统获取消息,将消息从系统中移除,阻塞函数,当系统无消息时,会等候下一条消息

PeekMessage:以查看的方式从系统获取消息,可以不将消息从系统移除,非阻塞函数,当系统无消息时,返回FALSE,继续执行后续代码

WINUSERAPI BOOL WINAPI
PeekMessageA(
    _Out_ LPMSG lpMsg,//message information
    _In_opt_ HWND hWnd,//handle to window
    _In_ UINT wMsgFilterMin,//first message
    _In_ UINT wMsgFilterMax,//last message
    _In_ UINT wRemoveMsg//不移除标识:PM_NOREMOVE
);

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//消息循环
    MSG nMsg = { 0 };
    while (true) {
        if ((PeekMessage(&nMsg, NULL, 0, 0, PM_NOREMOVE))) {
            //有消息
            if (GetMessage(&nMsg, NULL, 0, 0)) {
                TranslateMessage(&nMsg);
                DispatchMessage(&nMsg);
            } else {
                break;
            }
        } else {
            //空闲处理
            WriteConsole(g_hOutout, "onIdle", strlen("onIdle"), NULL, NULL);
        }
    }

SendMessage:发送消息,会等候消息处理的结果

PostMessage-投递消息,消息发出后立刻返回,不等厚消息执行结果

1
2
3
4
5
6
BOOL SendMessage/PostMessage(
        HWND hWnd,//消息发送的目的窗口
        UNIT Msg,//消息ID
        WPARAM wParam,//消息参数
        LPARAM lParam,//消息参数
);

五、消息的分类

  • 系统消息:ID范围(0-0x03FF),由系统定义好的消息,可以在程序中直接使用
  • 用户自定义消息:ID范围(0x0400-0x7FFF),由用户自己定义,满足用户自己的需求,由用户自己发出消息。并处理,自定义消息宏:WM_USER

1.用户自定义消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define WM_MYMESSAGE WM_USER + 1001
 
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    switch (msgID) {
    case WM_CREATE: {      
        PostMessage(hWnd, WM_MYMESSAGE, 1, 2);     
        break;
    }
    case WM_MYMESSAGE: {
        char szText[256] = { 0 };
        sprintf(szText, "自定义消息消息被处理:wParam=%d, lParam=%d\n", wParam, lParam);
        MessageBox(hWnd, szText, "提示", MB_OK);
        break;
    }
    default:
        break;
    }  
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

 六、消息队列

1.消息队列概述

  • 消息队列是用来存放消息的队列
  • 消息在队列中先入先出
  • 所有窗口程序都具有消息队列
  • 程序可以从队列中获取信息
  • 系统消息队列:由系统维护的消息队列,存放系统产生的消息,例如鼠标,键盘等
  • 程序消息队列:属于每一个应用程序(线程)的消息队列,由应用程序(线程)维护

  进程产生的消息会先发送到系统的消息队列中,然后系统消息队列根据消息中的窗口句柄,将消息下发到指定窗口的程序消息队列中,然后应用程序通过GeMessage(...)从程序消息队列中获取消息,再通过TranslateMessage(...)翻译,DispatchMessage(...)转发消息,最后执行消息函数WinProc(...);所以,所有的进程消息只要能进入消息队列中,那么一定会先进入系统消息队列中,需要注意的是,PostMessage(...)发送的消息是进入系统消息队列中的。

2.消息和消息队列的关系

  当鼠标,键盘产生消息时,会将消息存放到系统消息队列,系统会根据存放的消息,找到对应程序的消息队列,将消息投递到程序的消息队列中。根据消息和消息队列之间使用关系,将消息分成两类:

(1).队列消息:消息的发送和获取,都是通过消息队列完成的,消息发送后,首先放入队列中,然后通过消息循环,从队列当中获取。     

1
2
3
GetMessage:从消息队列中获取消息
PostMessage:将消息投递到消息队列中
常见队列消息:WM_PAINT、键盘、鼠标、定时器等  

(2).非队列消息:消息的发送和获取,是直接调用消息的窗口处理完成的,消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用处理函数,完成消息。

1
2
SendMessage:直接将消息发送给窗口的处理函数,并等候处理结果
常见消息:WM_CREATE、WM_SIZE等

七、GetMessage工作原理

  • 在程序(线程)消息队列查找消息,如果队列有消息,检测消息是否满足指定条件(HWND,ID范围),不满足条件就不会取出消息,否则从队列中取出消息并返回
  • 如果程序(线程)消息队列中没有消息,向系统消息队列获取属于本程序的消息,如果系统队列的当前消息属于本程序,系统会将消息转发到程序消息队列中
  • 如果系统消息队列中也没有消息,检测当前进程的所有窗口的需要重新绘制的区域,如果发现有需要重新绘制的区域,产生WM_PAINT消息,取得消息返回处理
  • 如果没有重新绘制区域,检查定时器,如果有到时的定时器,产生WM_TIMER,返回处理执行
  • 如果没有到时的定时器,整理程序的资源、内存等
  • GetMessage会继续等候下一条消息,PeekMessage会返回FALSE,交出程序的控制权
  • 注意:GetMessage如果获取到的是WM_QUIT,函数会返回FALSE

八、键盘消息

1.键盘消息分类

  • WM_KEYDOWN:按键被按下时产生
  • WM_KEYUP:按键被放开时产生
  • WM_SYSKEYDOWN:系统键按下时产生,比如ALT、F10
  • WM_SYSKEYUP:系统键放开时产生

附带信息:WPARAM:按键的Virtual Key,LPARAM:按键的参数,例如按下次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    switch (msgID) {
    case WM_KEYDOWN: {
        char szText[256] = { 0 };
        sprintf(szText, "WM_KEYDOWN:键码值=%d\n", wParam);
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
        break;
    }
    case WM_KEYUP: {
        char szText[256] = { 0 };
        sprintf(szText, "WM_KEYUP:键码值=%d\n", wParam);
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
        break;
    }
    default:
        break;
    }
         
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

2.字符消息(WM_CHAR)

  • TranslateMessage在转换WM_KEYDOWN消息时,对于可见字符可以产生WM_CHAR,不可见字符无此消息
  • 附带信息:WPARAM:输入的字符的ASCII字符编码值,LPARAM:按键的相关参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    switch (msgID) {
    case WM_CHAR: {
        char szText[256] = { 0 };
        sprintf(szText, "WM_CHAR:键码值=%d\n", wParam);
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
        break;
    }
    default:
        break;
    }
         
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

字符消息工作原理:

1
2
3
4
5
6
7
8
9
10
11
12
TranslateMessage(&nMsg) {
    if (nMsg.message != WM_KEYDOWN)
        return ..
    根据nMsg.wParam(键码值)可以获知哪个按键被按下
    if (不可见字符的按键)
        return ...
    根据CapsLock(大写锁定键)是否处于打开状态
    if (CapsLock打开)
        PostMessage(nMsg.hwnd, WM_CHAR, 65, ...)//大写字符的ASCII码
    else
        PostMessage(nMsg.hwnd, WM_CHAR, 97, ...)//小写字符的ASCII码
}  

九、鼠标消息 

1.鼠标基本消息

1
2
3
4
5
WM_LBUTTONDOWN:鼠标左键按下
WM_LBUTTONUP:鼠标左键抬起
WM_RBUTTONDOWN:鼠标右键按下
WM_RBUTTONUP:鼠标右键抬起
WM_MOUSEMOVE:鼠标移动消息  

附带信息 :

1
2
3
wPARAM:其他按键的状态,例如Ctrl/Shift等
IPARAM:鼠标的位置,窗口客户区坐标系,LOWORD:X坐标位置,HIWORD:Y坐标位置 
一般情况下,鼠标的按下/抬起成对出现,在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE消息 

 示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    switch (msgID) {
    case WM_MOUSEMOVE: {
        char szText[256] = { 0 };
        sprintf(szText, "WM_MOUSEMOVE:其他按键状态:%d, X=%d, Y=%d\n",
            wParam, LOWORD(lParam), HIWORD(lParam));
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
        break;
    }
    case WM_LBUTTONDOWN: {
        char szText[256] = { 0 };
        sprintf(szText, "WM_LBUTTONDOWN:其他按键状态:%d, X=%d, Y=%d\n",
            wParam, LOWORD(lParam), HIWORD(lParam));
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
        break;
    }
    case WM_LBUTTONUP: {
        char szText[256] = { 0 };
        sprintf(szText, "WM_LBUTTONUP:其他按键状态:%d, X=%d, Y=%d\n",
            wParam, LOWORD(lParam), HIWORD(lParam));
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
        break;
    }
    default:
        break;
    }
         
    return DefWindowProc(hWnd, msgID, wParam, lParam);
} 

 2.鼠标双击消息:

1
2
WM_LBUTTONDBLCLK:鼠标左键双击
WM_RBUTTONDBLCLK:鼠标右键双击

附带信息:

1
2
wPARAM:其他按键的状态,例如Ctrl/Shift等
IPARAM:鼠标的位置,窗口客户区坐标系,LOWORD:X坐标位置,HIWORD:Y坐标位置 

消息产生顺序:  

1
2
3
4
5
6
以左键双击为例:
  WM_LBUTTONDOWN
  WM_LBUTTONUP
  WM_LBUTTONDBLCLK
  WM_LBUTTONUP
使用时需要在注册窗口类的时候添加CS_DBLCLKS风格

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <Windows.h>
#include <stdio.h>
 
HANDLE g_hOutput = 0;
 
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    switch (msgID) {
    case WM_LBUTTONDBLCLK: {
        char szText[256] = { 0 };
        sprintf(szText, " WM_LBUTTONDBLCLK:\n");
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
        break;
    }
    case WM_LBUTTONDOWN: {
        char szText[256] = { 0 };
        sprintf(szText, "WM_LBUTTONDOWN:其他按键状态:%d, X=%d, Y=%d\n",
            wParam, LOWORD(lParam), HIWORD(lParam));
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
        break;
    }
    case WM_LBUTTONUP: {
        char szText[256] = { 0 };
        sprintf(szText, "WM_LBUTTONUP:其他按键状态:%d, X=%d, Y=%d\n",
            wParam, LOWORD(lParam), HIWORD(lParam));
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
        break;
    }
    default:
        break;
    }
         
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}
 
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow) {
    AllocConsole();
    g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
     
    //设计窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = WindProc;
    wc.lpszClassName = TEXT("Main");
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
 
    //注册窗口类
    RegisterClass(&wc);//将以上所有赋值全部写入操作系统
 
    //在内存中创建窗口
    HWND hWnd = CreateWindowEx(0, wc.lpszClassName, TEXT("Window"), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
 
    //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
 
    //消息循环
    MSG nMsg = { 0 };
    while (true) {
        if ((PeekMessage(&nMsg, NULL, 0, 0, PM_NOREMOVE))) {
            //有消息
            if (GetMessage(&nMsg, NULL, 0, 0)) {
                TranslateMessage(&nMsg);
                DispatchMessage(&nMsg);
            } else {
                break;
            }
        }
    }
 
    return 0;
}

3.鼠标滚轮消息

1
WM_MOUSEWHEEL:鼠标滚轮消息  

附带信息: 

1
2
3
wPARAM:LOWORD:其他按键的状态,HIWORD:滚轮的偏移量,通过正负值表示滚动方向,正:向前滚动,负:向后滚动
IPARAM:鼠标当前的位置,屏幕坐标系,LOWORD:X坐标,HIWORD:Y坐标
使用:通过偏移量,获取滚动的方向和距离

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    switch (msgID) {
    case WM_MOUSEWHEEL: {
        short nDelta = HIWORD(wParam);//偏移量
        char szText[256] = { 0 };
        sprintf(szText, "WM_MOUSEWHEEL:nDetal=%d\n", nDelta);
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
        break;
    }
    default:
        break;
    }
         
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

十、定时器消息 

  在程序中创建定时器,当到底时间间隔时,定时器会向程序发送一个WM_TIMER消息。时器的精度是毫秒,但是准确度很低,例如设置时间间隔为1000ms,但是会在非1000毫秒到底消息。附带消息:wPARAM:定时器ID,IPARAM:定时器处理函数的指针

1.创建定时器

1
2
3
4
5
6
7
UINT_PTR SetTimer(
    HWND hWnd,//定时器窗口句柄
    UINT_PTR nIDEvent,//定时器ID
    UINT uElapse,//时间间隔
    TIMERPPOC lpTimerFunc//定时器处理函数指针(一般不使用,为NULL)
);
定时器创建成功后,返回非0

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WindProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {
    switch (msgID) {
    case WM_CREATE: {
        SetTimer(hWnd, 1, 1000, NULL);
        SetTimer(hWnd, 2, 2000, NULL);
        break;
    }
    case WM_TIMER: {
        char szText[256] = { 0 };
        sprintf(szText, "WM_TIMER:定时器ID=%d\n", wParam);
        WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);       
         
        KillTimer(hWnd, wParam);//销毁定时器
        break;
    }
    default:
        break;
    }
         
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}  

       

posted @   TechNomad  阅读(276)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示