经历之前几个步骤,视窗就会出现在显示器上。接下来视窗就需要对我们得一些操作来进行相应的响应,通过以下代码来实现相关的功能:
//从操作系统的消息队列中不断的捡取消息(讯息回圈)
while (::GetMessage(&msg, NULL, 0, 0)) {
::TranslateMessage(&msg); //软键盘转化消息
::DispatchMessage(&msg);//将消息发送到窗体中
}
GetMessage函数,其作用是从调用线程的消息队列中检索消息。该函数分派传入的已发送消息,直到已发布的消息可供检索为止。
BOOL GetMessage(
LPMSG lpMsg,//MSG指针
HWND hWnd,//视窗句柄
UINT wMsgFilterMin,
UINT wMsgFilterMax
);
第一个参数lpMsg为一个指向MSG结构的指针,该结构从线程的消息队列接收消息信息。
第二个参数hWnd为所要获取消息的视窗句柄。该窗口必须属于当前线程。
第三个wMsgFilterMin和第四个参数wMsgFilterMax分别为检索最低消息的整数值与最高消息的整数值。
在例程中,第二、第三 和第四个参数设定为 NULL 或者 0,表示程式接收它自己建立的所有视窗的所有讯息。
它的返回值为BOOL类型如果函数检索到WM_QUIT以外的消息,则返回值为非零。若检索WM_QUIT消息,则返回值为零。
接下来对于MSG结构进行一个说明,MSG的结构如下所示
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
DWORD lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;
其中POINT又是一个新的结构体
typedef struct tagPOINT {
LONG x ;
LONG y ;
} POINT, * PPOINT;
第一个变量hwnd为窗口过程接收消息的窗口句柄。与CreateWindow创建的句柄相同。
第二个变量message为消息标识符,它实际上是一个无符号整型的数,用来标识每一个不同的信息,这些信息都是以WM开头的,如WM_PAINT,为画图信息。
第三个wParam与第四个lParam为两个32位的消息,于其传入的信息有关。
第五个参数time为消息放入讯息队列的时间。
第六个参数pt为消息放入信息队列时的光标位置,以屏幕你坐标轴来表示。
TranslateMessage函数将msg传入Windows中,并进行一系列的键盘转换。
DispatchMessage函数将又将 msg 结构回传给 Windows。然後,Windows 将该消息发送给视窗讯息处理函数WndProc,让它进行处理。当WndProc处理完毕后,DispatchMessage结束呼叫,继续对while循环进行判断,继续运行GetMessage函数,直至窗口返回WM_QUIT消息跳出循环。
视窗信息处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message) {
case WM_CREATE:
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, TEXT("Hello windows32SDK"), -1, &rect,
DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY: //正在销毁窗口
//向消息序列投递一个WM_QUIT消息,促使GetMessage函数返回0,结束消息循环
::PostQuitMessage(0);
return 0;
}
return ::DefWindowProc(hwnd, message, wparam, lparam);
}
上面的代码为例程中的视窗信息处理函数,函数的名字可以任意,但是其格式一般为:
LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wparam, LPARAM lparam)
//可以看出,这四个参数与MSG结构的前一个完全一致
一个 Windows 程式可以有多个视窗信息处理函数。一个视窗信息处理函数总是与呼叫 RegisterClass 注册的特定视窗类别相关联。CreateWindow 函式根据特定视窗类别建立一个视窗。但依据一个视窗类别,可以建立多个视窗。
程序通常都不直接调用 WndProc函数,而是根据GetMessage函数从消息队列中得到的信息,Windows直接根据消息来调用WndProc函数。
视窗对于信息的处理,都是通过传入message来实现的,一般采取switch-case结构来对消息进行接受并且处理,如下所示:
switch (iMsg)
{
case WM_CREATE :
处理WM_CREATE讯息
return 0 ;
case WM_PAINT :
处理WM_PAINT讯息
return 0 ;
case WM_DESTROY :
处理WM_DESTROY讯息
return 0 ;
} return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
在WndProc函数进行每一条消息的处理后,必须传回 0。WndProc函数不予处理的所有讯息应该被传给名为 DefWindowProc 的 函数。从 DefWindowProc 传回的值必须由WndProc函数传回。
处理WM_PAINT信息
当视窗中的内容变为无效或者必须更新画面时,将会由WM_PAINT信息通知视窗信息处理函数。
当视窗最开始建立的时候,其这个显示的区域都是无效化的,这个时候就需要WM_PAINT消息进行绘制。当我们拖动视窗的大小调节按钮时,也需要该消息重新画出调整后的视窗大小。当一个视窗遮盖住另一个视窗时,被遮盖的那一部分即成为无效区域,若我们将上面的视窗拖动,这是又将使用WM_PAINT消息回复下面视窗的内容。
对于WM_PAINT消息的处理,首先是调用BeginPaint 函数,最后以EndPaint 函数进行结束。
BeginPaint函数的作为是准备指定的窗口绘画和填充一个PAINTSTRUCT结构有关绘画的信息。
HDC BeginPaint(
HWND hWnd,
LPPAINTSTRUCT lpPaint
);
第一个参数hWnd为所要进行回值的窗口句柄。
第二个参数lpPaint为指向将接收绘画信息的PAINTSTRUCT结构的指针,其结构如下所示:
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;
PAINTSTRUCT结构中包含一些视窗处理信息, 可以用来更新显示区域的内容。
BeginPaint函数来使视窗中的区域被选中,也就是说在经过BeginPaint函数的调用后视窗中的区域就可以进行绘制命令,而EndPaint 恰恰相反,它会使视窗中的区域变得不能进行绘制。
当执行完BeginPaint之后,继续执行GetClientRect函数,其用法如下所示:
BOOL GetClientRect(
HWND hWnd,
LPRECT lpRect
);
其作用是检索客户区,它用于取得指定窗口的客户区域大小。对于指定窗口分为客户区和非客户区,客户区就是用来显示内容的区域,非客户区就是显示命令按钮或者窗口标题的。
第一个参数hWnd为要显示的窗口句柄。
第二个参数lpRect为指向接收客户坐标的RECT结构的指针,RECT结构通过指定左上角的坐标和左下角的坐标指定一个矩形。left 和 top 栏位通常设定为 0,right 和 bottom 栏位设定为显示区域的宽度和高度。
RECT结构如下所示:
typedef struct tagRECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT, *NPRECT, *LPRECT;
若函数获取区域成功则返回非零,否则返回零。
在获取相应的矩形区域之后,调用DrawText函数将所要显示的文字画在对话框的内部。
处理WM_DESTROY信息
当单机视窗的关闭按钮时,视窗信息处理函数就会调用WM_DESTROY消息,在此我们调用PostQuitMessage函数,其作用为向系统指示线程已请求终止(退出)。
void PostQuitMessage(
int nExitCode
);
当传入0值时,该函数会将WM_QUIT消息传入wParam参数,故也可返回msg.wParam。
一些小小的总结
我们要创建一个视窗,首先就是要注册一个视窗类型,我们需要使用RegisterClass函数来进行视窗类型的注册。依据该类别建立的视窗使用这个视窗信息处理函数来处理视窗的所有消息。 Windows 通过呼叫视窗信息处理函数对视窗发送讯息。
无论是打开视窗,改变视窗中的相关内容,还是关闭视窗,都要向视窗信息处理函数发送信息来进行改变,通常这些信息都是WM开头的。
视窗信息处理函数的 wParam 和 lParam 参数除了作为传递 给 DefWindowProc 的参数外,不再有其他用处。
参考资料:
[1]《Windows程序设计(第五版)》
[2]https://docs.microsoft.com/zh-cn/welcome-to-docs
[3]https://blog.csdn.net/eryueniao1989/article/details/77992049