Win32消息机制和用户自定义消息类型

⌚Time: 2023-01-12 17:45:39

👨‍💻Author: Jack Ge

消息机制

过程驱动:程序是按照我们预先定义好的顺序执行,每执行一步,下一步都已经按照预定的顺序继续执行,直到程序结束。

DOS程序是过程驱动程序,程序按照实现编写好的指令一步一步顺序执行

事件驱动:程序的执行顺序是无序的。某个时间点所执行的代码,是由外界通知。由于我们无法决定用户执行顺序,所以代码的执行也是无序。如点击一个按钮,会发送一个按钮点击时间的消息,程序就会对此消息进行处理

Win32的消息机制:事件驱动。

消息类型

MSG结构体

windows程序中,消息由MSG结构体表示。定义如下


typedef struct tagMSG {

  HWND   hwnd;

  UINT   message;

  WPARAM wParam;

  LPARAM lParam;

  DWORD  time;

  POINT  pt;

  DWORD  lPrivate;

} MSG, *PMSG, *NPMSG, *LPMSG;

成员

hwnd

类型:HWND

其窗口过程接收消息的窗口的句柄。 当消息是线程消息时,此成员为 NULL 。

message

类型: UINT

消息的标识符。 应用程序只能使用低字;高字由系统保留。

wParam

类型: WPARAM

关于消息的附加信息。 确切含义取决于 消息 成员的值。

lParam

类型: LPARAM

关于消息的附加信息。 确切含义取决于 消息 成员的值。

time

类型:DWORD

消息的发布时间。

pt

类型: POINT

发布消息时的光标位置(以屏幕坐标表示)。

lPrivate

常见的消息类型

WM_TIMER

在计时器过期时发布到安装线程的消息队列。


#define WM_TIMER                        0x0113

参数

wParam [in]

计时器标识符。

lParam [in]

指向安装计时器时传递给 SetTimer 函数的应用程序定义的回调函数的指针。

返回值

类型: LRESULT

如果应用程序处理此消息,则应用程序应返回零。

WM_CREATE

当应用程序请求通过调用 CreateWindowEx 或 CreateWindow 函数创建窗口时发送。 (函数返回之前发送消息。) 新窗口的窗口过程在创建窗口后接收此消息,但在窗口变为可见之前。


#define WM_CREATE                       0x0001

参数

wParam

未使用此参数。

lParam

指向 CREATESTRUCT 结构的指针,其中包含有关正在创建的窗口的信息。

返回值

类型: LRESULT

如果应用程序处理此消息,它应返回零以继续创建窗口。 如果应用程序返回 –1,则窗口将被销毁, CreateWindowEx 或 CreateWindow 函数返回 NULL 句柄。

WM_SIZE

更改窗口大小后发送到窗口。窗口通过其 WindowProc 函数接收此消息。


#define WM_SIZE                         0x0005

参数

wParam

请求的大小调整的类型。 此参数的取值可为下列值之一:

值   含义

SIZE_MAXHIDE

4

当其他一些窗口最大化时,消息将发送到所有弹出窗口。

SIZE_MAXIMIZED

2

窗口已最大化。

SIZE_MAXSHOW

3

当其他一些窗口还原到其以前的大小时,消息将发送到所有弹出窗口。

SIZE_MINIMIZED

1

窗口已最小化。

SIZE_RESTORED

0

窗口已调整大小,但 SIZE_MINIMIZED 和 SIZE_MAXIMIZED 值均未应用。

lParam

lParam 的低序字指定工作区的新宽度。

lParam 的高序字指定工作区的新高度。

返回值

类型: LRESULT

如果应用程序处理此消息,它应返回零。

WM_CLOSE

发送为窗口或应用程序应终止的信号。窗口通过其 WindowProc 函数接收此消息。


#define WM_CLOSE                        0x0010

参数

wParam

未使用此参数。

lParam

未使用此参数。

返回值

类型: LRESULT

如果应用程序处理此消息,它应返回零。

WM_SYSCOMMAND

当用户从 “窗口 ”菜单中选择命令时,窗口会收到此消息, (以前称为系统或控件菜单) ,或者当用户选择最大化按钮、最小化按钮、还原按钮或关闭按钮时。


#define WM_SYSCOMMAND                   0x0112

参数

wParam

请求的系统命令的类型。 此参数的取值可为下列值之一:

值   含义

SC_CLOSE

0xF060

关闭窗口。

SC_CONTEXTHELP

