Win32彙編語言編程-彈出消息框

回憶了一下彙編語言的項目,通過寫一個 Windows 程序,程序運行時將彈出一個消息框並顯示"Assembly is great!"來回憶Windows API的使用方法。

一、Windows API是什麼?如何使用?

Windows 爲編寫應用程序提供了大量的資源。其中最重要的是Windows API (Application Programming Interface)。 Windows API是一大組功能強大的函數,它們本身駐紮在 Windows 中供人們隨時調用。這些函數的大部分被包含在幾個動態鏈接庫(DLL)中,譬如:kernel32.dll、 user32.dll 和 gdi32.dll。

Kernel32.dll中的函數主要處理內存管理和進程調度;
user32.dll中的函數主要控制用戶界面;
gdi32.dll中的函數則負責圖形方面的操作。除了上面主要的三個動態鏈接庫,您還可以調用包含在其他動態鏈接庫中的函數,當然您必須要有關於這些函數的足夠的資料。

動態鏈接庫,顧名思義,這些 API 的代碼本身並不包含在 Windows 的可執行文件中,而是當要使用時才被加載。爲了讓應用程序在運行時能找到這些函數,就必須事先把有關的重定位信息嵌入到應用程序的可執行文件中。這些信息存在於引入庫中,由鏈接器把相關信息從引入庫中找出插入到可執行文件中。您必須指定正確的引入庫,因爲只有正確的引入庫纔會有正確的重定位信息。

當應用程序被加載時 Windows 會檢查這些信息,這些信息包括動態鏈接庫的名字和其中被調用的函數的名字。若檢查到這樣的信息,Windows就會加載相應的動態鏈接庫,並且重定位調用的函數語句的入口地址,以便在調用函數時控制權能轉移到函數內部。

如果從和字符集的相關性來分,API 共有兩類:
一類是處理 ANSI 字符集的,另一類是處理 UNICODE 字符集的。

前一類函數名字的尾部帶一個"A"字符,處理UNICODE的則帶一個"W"字符("W"代表寬字符)。我們比較熟悉的ANSI字符串是以 NULL 結尾的一串字符數組,每一個ANSI字符是一個 BYTE 寬。對於歐洲語言體系,ANSI 字符集已足夠了,但對於有成千上萬個唯一字符的幾種東方語言體系來說就只有用 UNICODE 字符集了。每一個 UNICODE 字符佔有兩個 BYTE 寬,這樣一來就可以在一個字符串中使用 65336 個不同字符了。

這也是爲什麼引進 UNICODE
的原因。在大多數情況下我們都可以用一個包含頭文件,在其中定義一個宏,然後在實際調用函數時,函數名後不需要加後綴"A"或"W"。比如說:在頭文件中定義函數foo():

#ifdef UNICODE
#define foo() 
fooW()
#else
#define foo() fooA()
#endif

具體舉例

把框架程序放在下面,然後我們再向裏面加東西。

.386
.model flat, stdcall
.data
.code
start:
end start

應用程序的執行是從 END 定義的標識符後的第一條語句開始的。在上面的框架程序中就是從 START 開始。程序逐條語句執行一直到遇到
JMP,JNE,JE,RET 等跳轉指令。這些跳轉指令將把執行權轉移到其他語句上,若程序要退出 Windows,則必須調用函數 ExitProcess。

ExitProcess proto uExitCode:DWORD

上面一行是函數原型。函數原型會告訴編譯器和鏈接器該函數的屬性,這樣在編譯和鏈接時,編譯器和鏈接器就會作相關的類型檢查。 函數的原型定義如下:

FunctionName PROTO [ParameterName]:DataType,[ParameterName]:DataType,... 

簡言之,就是在函數名後加僞指令PROTO,再跟一串由逗號相隔的數據類型鏈表。在前面的 ExitProcess 定義中,該函數有一個 DWORD
類型的參數。當您使用高層調用語句 INVOKE 時,使用函數原型定義特別有用,您可以簡單地認爲 INVOKE 是一個有參數類型檢查的調用語句。譬如,假設這樣寫:

call ExitProcess

若您事先沒把一個DWORD類型參數壓入堆棧,編譯器和鏈接器都不會報錯,但毫無疑問,在您的程序運行時將引起崩潰。但是,當這樣寫:

invoke ExitProcess

