句柄是Windows程序设计的基础,而消息机制又是Windows程序设计的核心。
众所皆知,Windows是一个事件驱动,基于消息的操作系统。Windows强调的是事件触发,并且事件之间是无序的。在DOS下,代码的执行都是井然有序的,也就是说你在执行这个程序的时候不能执行另一个程序,单任务。而在强大的Windows下,能同时执行几个或者几十个程序。那么什么是触发事件?我们点一下鼠标、移动一下鼠标、按一下键盘按键,这些都会产生响应的事件,Windows在接收到这些事件之后就会把它们转化成对应的消息,然后放入系统的消息队列里。
Windows为当前执行的每个Windows程序维护一个消息队列。在发生输入事件之后,Windows将事件转换为一个消息并将消息放入程序的消息队列中。程序通过执行一块称之为消息循环的程序代码从消息队列中取出消息,然后根据不同的消息做出相应的响应。
依旧用之前的代码来分析:
在程序中,消息是用MSG消息结构来表示的,我们在程序一开始就定义了一个消息结构。在窗口类的注册、创建、显示、更新时,我们找不到任何有关msg执行的代码,只有在后面GetMessage的时候才能看到。

众所皆知,Windows是一个事件驱动,基于消息的操作系统。Windows强调的是事件触发,并且事件之间是无序的。在DOS下,代码的执行都是井然有序的,也就是说你在执行这个程序的时候不能执行另一个程序,单任务。而在强大的Windows下,能同时执行几个或者几十个程序。那么什么是触发事件?我们点一下鼠标、移动一下鼠标、按一下键盘按键,这些都会产生响应的事件,Windows在接收到这些事件之后就会把它们转化成对应的消息,然后放入系统的消息队列里。
Windows为当前执行的每个Windows程序维护一个消息队列。在发生输入事件之后,Windows将事件转换为一个消息并将消息放入程序的消息队列中。程序通过执行一块称之为消息循环的程序代码从消息队列中取出消息,然后根据不同的消息做出相应的响应。
依旧用之前的代码来分析:
/* -------------------------------------------------------------------
MyWindows.c -- 基本窗口模型
《Windows 程序设计(SDK)》视频教程
--------------------------------------------------------------------*/
#include
<windows.h>
LRESULT
CALLBACK
WndProc
(
HWND
,
UINT
,
WPARAM
,
LPARAM
)
;
//回调函数
int
WINAPI
WinMain
(
HINSTANCE
hInstance
,
HINSTANCE
hPrevInstance
,
PSTR
szCmdLine
,
int
iCmdShow
)
{
static
TCHAR
szAppName
[
]
=
TEXT
(
"MyWindows"
)
;
//定义一个静态的扩展字符名
HWND
hwnd
;
//定义一个句柄
MSG
msg
;
//定义一个消息结构
WNDCLASS
wndclass
;
//定义一个窗口类结构
wndclass
.
style
=
CS_HREDRAW |
CS_VREDRAW
;
//窗口风格
wndclass
.
lpfnWndProc
=
WndProc
;
//回调函数
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
;
//指定窗口类名,面向程序员
if
(
!
RegisterClass
(
&
wndclass
))
//注册且测试该窗口类
{
MessageBox
(
NULL
,
TEXT
(
"这个程序需要在 Windows NT 才能执行!"
)
,
szAppName
,
MB_ICONERROR
)
;
return
0
;
}
hwnd
=
CreateWindow
(
//创建窗口
szAppName
,
//窗口类名,面向程序员
TEXT
(
"与窗口的第一次"
)
,
//窗口名,面向用户
WS_OVERLAPPEDWINDOW
,
//窗口风格
CW_USEDEFAULT
,
//指定窗口的初始水平位置
CW_USEDEFAULT
,
//指定窗口的初始垂直位置
CW_USEDEFAULT
,
//指明窗口的宽度
CW_USEDEFAULT
,
//指明窗口的高度
NULL
,
//指向被创建窗口的父窗口或所有者窗口的句柄
NULL
,
//菜单句柄
hInstance
,
//与窗口相关联的模块事例的句柄
NULL
)
;
//暂忽略
ShowWindow
(
hwnd
,
iCmdShow
)
;
//设置指定窗口的显示状态
UpdateWindow
(
hwnd
)
;
//暂忽略
while
(
GetMessage
(
&
msg
,
NULL
,
0
,
0
))
//从消息队列中获取消息并放在msg结构中
{
TranslateMessage
(
&
msg
)
;
//将虚拟键消息转换为字符消息
DispatchMessage
(
&
msg
)
;
//调度一个消息给窗口程序
}
return
msg
.
wParam
;
}
LRESULT
CALLBACK
WndProc
(
HWND
hwnd
,
UINT
message
,
WPARAM
wParam
,
LPARAM
lParam
)
{
HDC
hdc
;
PAINTSTRUCT
ps
;
RECT
rect
;
switch
(
message
)
{
case
WM_PAINT
:
hdc
=
BeginPaint
(
hwnd
,
&
ps
)
;
GetClientRect
(
hwnd
,
&
rect
)
;
DrawText
(
hdc
,
TEXT
(
"这是我的第一个窗口程序!"
)
,
-1
,
&
rect
,
DT_SINGLELINE |
DT_CENTER |
DT_VCENTER
)
;
EndPaint
(
hwnd
,
&
ps
)
;
return
0
;
case
WM_DESTROY
:
PostQuitMessage
(
0
)
;
return
0
;
}
return
DefWindowProc
(
hwnd
,
message
,
wParam
,
lParam
)
;
}
这个循环就是处理消息的,GetMessage函数负责从消息队列中获取一个消息传给指定的结构(msg)。如果函数取得WM_QUIT之外的其他消息,返回非零值。如果函数取得WM_QUIT消息,返回值是零。如果返回非零值,就执行循环体,循环体是两个函数,一个是TranslateMessage,用于将虚拟键消息转换成字符消息,字符消息被寄送到调用线程的消息队列里。DispatchMessage则是将一个消息给窗口程序。就是在执行这个函数的时候,系统调用了回调函数。函数中利用switch-case的方式,判断是不是我们“感兴趣”的消息,如果是该怎么响应,若遇到有些我们“不感兴趣”的消息,我们就直接return DefWindowsProc···交给这个函数进行默认的处理。
关于消息机制的补充:
--消息队列是FIFO的形式,也就是先进先出。先进入的队列会最先出,跟栈的形式刚好相反吧。
--WM_PAINT,WM_TIMER和WM_QUIT这三个队列属于特例,操作系统会把它们时刻放在消息队列的最后。只有当消息队列里面其他东西都执行完了,才去执行这三个。
--消息分为非队列消息和队列消息,非队列消息能开G,能插队,是Windows直接放给窗口过程的。
若想了解一下MSG结构或者其他的一些结构、API,可以鱼C一下,点我~