0xF180

使用指针将光标更改为问号。 如果用户随后单击对话框中的控件,该控件将收到 WM_HELP 消息。

SC_DEFAULT

0xF160

选择默认项;用户双击窗口菜单。

SC_HOTKEY

0xF150

激活与应用程序指定的热键关联的窗口。 lParam 参数标识要激活的窗口。

SC_HSCROLL

0xF080

水平滚动。

SCF_ISSECURE

0x00000001

指示屏幕保存程序是否安全。

SC_KEYMENU

0xF100

检索窗口菜单作为击键的结果。 有关详细信息,请参见“备注”部分。

SC_MAXIMIZE

0xF030

最大化窗口。

SC_MINIMIZE

0xF020

最小化窗口。

SC_MONITORPOWER

0xF170

设置显示的状态。 此命令支持具有节能功能的设备,例如电池供电的个人电脑。

lParam 参数可以具有以下值:

-1 (显示器开机)

1 (显示器将低功率)

2 (显示器正在关闭)

SC_MOUSEMENU

0xF090

在单击鼠标后检索窗口菜单。

SC_MOVE

0xF010

移动窗口。

SC_NEXTWINDOW

0xF040

移动到下一个窗口。

SC_PREVWINDOW

0xF050

移动到上一个窗口。

SC_RESTORE

0xF120

将窗口还原到其正常位置和大小。

SC_SCREENSAVE

0xF140

执行在System.ini文件的 [boot] 节中指定的屏幕保存程序应用程序。

SC_SIZE

0xF000

调整窗口的大小。

SC_TASKLIST

0xF130

激活"开始"菜单菜单。

SC_VSCROLL

0xF070

垂直滚动。

lParam

低序单词指定光标的水平位置(以屏幕坐标为单位)(如果使用鼠标选择窗口菜单命令)。 否则,不使用此参数。

高序单词指定光标的垂直位置(以屏幕坐标为单位)(如果使用鼠标选择窗口菜单命令)。 如果使用系统加速器选择命令,则此参数为 1;如果使用助记键,则为 1

返回值

如果应用程序处理此消息,则应用程序应返回零。

发送消息

SendMessage函数调用指定窗口的窗口过程,在窗口过程处理消息之前不会返回


LRESULT SendMessageA(

  [in] HWND   hWnd,

  [in] UINT   Msg,

  [in] WPARAM wParam,

  [in] LPARAM lParam

);

参数

[in] hWnd

类型:HWND

窗口过程将接收消息的窗口句柄。 如果此参数 HWND_BROADCAST ( (HWND) 0xffff) ,则会将消息发送到系统中的所有顶级窗口,包括已禁用或不可见的未所有者窗口、重叠窗口和弹出窗口;但消息不会发送到子窗口。

消息发送受 UIPI 的约束。 进程的线程只能将消息发送到进程较小或等于完整性级别的线程的消息队列。

[in] Msg

类型: UINT

要发送的消息。

有关系统提供的消息的列表,请参阅 系统定义的消息。

[in] wParam

类型: WPARAM

其他的消息特定信息。

[in] lParam

类型: LPARAM

其他的消息特定信息。

返回值

类型: LRESULT

返回值指定消息处理的结果;这取决于发送的消息。

PostMessage将信息放入消息队列中,不等待处理消息的情况下立即返回


BOOL PostMessageA(

  [in, optional] HWND   hWnd,

  [in]           UINT   Msg,

  [in]           WPARAM wParam,

  [in]           LPARAM lParam

);

参数

[in, optional] hWnd

类型:HWND

窗口过程接收消息的窗口的句柄。 以下值具有特殊含义。

Value   含义

HWND_BROADCAST

( (HWND) 0xffff)

消息将发布到系统中的所有顶级窗口,包括已禁用或不可见的未拥有窗口、重叠的窗口和弹出窗口。 消息不会发布到子窗口。

Null

该函数的行为类似于对 PostThreadMessage 的调用, 其中 dwThreadId 参数设置为当前线程的标识符。

从 Windows Vista 开始,消息发布受 UIPI 的约束。 进程的线程只能将消息发布到小于或等于完整性级别的进程中线程的消息队列。

[in] Msg

类型: UINT

要发布的消息。

有关系统提供的消息的列表,请参阅 系统定义的消息。

[in] wParam

类型: WPARAM

其他的消息特定信息。

[in] lParam

类型: LPARAM

其他的消息特定信息。