連接器將報錯提醒您忘記壓入一個 DWORD 類型參數。所以我建議您用 INVOKE 指令而不是CALL去調用一個函數。INVOKE 的語法如下:

INVOKE expression [,arguments]

expression 既可以是一個函數名也可以是一個函數指針。參數由逗號隔開。大多數API函數的原型放在頭文件中。 如果您用的是 hutch 的
MASM32,這些頭文件在文件夾MASM32/include 下, 這些頭文件的擴展名爲 INC,函數名和 DLL 中的函數名相同,譬如:KERNEL32.LIB 引出的函數 ExitProcess 的函數原形聲明於kernel.inc中。您也可以自己聲明函數原型。

好,我們現在回到ExitProcess 函數,參數uExitCode 是您希望當您的應用程序結束時傳遞 Windows 的。 您可以這樣寫:

invoke ExitProcess,0 

把這一行放到開始標識符下,這個應用程序就會立即退出 Windows,當然毫無疑問個應用程序本身是一個完整的 Windows 程序。

.386
.model flat, stdcall
option 
casemap:none

include \masm32\include\windows.inc
include 
\masm32\include\kernel32.inc
includelib 
\masm32\lib\kernel32.lib

.data
.code
start:
invoke 
ExitProcess,0
end start

option casemap:none 一句的意思是告訴 MASM 要區分標號的大小寫,譬如:start 和 START 是不同的。請注意新的僞指令
include,跟在其後的文件名所指定的文件在編譯時將“插”在該處。在我們上面的程序段中,當MASM處理到語句 include \masm\include\windows.inc 時,它就會打開文件夾\MASM32\include中的文件windows.inc,這和您把整個文件都粘貼到您的源程序中的效果是一樣的。 hutch 的 windows.inc 包含了 WIN32編程所需要的常量和結構體的定義。 但是它不包含函數原型的定義。

好了,現在保存例子,取名爲msgbox.asm。把 ml.exe 的路徑放到 PATH 環境變量中,鍵入下面一行 進行編譯:
ml /c /coff /Cp msgbox。asm (譯者注:命令行參數大小寫是有區別的)

/c 是告訴MASM只編譯不鏈接。這主要是考慮到在鏈接前您可能還有其他工作要做。
/coff 告訴MASM產生的目標文件用 coff 格式。MASM 的 coff 格式是COFF(Common Object File
Format:通用目標文件格式) 格式的一種變體。在 UNIX 下的 COFF 格式又有不同。
/Cp 告訴 MASM 不要更改用戶定義的標識符的大小寫。若您用的是 hutch 的包含文件的話,在.model 指令下加入 “option
casemap:none” 語句,可達到同樣的效果。

當您成功的編譯了 msgbox.asm 後,編譯器會產生 msgbox.obj目標文件,目標文件和可執行文件只一步之遙,目標文件中包含了以二進制形式存在的指令和數據,比可執行文件相差的只是鏈接器加入的重定位信息。
好,我們來鏈接目標文件:

link /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm32\lib msgbox.obj

/SUBSYSTEM:WINDOWS 告訴鏈接器可執行文件的運行平臺
/LIBPATH:〈path to import library〉 告訴鏈接器引入庫的路徑。

下面我們來做一點可以看的見摸的着的,我們在程序中加入一個對話框。該函數的原型如下:

MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD 

hWnd 是父窗口的句柄。句柄代表您引用的窗口的一個地址指針。它的值對您編 Windows 程序並不重要。您只要知道它代表一個窗口。當您要對窗口做任何操作時,必須要引用該窗口的指針。
lpText 是指向您要顯示的文本的指針。指向文本串的指針事實上就是文本串的首地址。
lpCaption 是指向您要顯示的對話框的標題文本串指針。
uType 是顯示在對話框窗口上的小圖標的類型。

下面是源程序:

.386 
.model flat,stdcall 
option casemap:none 

include \masm32\include\windows.inc 
include \masm32\include\kernel32.inc 

includelib \masm32\lib\kernel32.lib 
include \masm32\include\user32.inc 

includelib \masm32\lib\user32.lib 
.data 
MsgBoxCaption  db "Iczelion Tutorial No.2",0 

MsgBoxText       db "Win32 Assembly is Great!",0 
.code 
start: 
invoke MessageBox, NULL, addr 
MsgBoxText, addr MsgBoxCaption, MB_OK 
invoke ExitProcess, NULL 
end start 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章