本节书摘来自异步社区出版社《c++多线程编程实战》一书中的第1章,第1.8节,作者: 【黑山共和国】milos ljumovic(米洛斯 留莫维奇),更多章节内容可以访问云栖社区“异步社区”公众号查看。
许多程序都要响应一些事件,例如,当用户按下按键或输入一些文本时。事件处理或程序能响应用户的动作是一种非常重要的机制。如果要在用户按下按键时处理这个事件,就要创建某种监听器,监听按键事件(即,按下的动作)。
事件处理器是操作系统调用的一个函数,每次都发送某种类型的消息。例如,在按下按键时发送“已按下”,在文本输入时发送“接收到一个字符”。
事件处理器非常重要。计时器是经过某段时间后触发的事件。当用户按下键盘上的一个按键,操作系统就引发“按下按键”事件,等等。
对我们而言,窗口的事件处理器至关重要。大多数应用程序都有窗口或窗体。每个窗口都要有自己的事件处理器,一旦在窗口中发生事件都要调用事件处理器。例如,如果创建一个带多个按钮和文本框的窗口,则必须有一个与该窗口相关的窗口过程来处理这些事件。
windows操作系统以窗口过程的形式提供了这样一种机制,通常命名为<code>wndproc</code>(也可以叫其他名称)。每次指定窗口发生事件时,操作系统就会调用该过程。在下面的例子中,我们将创建第1个windows应用程序(即创建一个窗口),并解释窗口过程的用法。
准备就绪
确定安装并运行了<code>visual studio</code>。
操作步骤
执行下面的步骤。
1.创建一个新的c++ win32项目,命名为<code>guiproject</code>,单击右下方的【确定】。在弹出的向导窗口中单击【下一步】,在附加选项中勾选【空项目】,然后单击【完成】。现在,在【解决方案资源管理器】中右键单击【源文件】,选择【添加】,然后左键单击【新建项】。在弹出的窗口中选择【c++文件(.cpp)】,命名为<code>main</code>。然后,单击窗口右下方的【添加】。
2. 现在创建代码。首先,添加所需的头文件:<code>#include <windows.h></code>
大多数api都需要<code>windows.h</code>头文件才能处理一些视觉特性,如窗口、控件、枚举和样式。在创建一个应用程序入口点之前,必须先声明一个窗口过程的原型才能在窗口结构中使用它,如下代码所示:
int winapi winmain(hinstance hthis, hinstance hprev, lpstr szcmdline, int icmdshow)<code>`</code>
注意,在返回类型(int)后面有一个winapi宏,它表示一种调用约定<code>(calling convention)</code>。
可以用or(|)运算符组合多个位值(欲了解详细内容,请参阅msdn)。
接下来,在winmain的函数体中,用unreferenced_rarameter宏告诉编译器不使用某些参数,方便编译器进行一些额外的优化。如下代码所示:
wndex.hicon = loadicon( null, idi_application );
wndex.hiconsm = loadicon( null, idi_application );<code>`</code>
下面的代码定义了包含窗口过程的实例句柄:
`
wndex.hinstance = hthis;`
下面的代码定义了指向窗口过程的指针:
wndex.lpfnwndproc = wndproc;`
下面的代码定义了指向以空字符结尾的字符串或原子的指针:
wndex.lpszclassname = text("guiproject");`
下面的代码定义了指向以空字符结尾的字符串的指针,该字符串指定了窗口类菜单的资源名:
wndex.lpszmenuname = null;`
下面的代码定义了窗口类的样式:
wndex.style = cs_hredraw | cs_vredraw;`
下面的代码注册一个窗口类,供<code>createwindow</code>或<code>createwindowex</code>函数稍后使用:
hwnd hwnd = createwindow( wndex.lpszclassname, text("gui project"), ws_overlappedwindow,
200, 200, 400, 300, hwnd_desktop,null, hthis, 0 );
if ( !hwnd )
{
return -1;
}<code>`</code>
如果指定窗口的更新域未被填满,<code>updatewindow</code>函数就向窗口发送一条<code>wm_paint</code>消息,更新指定窗口的客户区。该函数绕过应用程序的消息队列,向指定窗口的窗口过程直接发送一条<code>wm_paint</code>消息。如下代码所示:
<code>updatewindow( hwnd );</code>
下面的代码设置指定窗口的显示状态:
showwindow( hwnd, icmdshow );`
我们还需要一个msg结构的实例来表示窗口消息。
msg msg = { 0 };`
接下来,进入一个消息循环。<code>windows</code>中的应用程序是事件驱动的,它们不会显式调用函数(如,c运行时库调用)来获得输入,而是等待系统把输入传递给它们。系统把所有的输入传递给应用程序的不同窗口。每个窗口都有一个叫做窗口过程的函数,当有输入需要传递给窗口时,系统调用会调用该函数。窗口过程处理输入,并把控制权返回系统。<code>getmessageapi</code>从主调线程的消息队列中检索信息,如下代码所示:
lresult callback wndproc( hwnd hwnd, uint umsg, wparam wparam, lparam lparam )
{<code>`</code>
消息代码负责处理消息,如默认消息(该例中是<code>wm_close</code>),即正在关闭应用程序时系统发送的消息。然后,调用<code>postquitmessageap</code>i释放系统资源,并安全关闭该应用程序。
return defwindowproc(hwnd, umsg, wparam, lparam);
}
}
return 0;
虽然本节介绍的窗口应用程序示例非常简单,但是它完整地反映了事件驱动系统特性和事件处理机制。在后面的章节中,我们将频繁地使用事件处理,所以理解这些基本过程非常重要。