windows是一个消息驱动的错做系统。一个消息由一个消息名称(UINT类型)和两个参数(WPARAM,LPARAM)构成。当用户进行了输入或是窗口的状态发生改变时,系统会发送消息到某个窗口。例如当菜单选中之后会有WM_COMMAND消息发送,WPARAM的高字中(HIWORD(wParam))是命令的ID号,对菜单来说就是菜单ID。当然用户也可以定义自己的消息名称,也可以利用自定义消息来发送通知和传送数据。
一个消息必须由一个窗口接收,在窗口的消息处理函数中可以对消息进行分析,对自己感兴趣的消息进行处理。例如希望对菜单选择进行处理,那么可以定义对WM_COMMAND进行处理的代码,如果希望在窗口中进行图形输出就对WM_PAINT进行处理。
事实上为了应付那些没有被相应的消息,windows为窗口编写了默认的窗口消息处理函数,这个窗口过程将负责处理那些程序中没有处理的消息。正因为有了这个默认窗口过程,程序员才可以利用windows的窗口进行开发而不必过多关注窗口各种消息的处理。例如窗口在被拖动时会有很多消息发送,而程序员都可以不予理睬让系统自己处理。
说到消息就不能不说窗口句柄,系统通过窗口句柄在整个操作系统中唯一标识一个窗口。发送消息时必须制定一个窗口句柄表明该消息由哪个窗口接收。而每个窗口都会有自己的窗口消息处理函数,所以用户的输入就会被正确的处理。
下面这段微带掩饰如何在窗口过程中处理消息:
LONG windowProc(HWND hWnd,UINT uMessageType,WPARAM w,LPARAM l)
{
switch(uMessageType)
{
//使用switch语句将各种消息分开
case WM_PAINT: //处理绘制消息
Redraw();
break;
case WM_TIMER: //处理定时器消息
OnTimer();
break;
case WM_LBUTTONDOWN: //处理鼠标左键按下的消息
OnLButtonDown();
break;
default:
defualtWndProc(); //缺省的其他消息处理函数
break;
}
}
在windows操作系统中维护着一个活多个消息队列,所有产生的消息都被放入队列中。系统在队列中每次取出一条,根据消息的接收句柄而将该消息发送给拥有该窗口的消息循环。每一个运行的程序都有自己的消息循环,在循环中得到属于自己的消息并根据接收窗口的句柄调用相应的窗口过程。而在没有消息时消息循环就将控制权交给系统,从而使windows可以同时进行多个任务。下面的伪代码演示了消息循环的用法:
while(1)
{
id=GetMessage();
if(id==WM_QUIT)
break;
DispatchMessage();
}
初看这段代码容易给人一种错觉,这是一个忙等待(busy waiting)的消息循环,因为采用了while(1)的循环方式,而忙等待是个非常糟糕的东西。
而实际上绝大部分时间里这个程序是在阻塞状态,因为程序没有收到消息通知时GetMessage就不会反悔,所以也就不会占用系统的CPU时间。GetMessage函数的阻塞调用时这段代码的关键部分。
上面这段代码的意思是调用GetMessage函数从消息队列中取出消息,然后调用DispatchMessage将消息发送给窗口消息处理函数。