这个问题在论坛中的出现频率很高。在解决这个问题时,首先要明确Windows处理用户输入的方法完全不同于Dos操作系统。当用户按键后,Dos应用向操作系统提出请求,而在Windows中,当用户事件发生时,是由Windows请求调用相应的代码,代码实现自己必须的处理,最后将控制返回到操作系统。
当你从Dos操作系统编程转向Windows的时候,你会很不习惯Windows的面向事件与消息的处理模式,但是面向对象的处理方法在Windows中非常灵活实用。
本文要讨论的问题是如何在应用程序中实现用户事件的轮询。例如,当你的应用程序在忙碌状态时,如何探测用户按键(Escape)来终止正在进行的处理或操作。
当用户按键或移动鼠标导致系统事件发生时,操作系统将这些事件存储在相应的应用程序消息队列中,事件会一直以消息的形式存储在消息队列中直到应用程序完消息并将控制返回到Windows,这时Windows将把消息队列中的下一条消息发送到应用程序。
所以,为了确定是否用户已经按下了某一个按键,应用程序需要确定某一按键的消息当前是否在消息队列中。为此可以调用PeekMessage函数,例如:
MSG msg;
// 检查是否按下 Escape 键
if (::PeekMessage(&msg, m_hWnd, WM_KEYFIRST,
WM_KEYLAST, PM_REMOVE)) {
if (msg.message == WM_KEYDOWN && msg.wParam
== VK_ESCAPE)
// 退出循环或者停止处理;
}
第一个参数MSG结构接收与消息有关的信息。第二个参数是window句柄,如果程序是基于MFC的应用,这个参数传递m_hWnd即可。下两个参数是确定类型的消息,PeekMessage将返回消息队列中落在这两个值之间的第一个消息。因为这里我们感兴趣的是按键,所以就用WM_KEYFIRST 和 WM_KEYLAST作为参数。最后一个参数可以是PM_NOREMOVE 或者 PM_REMOVE,表示消息信息是否应该从消息队列中删除。
如果PeekMessage在请求范围内寻找消息,他返回非零值。这样上面的代码检查是否发现WM_KEYDOWN消息并且wParam等于VK_ESCAPE,如果发现则退出循环并终止代码的处理。