(譯)win32asm教程-12-完結 (轉)
這個是本系列教程的最後一篇了。下面我可能會貼出關於用寫一個遊戲的例項。仍然是面向基礎,面向初學者的。在此,也要感謝原作者:james的大力支援,無償提供本文的中文譯權。
-譯者taowen2002
13.0 中的視窗:namespace prefix = o ns = "urn:schemas--com::office" />
在本章中,我們將建立一個有視窗的
12.1視窗
你可能已經猜到了Windows之所以稱為Windows的原因了。在Windows中,有兩種程式:GUI程式和控制檯程式。控制檯的程式看上去就像Dos程式,它們在一個似-dos的視窗中執行。你使用的大多數程式是GUI(圖形介面)程式,它們有一個用於和使用者互動的圖形介面。這是由建立視窗來完成的。幾乎你在Windows中看見的每一件東西都是視窗。首先,你建立一個父視窗,然後是像編輯框,靜態(文字標籤-譯者注),按鈕等的自視窗(控制元件)。
13.2視窗類
每一個視窗都有名字。你為你的父視窗定義你自有的類。對於控制元件,你可以使用Windows的標準類名(例如,“Edit”,“Static”,“Button”)
13.3結構
你程式中的視窗類是用“RegisterClassEx“註冊的。(RegisterClassEx是RegisterClass的擴充套件版本,後者已經不太使用了)這個函式的宣告是:
ATOM RegisterClassEx(
CONST WNDLCASSEX *lpwcx//有類資料的結構之地址
);
lpwcx:指向WNDCLASSEX結構。在把它傳遞給函式之前,你必須用適當的類屬性填寫結構。
唯一的引數是指向結構的指標。先來看看一些結構的基本知識:
一個結構是一些變數(資料)的集合。它用STRUCT定義:
SOMESTRUCTURE STRUCT
d1 dd ?
dword2 dd ?
some_word dw ?
abyte ?
anotherbyte db ?
SOMESTRUCTURE ENDS
(結構名不一定要大寫)
你可以用問號把你的變數定義在未初始化data部分。現在你可以根據定義建立一個結構:
Initialized
Initializedstructure SOMESTRUCTURE <100,200,10,'A',90h>
Uninitialized
UnInitializedstructure SOMESTRUCTURE <>
在第一個例子中,建立了一個新的結構(用初始化了的結構儲存它的offset),而且結構的每一個元素用初始化數值填寫了。第二個例子只是告訴masm為結構名分配,而且每個資料元素用0初始化。在建立了結構之後,你可以在程式碼中使用它:
mov eax, Initializedstructure.some_word
; eax現在是 10
inc UnInitializedstructure.dword1
; 結構的dword1步增
這是結構怎樣存在記憶體中的:
記憶體地址
內容
offset of Initializedstructure
100 (dword, 4 bytes)
offset of Initializedstructure + 4
200 (dword, 4 bytes)
offset of Initializedstructure + 8
10 (word, 2 bytes)
offset of Initializedstructure + 10
65 or 'A' (1 byte)
offset of Initializedstructure + 11
90h (1 byte)
12.3 WNDCLASSEX
現在已經瞭解了足夠多的結構知識,讓我們處理RegisterClassEx吧。在《程式設計師參考》中,你可以查詢WNDCLASSEX結構的定義。
typedef struct _WNDCLASSEX { // wc
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
HICON hIconSm;
} WNDCLASSEX;
解釋
cbSize
WNDCLASSEX結構體的大小。用於Windows的。你可以用SIZEOF得到它的大小:
mov wc.cbsize, SIZEOF WNDCLASSEX
style
為類指定一個樣式(如果視窗要有捲軸,加上重畫標誌。等等)
lpfnWndProc
指向Windows過程的指標(本章後面有更多內容)
cbClsExtra
在Windows類結構後本配多少額外記憶體。對我們不重要
cbWndExtra
在Windows例項後分配多少額外記憶體。對我們也不重要
hInstance
你程式的實力控制程式碼。你可以用GetMoudleHandle函式得到這個控制程式碼
hIcon
視窗圖示資源的控制程式碼
hCursor
視窗游標資源的控制程式碼
hbrBackground
用於填充背景的畫刷控制程式碼,或是標準刷子型別中的一個,如 COLOR_WINDOW, COLOR_BTNFACE , COLOR_BACKGROUND.
lpszMenuName
指向一個指定選單類名的零結尾字串
lpszClassName
指向一個指定視窗類名的零結尾字串
hIconSm
一個和視窗類關聯的小圖示控制程式碼
在你的Win32夾中建立一個名為firstWindow的資料夾並在這個資料夾中建立一個名為window.asm的新檔案,輸入一下內容:
.486
.model flat, stdcall
option casemap:none
includelib masm32libkernel32.lib
includelib masm32libuser32.lib
includelib masm32libgdi32.lib
include masm32includewindows.inc
include masm32includekernel32.inc
include masm32includeuser32.inc
include masm32includegdi32.inc
然後建立一個名為make.bat的.bat檔案。把這些文字貼上進去:
@echo off
ml /c /coff window.asm
link /subsystem:windows window.obj
pause>nul
從現在開始,為了節省空間,僅顯示小段的程式碼。你可以透過點
譯者注:為了方便,我又把這些放回來了。
13.4註冊類
現在我們在名為WinMain的過程中註冊類。該過程中完成視窗的初始化。
把這些加入你的彙編檔案:
WinMain PROTO STDCALL :DWORD, :DWORD, :DWORD
.data?
hInstance dd ?
.code
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke WinMain, hInstance, NULL, NULL, SW_SHOWNORMAL
end start
這些程式碼透過GetModuleHandle得到模組控制程式碼,並把模組控制程式碼放入hInstance變數中。這個控制程式碼在Windows 中頻繁使用。然後它WinMain過程。這不是一個API函式,而是一個我們將要定義的過程。原型是:WinMain PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD,因而是一個帶4個引數的函式:
現在把這些程式碼放在end start:前
WinMain proc hInst:DWORD, hPrevInst:DWORD, CmdLine:DWORD, CmdShow:DWORD
ret
WinMain endp
你根本就不需要用這個winmain過程,但這是一種十分普遍的處世化你的程式的方法。Visual C自動初始化這個函式的引數,但我們必須自己來做。現在不要管hPrevInst和CmdLine。集中注意在hInst和CmdShow上。Hinst是例項控制程式碼(=模組控制程式碼),CmdShow是定義視窗該如何顯示的標誌。(你可以在API參考關於ShowWindows部分發現更多)
在前面程式碼中的"invoke WinMain, hInstance, NULL, NULL, SW_SHOWNORMAL"用正確的例項控制程式碼和顯示標誌呼叫這個函式。現在我們可以在WinMain中寫我們的初始化程式碼了。
WinMain proc hInst:DWORD, hPrevInst:DWORD, CmdLine:DWORD, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL hwnd:DWORD
ret
WinMain endp
這有我們將在過程中要用的兩個區域性變數
.data
ClassName db "FirstWindowClass",0
.code
WinMain proc hInst:DWORD, hPrevInst:DWORD, CmdLine:DWORD, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL hwnd:DWORD
; now set all the structure members of the WNDCLASSEX structure wc:
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, ADDR wc
ret
WinMain endp
讓我們來看看發生了什麼:
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
初始化了結構的大小(這是RegisterClassEx要求的)。設定類的樣式為”CS_HREDRAW or CS_VREDRAW”,然後設定了視窗過程的offset。你在後面會知道什麼是視窗過程,現在你僅需要記住你需要WndProc過程的地址。該地址可以透過“offset WndProc”獲得。Cb.ClsExtra和cb.WndExtra我們沒有使用因而設它們為Null。
Push hInst
Pop wc.hInstance
Wc.hInstance設為WinMain的hInst引數。為什麼我們不用:mov wc.hInstance, hInst?因為mov指令不允許從一個地址移到另一個地址。透過push/pop,值被壓入棧,然後又彈入目標中。
mov wc.hbrBackground, COLOR_WINDOW
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassName
類的背景色被設為COLOR_WINDOW,沒有定義選單(null)而且lpszClassName設為一個指向零結尾的類名字串:“FirstWindowClass”它應該是一個在你的程式中定義的唯一名字。
invoke LoadIcon, NULL, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, eax
視窗需要一個圖示。但又因為我們要一個指向圖示的控制程式碼,我們使用LoadIcon來載入圖示並獲得控制程式碼。LoadIcon有兩個引數:hInstance和lpIconName。HInstance是包含圖示的可檔案的模組控制程式碼。LpIconName是一個指向圖示資源和圖示ID的字串的指標。如果你用NULL為hInstance,你可以從一些標準圖表中選這一個(這卻是是因為我們在這裡還沒有圖示資源)hIconSm是小圖示,你可以對它使用相同的控制程式碼。
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
對游標也一樣。NULL作hInstance,並用一個標準游標型別:IDC_ARROW,標準Windows箭頭型游標。
invoke RegisterClassEx, ADDR wc
現在,最終用RegisterClassEx來註冊類,透過一個指向WNDCLASSEX結構的指標作引數。
13.5建立視窗
現在,你已經註冊了一個類,你可以使用它建立一個視窗:
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和dwStyle是兩個決定視窗樣式的引數。
LpClassName 是一個指向你註冊了的類名的指標。
LpWindowName 是你視窗的名字(如果有的話,這將成為你視窗的標題)
X, Y, nWidth, nHeight 決定你視窗的位置和大小
HMenu 是選單視窗的控制程式碼(在後面討論,現在為空)
HInstance 是程式例項的控制程式碼
LpPara是你能在你的程式中使用的擴充套件值
.data
AppName "FirstWindow",0
.code
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
CW_USEDEFAULT,400,300,NULL,NULL,
hInst,NULL
mov hwnd, eax
invoke ShowWindow, hwnd, SW_SHOWNORMAL
invoke UpdateWindow, hwnd
(注意使彙編器讀下一行的時候好像還在同一行)
我們的程式碼將用我們剛剛註冊的類名建立一個新的視窗。標題是“FirstWindow”(程式名,AppName),樣式是WS_OVERLAPPEDWINDOW,這是一個建立有標題,選單,可縮放邊框和最大化/最小化按鈕的視窗樣式。CW_USERDEFAULT作為x和y的位置會使Windows為新視窗使用預設位置。視窗的(初始)大小是400×300象素。
函式的返回值是視窗控制程式碼,HWND。它儲存在區域性變數hwnd中。然後視窗用ShowWindow顯示。UpdateWindow確保視窗被畫出。
13.6訊息迴圈
視窗可以透過訊息和你的程式以及其他視窗通訊。無論何時,一條訊息被髮送給指定的視窗。它的視窗過程都要被呼叫。每個視窗都有一個訊息迴圈或訊息泵(pump)。這是一個無止盡的檢查是否給有你的視窗的訊息的迴圈。而且如果有,把訊息傳遞給dispatchMessage函式。這個函式會呼叫你的視窗過程。訊息迴圈和視窗過程是兩個完全不同的東西!!!
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD ........ .WHILE TRUE 這是訊息迴圈看上去的樣子。.WHILE TRUE, .ENDW迴圈到eax為0之前都會繼續。如果它接到了WM_QUIT訊息,GetMessage返回0,這將關閉視窗因而程式應該在不論GetMessage返回0時退出。如果不是這樣(0),訊息被傳遞給TranslateMessage(這個函式把按鍵翻譯為訊息)而且訊息被Windows用DispatchMessage函式解包。訊息本身在一個訊息迴圈的組成部分MSG結構中(LOCAL msg: MSG被加入過程,增加了一個稱為msg的區域性訊息結構)你可以在你的所有程式中用這個訊息迴圈。 13.7視窗過程 訊息會被髮送往視窗過程。一個視窗過程看上去總是這樣: WndProc PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD .code WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD 視窗過程總是有4個引數 hWnd 包含視窗控制程式碼 uMsg 訊息 wParam 訊息的第一個引數(由訊息定義) lParam 訊息的第二個引數(由訊息定義) 視窗不處理的訊息應該傳遞給DefWindowProc,它會處理這些。一個視窗過程的例子: WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD 這段程式碼在視窗初始化時顯示程式名稱。也要注意我加入了WM_DESTROY訊息的處理。這條訊息在視窗將要關閉的時候傳送。程式要用PostQuitMessage作出反應。 現在看看最終的程式碼: .486 .model flat, stdcall option casemap:none includelib masm32libkernel32.lib includelib masm32libuser32.lib includelib masm32libgdi32.lib include masm32includewindows.inc include masm32includekernel32.inc include masm32includeuser32.inc include masm32includegdi32.inc WinMain PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD WndProc PROTO STDCALL :DWORD, :DWORD, :DWORD, :DWORD .data? hInstance dd ? .data ClassName db "FirstWindowClass",0 AppName db "FirstWindow",0 .code start: invoke GetModuleHandle, NULL mov hInstance, eax invoke WinMain, hInstance, NULL, NULL, SW_SHOWNORMAL invoke ExitProcess, NULL WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX LOCAL hwnd:DWORD LOCAL msg:MSG mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst pop wc.hInstance mov wc.hbrBackground,COLOR_WINDOW mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon, eax mov wc.hIconSm, eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax invoke RegisterClassEx, addr wc INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName, WS_OVERLAPPEDWINDOW-WS_SIZEBOX-WS_MAXIMIZEBOX,CW_USEDEFAULT, CW_USEDEFAULT,400,300,NULL,NULL, hInst,NULL mov hwnd,eax invoke ShowWindow, hwnd,SW_SHOWNORMAL invoke UpdateWindow, hwnd .WHILE TRUE invoke GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .ENDW mov eax,msg.wParam ret WinMain endp WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD mov eax, uMsg .IF eax==WM_CREATE invoke MessageBox, NULL, ADDR AppName, ADDR AppName, NULL .ELSEIF eax==WM_DESTROY invoke PostQuitMessage, NULL .ELSE invoke DefWindowProc, hWnd, uMsg, wParam, lParam .ENDIF ret WndProc endp end start
LOCAL wc:WNDCLASSEX
LOCAL hwnd:DWORD
LOCAL msg:MSG ;<<
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax, uMsg
.IF eax==XXXX
.ELSEIF eax==XXXX
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
.ENDIF
ret
WndProc endp
mov eax, uMsg
.IF eax==WM_CREATE
invoke MessageBox, NULL, ADDR AppName, ADDR AppName, NULL
.ELSEIF eax==WM_DESTROY
invoke PostQuitMessage, NULL
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
.ENDIF
ret
WndProc endp
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991692/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- (譯)win32asm教程-7 (轉)Win32ASM
- (譯)win32asm教程-9 (轉)Win32ASM
- (譯)win32asm教程-8 (轉)Win32ASM
- (譯)win32asm教程-10 (轉)Win32ASM
- (譯)win32asm教程-11 (轉)Win32ASM
- (譯)win32asm教程-5 (轉)Win32ASM
- (譯)win32asm教程-6 (轉)Win32ASM
- (譯)win32asm例項-1 (轉)Win32ASM
- (譯)win32asm例項-3 (轉)Win32ASM
- Nim教程【十五】【完結】
- ModernUI教程:目錄 (完結)UI
- Iczelion 的 Win32Asm VxD 彙編教程 (二) (轉)Win32ASM
- Iczelion 的 Win32Asm VxD 彙編教程 (四) (轉)Win32ASM
- Iczelion 的 Win32Asm VxD 彙編教程 (三) (轉)Win32ASM
- Iczelion 的 Win32Asm VxD 彙編教程 (七) (轉)Win32ASM
- Iczelion 的 Win32Asm VxD 彙編教程 (六) (轉)Win32ASM
- Iczelion 的 Win32Asm VxD 彙編教程 (八) (轉)Win32ASM
- Iczelion 的 Win32Asm VxD 彙編教程 (一) (轉)Win32ASM
- Iczelion 的 Win32Asm VxD 彙編教程 (五) (轉)Win32ASM
- win32asm原理 (轉)Win32ASM
- 微信小程式教程04:API(完結)微信小程式API
- python教程12-物件導向進階Python物件
- Win32Asm 教程,來自易語言俱樂部論壇,譯者taowen。 (5千字)Win32ASM
- Nodejs教程30(完結):PM2入門NodeJS
- web前端教程《每日一題》(1-99)完結Web前端每日一題
- Win32ASM經驗點滴 (轉)Win32ASM
- 我的WebDesign之路--完結篇 (轉)Web
- Fedora Core 4 基礎教程 (上傳完畢)(轉)
- Delphi程式碼最佳化 完結篇 (轉)
- Javascript保留格式翻譯選區內容及預覽(完結)JavaScript
- git指南完結Git
- 完結撒花!
- FreeLearning 安全譯文集翻譯完畢
- 這可能是最好的RxJava 2.x 教程(完結版)RxJava
- ORACLE SQL效能最佳化系列 (十四) 完結篇 (轉)OracleSQL
- 【完結啦】TDD 構建論壇課程筆記完結啦筆記
- ArchSummit乾貨之旅(完結)MIT
- 7.5 - 貪心篇完結