Win32彙編教程四 編寫一個簡單的視窗 (轉)

worldblog發表於2007-12-02
Win32彙編教程四 編寫一個簡單的視窗 (轉)[@more@]


--------------------------------------------------------------------------------

有關視窗的基本知識

視窗是螢幕上的矩形區域。一個視窗可以從鍵盤或者滑鼠接受的輸入,並在其內部顯示圖形輸出。一個應用視窗通常包含程式的標題條、選單、邊框,捲軸。其中,對話方塊也是一種視窗。不同的是,對話方塊表面通常包含幾個其它視窗,稱之為“子視窗”。這些子視窗的形式有壓入按鈕、單選按鈕、核取方塊、文字輸入區域、列表框和捲軸等。 使用者將這些視窗看成螢幕上的,可以透過按下一個按鈕或者滾動一個捲軸與這些物件直接互動。
視窗以“訊息”的形式接收視窗的輸入,視窗也用訊息與其它視窗通訊。比如在程式視窗的大小改變時,字會重新格式化其中的文字。視窗大小改變的細節是由操作處理的,但程式能夠響應這個系統功能。當使用者改變視窗的大小時,給程式傳送一條訊息指出新視窗的大小。然後,程式就可以調整視窗中的內容,以響應大小的變化。程式建立的每一個視窗都有相關的視窗過程。也就是給這個視窗指定一個子程式(視窗過程),Windows透過它來給視窗傳送訊息。視窗過程再根據此訊息進行處理,然後將控制返回給Windows。
視窗在“視窗類”的基礎上建立的。Windows定義了確省的視窗過程,如果你對所有的訊息都讓Windows自己處理,那麼你就能得到一個標準的視窗,同樣,你也可以選擇處理自己感興趣的訊息,這樣,相當於產生了不同的子類,也就形成了不同的應用程式。同樣,子視窗也是基於同一個視窗類,並且使用同一個視窗過程。例如,所有Windows 程式中的所有按鈕都基於同一視窗類。這個視窗類有一個處理所有按鈕訊息的視窗過程,但是,如果你按自己的設想設計一個按鈕,如想把按鈕的表面換成點陣圖,你就可以自己處理按鈕視窗的 WM_PAINT 訊息,當 Windows 需要畫按鈕表面的時候,你就可以隨自己的意思去畫。
Windows程式開始後,Windows為該程式建立一個“訊息佇列”。這個訊息佇列用來存放該程式可能建立的各種不同視窗的訊息。程式中有一段程式碼,叫做“訊息迴圈”, 它用來從佇列中取出訊息,並且將它們傳送給相應的視窗過程。在沒有訊息發生的時候,你的程式實際上就在訊息迴圈中轉圈子。
建立一個視窗的過程如下:

取得程式的例項控制程式碼(hInstance)
註冊視窗類,實際上就是為你的視窗指定處理訊息的過程,定義游標,視窗風格,顏色等引數
建立視窗
顯示視窗
然後進入訊息迴圈,也就是不停地檢測有無訊息,並把它傳送給視窗程式去處理。
建立一個視窗的程式碼在不同的程式中實際上是幾乎一模一樣的,所以你編一個新的程式時可以把這一段拷來拷去,稍微修改一下就行,程式的大部分程式碼實際上是用在視窗過程中,因為這才是不同程式的不同之處。視窗過程的要點如下:

從Windows傳給視窗過程的引數 uMsg 得到訊息型別,並轉到不同的分枝去處理。
對自己已經處理的訊息,返回 Windows 時必須在eax 中返回0。
自己不處理的訊息,必須呼叫 DefWindowProc 處理,並把返回值傳回Windows,否則,Windows會無法顯示。
uMsg 引數中指定的訊息有280多種,實際上我們需要處理的只有重要的幾種,如Windows在建立的時候會傳送 WM_CREATE 訊息,我們就可以在這時候初始化,分配等等,而退出時會傳送 WM_CLOSE,我們就可以進行釋放記憶體等清除工作,當Windows上的選單或按鈕被按下時傳送 WM_COMMAND 訊息等等,具體可以參考 Programmer's Reference。下面,我們來看一個建立視窗的簡單程式。

一個建立視窗的程式


.386
.model flat, stdcall
option casemap :none ; case sensitive

include windows.inc
include user32.inc
include kernel32.inc
include comctl32.inc
include comdlg32.inc
include gdi32.inc

includelib user32.lib
includelib kernel32.lib
includelib comctl32.lib
includelib comdlg32.lib
includelib gdi32.lib

IDI_MAIN equ 1000 ;icon

IDM_MAIN equ 4000 ;menu
IDM_EXIT equ 4001

.data?

hInstance dd ?
hWinMain dd ?
hMenu dd ?
szBuffer 256 dup (?)

.data

szClassName db "Windows Template",0
szCaptionMain db '視窗模板',0

.code

start:
call _WinMain
invoke ExitProcess,NULL

_WinMain proc
local @stWcMain:WNDCLASSEX
local @stMsg:MSG

