天天看點

《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;

雖然本節介紹的視窗應用程式示例非常簡單,但是它完整地反映了事件驅動系統特性和事件處理機制。在後面的章節中,我們将頻繁地使用事件處理,是以了解這些基本過程非常重要。