天天看点

《C++多线程编程实战》——1.8 事件处理器和消息传递接口

本节书摘来自异步社区出版社《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 &lt;windows.h&gt;</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;

虽然本节介绍的窗口应用程序示例非常简单,但是它完整地反映了事件驱动系统特性和事件处理机制。在后面的章节中,我们将频繁地使用事件处理,所以理解这些基本过程非常重要。