返回值

类型: BOOL

如果该函数成功,则返回值为非零值。

如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。

接收消息

GetMessage 获取消息,阻塞函数


BOOL GetMessage(

  [out]          LPMSG lpMsg,

  [in, optional] HWND  hWnd,

  [in]           UINT  wMsgFilterMin,

  [in]           UINT  wMsgFilterMax

);

PeekMessage 获取消息,非阻塞函数,如果获取不到消息就直接向下执行,直到结束


BOOL PeekMessageA(

  [out]          LPMSG lpMsg,

  [in, optional] HWND  hWnd,

  [in]           UINT  wMsgFilterMin,

  [in]           UINT  wMsgFilterMax,

  [in]           UINT  wRemoveMsg

);

消息循环

Windows程序开始执行后,Windows为该程序创建一个“消息队列”。这个消息队列用来存放该程序可能创建的各种不同窗口的消息。程序中有一段代码,叫做“消息循环”, 它用来从队列中取出消息,并且将它们发送给相应的窗口过程。在没有消息发生的时候,你的程序实际上就在消息循环中转圈子。

涉及的函数:

TranslateMessage

将virtual-key消息转换为字符消息。 字符消息将发布到调用线程的消息队列,下次线程调用 GetMessage 或 PeekMessage 函数时要读取。


BOOL TranslateMessage(

  [in] const MSG *lpMsg

);

DispatchMessage

将消息调度到窗口过程。 它通常用于调度 GetMessage 函数检索的消息。


LRESULT DispatchMessage(

  [in] const MSG *lpMsg

);

消息循环实现


MSG msg;

BOOL bRet;



while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)

{ 

    if (bRet == -1)

    {

        // handle the error and possibly exit

    }

    else

    {

        TranslateMessage(&msg); 

        DispatchMessage(&msg); 

    }

}

在接收到WM_QUIT消息之前GetMessage返回非0值,在主函数结尾处添加消息循环,程序就会一直循环处理窗体消息

消息处理

对于程序创建的窗口,编写窗体过程函数来处理消息

窗体过程函数形式,使用switch判断接收的消息类型,并且执行对应的处理


LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

    switch (uMsg)

    {

    case WM_CREATE:窗体创建消息

        printf("aa\n");//打印字符"aa"

        return 0;

    case WM_DESTROY://窗体销毁消息

        PostQuitMessage(0);

        return 0;



    case WM_PAINT://窗体重绘消息

        {

            PAINTSTRUCT ps;

            HDC hdc = BeginPaint(hwnd, &ps);



            // All painting occurs here, between BeginPaint and EndPaint.



            FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));



            EndPaint(hwnd, &ps);

        }

        return 0;



    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);

}


对于系统预定义窗体类,如进度条控件,有自己的消息处理函数。无需编写窗体过程函数。如向进度条控件发送消息PBM_SETRANGE,设置进度条范围


SendMessage( g_hProgress1,PBM_SETRANGE,(WPARAM)0, MAKELPARAM(0,100));

进度条g_hProgress1会处理该消息,并且设置成进度范围1-100

用户自定义消息

用户可自定义消息。消息符号本质上是宏定义的整数类型


#define WM_USER                         0x0400

其中0到WM_USER-1是系统保留消息,为了避免与系统消息冲突,应用程序可以定义 (WM_USER到 0x7FFF) 的消息编号

定义消息类型


#define MyMsg0 WM_USER

#define MyMsg1 WM_USER+1

或者利用枚举类型,成员递增1的特性,定义消息


enum CustomWinMessage{

    MyMsg0 = WM_USER,

    MyMsg1,

    MyMsg2};

发送自定义消息、参数,获取返回值


printf("send custom message!\n",i);

int i = SendMessage(hwnd, MyMsg0,1,2);

printf("return value:%d\n",i);

自定义消息处理,接收消息参数、返回数值


LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

    switch (uMsg)

    {

    case MyMsg0://自定义消息类型

        printf("message parameter:%ld %ld\n",wParam,lParam);//打印消息参数

        return 11;//返回值 11

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;



    case WM_PAINT:

        {

            PAINTSTRUCT ps;

            HDC hdc = BeginPaint(hwnd, &ps);



            // All painting occurs here, between BeginPaint and EndPaint.



            FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));



            EndPaint(hwnd, &ps);

        }

        return 0;



    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

运行结果,在控制台出现字符


send custom message!

message received!message parameter:1 2

return value:11