invoke InitCommonControls
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke LoadIcon,hInstance,IDI_MAIN
mov hIcon,eax
invoke LoadMenu,hInstance,IDM_MAIN
mov hMenu,eax
;*************** 註冊視窗類 *****************************************
invoke LoadCursor,0,IDC_ARROW
mov @stWcMain.hCursor,eax
mov @stWcMain.cbSize,sizeof WNDCLASSEX
mov @stWcMain.hIconSm,0
mov @stWcMain.style,CS_HREDRAW or CS_VREDRAW
mov @stWcMain.lpfnWndProc,offset WndMainProc
mov @stWcMain.cbClsExtra,0
mov @stWcMain.cbWndExtra,0
mov eax,hInstance
mov @stWcMain.hInstance,eax
mov @stWcMain.hIcon,0
mov @stWcMain.hbrBackground,COLOR_WINDOW + 1
mov @stWcMain.lpszClassName,offset szClassName
mov @stWcMain.lpszMenuName,0
invoke RegisterClassEx,addr @stWcMain
;*************** 建立輸出視窗 ***************************************
invoke CreateWindowEx,WS_EX_CLIENTEDGE,
offset szClassName,offset szCaptionMain,
WS_OVERLAPPEDWINDOW OR WS_VSCROLL OR WS_HSCROLL,
0,0,550,300,
NULL,hMenu,hInstance,NULL

invoke ShowWindow,hWinMain,SW_SHOWNORMAL
invoke UpdateWindow,hWinMain
;*************** 訊息迴圈 *******************************************
.while TRUE
invoke GetMessage,addr @stMsg,NULL,0,0
.break .if eax == 0
invoke TranslateMessage,addr @stMsg
invoke DispatchMessage,addr @stMsg
.endw
ret

_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
WndMainProc proc uses ebx edi esi,
hWnd:D,uMsg:DWORD,wParam:DWORD,lParam:DWORD

mov eax,uMsg
.if eax == WM_CREATE
mov eax,hWnd
mov hWinMain,eax
call _Init
;********************************************************************
.elseif eax == WM_COMMAND
.if lParam == 0
mov eax,wParam
.if ax == IDM_EXIT
call _Quit
.endif
.endif
;********************************************************************
.elseif eax == WM_CLOSE
call _Quit
;********************************************************************
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret

WndMainProc endp

_Init proc

invoke SendMessage,hWinMain,WM_SETICON,ICON_SMALL,hIcon

ret

_Init endp
;********************************************************************
_Quit proc

invoke DestroyWindow,hWinMain
invoke PostQuitMessage,NULL
ret

_Quit endp
;********************************************************************
end start

視窗程式的分析

讓我們來簡單分析一下這個程式,首先程式呼叫 _WinMain,在_WinMain 中定義了兩個區域性變數 @stMsg 和 @stWinMain,資料型別分別是 MSG 和 WNDCLASSEX結構,在參考手冊中,可以看到WNDCLASSEX定義了一個視窗的所有引數,如使用的選單、游標、顏色、視窗過程等,接下來的一大堆 mov 指令實際上就是在填寫這個資料結構,填寫完成後,最重要的兩句是 mov @stWcMain.lpfnWndProc,offset WndMainProc 定義了處理訊息的視窗過程, mov @stWcMain.lpszClassName,offset szClassName 定義了你要建立的類的名稱,然後就是使用 RegisterClassEx 註冊這個視窗類,注意,這時候視窗並沒有建立,你只不過是定義好了一個子類,接下去你要用你定義的類去建立一個視窗。也就是使用 CreateWindowEx 去建立它。在手冊中,CreateWindowEx 是這樣定義的:

HWND CreateWindowEx(
DWORD dwExStyle, // extended window style
LPCTSTR lpClassName, // pointer to registered class name
LPCTSTR lpWindowName, // pointer to window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // handle to menu, or child-window ntifier
HINSTANCE hInstance, // handle to application instance
LPVOID lpParam // pointer to window-creation data );

其中的引數 dwExStyle 是視窗的風格,lpClassName 就是我們自己定義的類的名字。如果大家要建立一個已經定義好的類,如 RichEdit 類等等,只要把 lpClassName 指向 "RichEdit32" 字串就行了,當然這時就不用 RegisterClass 以及編寫自己的視窗過程了。執行 CreateWindowEx 後,得到一個返回值就是視窗控制程式碼,這個值在以後是要經常用到了,所以要先儲存下來。這時視窗並沒有在螢幕上顯示出來,而是處於隱藏狀態,我們要用 ShowWindow 來顯示出視窗並用UpdateWindow 來繪視窗的內容。
視窗顯示出來後,程式就進入一個迴圈----訊息迴圈,前面我已經說過,作用是不停地接收 Windows 訊息併傳送給視窗過程去處理。GetMessage 從訊息佇列中取出一條訊息,如果取得的訊息不是 WM_QUIT,那麼 GetMessage 返回一個非零值,否則返回零,這時候迴圈結束,程式執行 ExitProcess退回。TranslateMessage 將訊息進行一些鍵盤轉換,用於處理一些,DispatchMessage 將處理後的訊息發回 Windows,由Windows呼叫視窗程式進行處理,當視窗程式處理完返回後,程式才從 DispatchMessage 返回,從而開始下一個 GetMessage 呼叫。這些函的引數可以參考手冊。

視窗過程的分析

視窗過程有四個引數,hWnd 是本視窗的控制程式碼,和建立視窗時返回的值相同,uMsg 是本次呼叫的訊息型別,wParam 和lParam是訊息的引數,其含義和數值根據訊息的不同而不同。在本程式中,我們處理 WM_CREATE,WM_COMMAND 和 WM_QUIT 訊息,然後返回0,對不處理的訊息,使用 invoke DefWindowProc,hWnd,uMsg,wParam,lParam 來處理並直接用 ret 將返回值傳回 Windows。在響應 WM_CLOSE 訊息時,我們用 DestroyWindow 清除視窗並用PostQuitMessage 產生一條 WM_QUIT 訊息,從而使程式在 訊息迴圈呼叫GetMessage 時返回0,以結束訊息迴圈並結束程式。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-987442/,如需轉載,請註明出處,否則將追究法律責任。

相關文章