[轉載]Windows程式如何建立一個視窗。

來源:互聯網
上載者:User

摘抄自羅雲彬的《Windows環境下32位組合語言程式設計》 

第4章 第一個視窗程序

  4.1 開始瞭解視窗(3)

  讓我們開啟一個DOS視窗,切換到FistWindow所在的目錄,運行環境設定的批次檔var.bat,再鍵入nmake編譯出FirstWindow.exe,這個程式只有2 560位元組,運行後視窗出來了,4.3所示。對於這個視窗,使用者可以拖動邊框去改變大小、按標題列上的按鈕來最大化和最小化,當游標移到邊框的時候,會自動變成雙箭頭……總之,這個視窗包括了一個典型視窗的所有特徵。

  接下來開始分析原始碼,看了這三頁多的原始碼,第一個感覺是什嗎?是不是想撤退了?筆者剛開始編Win32程式的時候就是這種感覺,可能90%的人有同樣的感覺,別急,過了這一關,Win32彙編的入門就成功了一半,所以千萬要挺住!有個振奮人心的訊息是,這個程式是大部分視窗程序的模板,以後要寫一個新的程式,把它拷貝過來再往中間添磚加瓦就是了,功夫一點都不白費。

  先靜下心來分析一下程式的結構,還看得懂,很好!其實來源程式的結構在第3章裡已經瞭解過了,首先是注釋……模式定義……include…… .data資料區段,都沒有問題,這些已經佔去了近40行了,好了,終於是關鍵的程式碼片段了,統計一下,只剩80行代碼了。

  分析一下程式的結構,發現入口是start,然後執行了一個_WinMain子程式,完成後就是程式退出的函數ExitProcess,再看_WinMain的結構,前面是順序下來的幾個API:

  GetModuleHandle → RtlZeroMemory → LoadCursor  → RegisterClassEx

  → CreateWindowEx → ShowWindow → UpdateWindow

  從名稱上就能看出它們的用途,很明顯,視窗是在CreateWindowEx處建立的,ShowWindow則是把視窗顯示在螢幕上,這些代碼是視窗的建立過程。

  接下來,就是一個由3個API組成的迴圈了:

  GetMessage → TranslateMessage → DispatchMessage

  很明顯,這是和訊息有關的迴圈,因為API名稱中都帶有Message字樣,如果退出這個迴圈,程式也就結束了,這個迴圈叫做訊息迴圈。設定_WinMain子程式並不是必須的,可以把_WinMain的所有代碼放到主程式中,沒有任何影響,之所以這樣只是為了將這裡使用的變數定義成局部變數,這樣可以方便移植。

  看了程式的流程,似乎沒有什麼地方涉及視窗的行為,如改變大小和移動位置的處理等。再看來源程式,除了_WinMain,還有一個子程式_ProcWinMain,但除了在WNDCLASSEX結構的賦值中提到過它,好像就沒有什麼地方要用到這個子程式,起碼在自己編寫的原始碼中沒有任何一個地方調用過它。

  再看_ProcWinMain,它是一個分支結構處理的子程式,功能是把參數uMsg取出來,根據不同的uMsg執行不同的代碼,完了以後就退出了,中間也沒有任何東西和主程式有關聯。

  第一個視窗程序就是由這麼兩個似乎是風馬牛不相及的部分組成的,但它確實能工作,對於寫慣了DOS彙編的程式員來說,這似乎不可理解。下面來看看這麼一個陌生而奇怪的程式是如何工作的。

  3. 視窗程序的運行過程

  在螢幕上顯示一個視窗的過程一般有以下步驟,這就是主程式的結構流程:

  (1)得到應用程式的控制代碼(GetModuleHandle)。

  (2)註冊視窗類別(RegisterClassEx)。在註冊之前,要先填寫RegisterClassEx的參數WNDCLASSEX結構。

  (3)建立視窗(CreateWindowEx)。

  (4)顯示視窗(ShowWindows)。

  (5)重新整理視窗客戶區(UpdateWindow)。

  (6)進入無限的訊息擷取和處理的迴圈。首先擷取訊息(GetMessage),如果有訊息到達,則將訊息指派到回呼函數處理(DispatchMessage),如果訊息是WM_QUIT,則退出迴圈。

  程式的另一半_ProcWinMain子程式是用來處理訊息的,它就是視窗的回呼函數(Callback),也叫做視窗過程,之所以是回呼函數是因為它是由Windows而不是我們自己調用的,我們調用DispatchMessage,而DispatchMessage再回過來調用視窗過程。

  所有的使用者操作都是通過訊息來傳給應用程式的,如使用者按鍵,滑鼠移動,選擇了菜單和拖動了視窗等,應用程式中由視窗過程接收訊息並處理,在例子程式中就是_ProcWinMain。視窗過程構造了一個分支結構,對應不同的訊息執行不同的代碼,所以一個應用程式中幾乎所有的功能代碼都集中在視窗過程裡。

  視窗程序運行中訊息傳輸的流程可以由圖4.4來表示。

  先來看看Windows對訊息的處理。Windows在系統內部有一個系統訊息佇列,當輸入裝置有所動作的時候,如使用者按動了鍵盤、移動了滑鼠,按下或放開了滑鼠等,Windows都會產生相應的記錄放在系統訊息佇列裡,4.4中的箭頭a和b所示,每個記錄中包含訊息的類型、發生的位置(如滑鼠在什麼座標移動)和發生的時間等資訊。

  圖4.4  視窗程序的運行過程

  同時,Windows為每個程式(嚴格地說是每個線程)維護一個訊息佇列,Windows檢查系統訊息佇列裡訊息的發生位置,當位置位於某個應用程式的視窗範圍內的時候,就把這個訊息派送到應用程式的訊息佇列裡,4.4中的箭頭c所示。

  當應用程式還沒有來取訊息的時候,訊息就暫時保留在訊息佇列裡,當程式中的訊息迴圈執行到GetMessage的時候,控制權轉移到GetMessage所在的USER32.DLL中(箭頭1),USER32.DLL從程式訊息佇列中取出一條訊息(箭頭2),然後把這條訊息返回應用程式(箭頭3)。

  應用程式可以對這條訊息進行預先處理,如可以用TranslateMessage把基於鍵盤掃描碼的按鍵訊息轉換成基於ASCII碼的鍵盤訊息,以後也會用到TranslateAccelerator把鍵盤快速鍵轉換成命令訊息,但這個步驟不是必需的。

  然後應用程式將處理這條訊息,但方法不是自己直接調用視窗過程來完成,而是通過DispatchMessage間接調用視窗過程,Dispatch的英文含義是“指派”,之所以是“指派”,是因為一個程式可能建有不止一個視窗,不同的視窗訊息必須指派給相應的視窗過程。當控制權轉移到USER32.DLL中的DispatchMessage時,DispatchMessage找出訊息對應視窗的視窗過程,然後把訊息的具體資訊當做參數來調用它(箭頭5),視窗過程根據訊息找到對應的分支去處理,然後返回(箭頭6),這時控制權回到DispatchMessage,最後DispatchMessage函數返回應用程式(箭頭7)。這樣,一個迴圈就結束了,程式又開始新一輪的GetMessage。

  有個很常見的問題:為什麼要由Windows來調用視窗過程,程式取了訊息以後自己處理不是更簡便嗎?事實上並非如此,如果程式自己處理訊息的“指派”,就必須自己維護本程式所屬視窗的列表,當程式建立的視窗不止一個的時候,這個工作就變得複雜起來;另一個原因是:別的程式也可能用SendMessage通過Windows直接調用你的視窗過程;第三個原因:Windows並不是把所有的訊息都放進訊息佇列,有的訊息是直接調用視窗過程處理的,如WM_SETCURSOR等即時性很強的訊息,所以視窗過程必須開放給Windows。

  應用程式之間也可以互發訊息,PostMessage是把一個訊息放到其他程式的訊息佇列中,4.4中箭頭d所示,目標程式收到了這條訊息就把它放入該程式的訊息佇列去處理;而SendMessage則越過訊息佇列直接調用目標程式的視窗過程(4.4中箭頭I所示),視窗過程返回以後才從SendMessage返回(4.4中箭頭II所示)。

  視窗過程是由Windows回調的,Windows又是怎麼知道往哪裡回調呢?答案是我們在調用RegisterClassEx函數的時候告訴了Windows。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.