要用Nasm来创建Windows窗口的话必须要用到Nasm和一个辅助工具Nasmx,该工具提供了对Windows API的支持,Nasmx可以从 http://sourceforge.net/projects/nasmx/ 下载,Nasm从http://www.nasm.us 上下载,其实Nasmx已经包含了Nasm,所以Nasm可以不下载.
在编写基于Windows的窗口之前首先要了解的是Windows的消息机制, 即Windows操作系统是如何处理接收到的消息的:
比如当你按下键盘上或者鼠标的某一个键的时候, 操作系统将捕获这个按键信息, 然后放到应用程序的消息队列中(操作系统会为每个应用程序创建消息队列), 这时应用程序(消息循环 )从消息队列中取出消息, 翻译消息并将消息传递给操作系统, 由操作系统去调用窗口过程(窗口过程是一个回调函数,由操作系统调用) , 由窗口过程进行对消息进行处理.
以下代码创建一个带有按钮,文本框,静态文本和列表框的窗口:
%include "nasmx.inc"
%include "win32/user32.inc"
%include "win32/kernel32.inc"
%include "win32/windows.inc"
%include "win32/gdi32.inc"
%define IDC_BUTTON1 500
%define IDC_EDIT1 501
%define IDC_LIST1 502
entry EntryPoint
[section .bss]
hWnd: resd 1
hInstance: resd 1
hButton1: resd 1
hList1: resd 1
hEdit1: resd 1
[section .data]
szButton: db "BUTTON",0
szEdit: db "EDIT",0
szListBox: db "LISTBOX",0
szStatic: db "STATIC",0
szClass: db "WClass",0
szWndName: db "Window",0
szBtnStr: db "OK",0
szBtnText1: db "Hello...",0
szText1: db "File",0
wc:
istruc WNDCLASSEX
at WNDCLASSEX.cbSize, dd WNDCLASSEX_size
at WNDCLASSEX.style, dd CS_VREDRAW + CS_HREDRAW
at WNDCLASSEX.lpfnWndProc, dd NULL
at WNDCLASSEX.cbClsExtra, dd NULL
at WNDCLASSEX.cbWndExtra, dd NULL
at WNDCLASSEX.hInstance, dd NULL
at WNDCLASSEX.hIcon, dd NULL
at WNDCLASSEX.hCursor, dd NULL
at WNDCLASSEX.hbrBackground, dd COLOR_BTNFACE + 1
at WNDCLASSEX.lpszMenuName, dd NULL
at WNDCLASSEX.lpszClassName, dd NULL
at WNDCLASSEX.hIconSm, dd NULL
iend
message:
istruc MSG
at MSG.hwnd, dd NULL
at MSG.message, dd NULL
at MSG.wParam, dd NULL
at MSG.lParam, dd NULL
at MSG.time, dd NULL
at MSG.pt, dd NULL
iend
[section .code]
proc EntryPoint
invoke GetModuleHandleA,dword NULL
mov [hInstance],eax
invoke WinMain,dword hInstance,dword NULL,dword NULL,dword SW_SHOWNORMAL
invoke ExitProcess,dword NULL
ret
endproc
proc WinMain
hInst argd
hPrevInstance argd
lpCmdLine argd
nCmdShow argd
invoke LoadIconA,dword NULL,dword IDI_APPLICATION
mov [wc+WNDCLASSEX.hIcon],eax
mov [wc+WNDCLASSEX.hIconSm],eax
mov eax,dword szClass
mov [wc+WNDCLASSEX.lpszClassName],eax
mov eax,dword WinProc
mov [wc+WNDCLASSEX.lpfnWndProc],eax
mov eax,dword argv(hInst)
mov [wc+WNDCLASSEX.hInstance],eax
invoke LoadCursorA,dword NULL,dword IDC_ARROW
mov [wc+WNDCLASSEX.hCursor],eax
invoke GetStockObject,dword WHITE_BRUSH
mov [wc+WNDCLASSEX.hbrBackground],eax
invoke RegisterClassExA,dword wc
invoke CreateWindowExA,dword NULL,dword szClass,dword szWndName,dword WS_CAPTION|WS_SYSMENU|WS_VISIBLE,dword 300,dword 100,dword 600,dword 600,dword NULL,dword NULL,dword argv(hInst),dword NULL
mov [hWnd],eax
invoke ShowWindow,dword hWnd,dword argv(nCmdShow)
invoke UpdateWindow,dword hWnd
;消息循环
.msgloop:
invoke GetMessageA,dword message,dword NULL,dword NULL,dword NULL
cmp eax,dword 0
je .exit
invoke TranslateMessage,dword message
invoke DispatchMessageA,dword message
jmp .msgloop
.exit:
mov eax,dword [message+MSG.wParam]
ret
endproc
;窗口过程
proc WinProc
hwnd argd
uMsg argd
wParam argd
lParam argd
.wm_create:
cmp argv(uMsg),dword WM_CREATE
jnz .wm_command
;创建按钮
invoke CreateWindowExA,dword NULL,dword szButton,dword szBtnStr,dword BS_DEFPUSHBUTTON|WS_CHILD|WS_VISIBLE,dword 250,dword 10,dword 100,dword 20,dword argv(hwnd),dword IDC_BUTTON1,dword hInstance,dword NULL
mov [hButton1],eax
;创建文本框
invoke CreateWindowExA,dword NULL,dword szEdit,dword NULL,dword ES_LEFT|WS_CHILD|WS_BORDER|WS_VISIBLE,dword 10,dword 10,dword 200,dword 20,dword argv(hwnd),dword IDC_EDIT1,dword hInstance,dword NULL
mov [hEdit1],eax
;创建列表框
invoke CreateWindowExA,dword NULL,dword szListBox,dword NULL,dword WS_CHILD|WS_BORDER|WS_VISIBLE,dword 300,dword 50,dword 200,dword 200,dword argv(hwnd),dword IDC_LIST1,dword hInstance,dword NULL
mov [hList1],eax
;创建静态文本
invoke CreateWindowExA,dword NULL,dword szStatic,dword szText1,dword SS_CENTER|WS_CHILD|WS_VISIBLE,dword 10,dword 50,dword 100,dword 20,dword argv(hwnd),dword 600,dword hInstance,dword NULL
jmp .default
;处理单击按钮消息
.wm_command:
cmp argv(uMsg),dword WM_COMMAND
jnz .wm_destroy
mov edx,dword argv(lParam) ;等价于(HWND)lParam==hButton
cmp edx,dword [hButton1]
jnz .wm_destroy
mov ebx,dword wParam
shr ebx,16
and ebx,0000ffffh
cmp ebx,dword BN_CLICKED ;等价于HIWORD(wParam)==BN_CLICKED
jnz .wm_destroy
invoke MessageBoxA,dword NULL,dword szBtnText1,dword szBtnText1,dword MB_OK
jmp .default
.wm_destroy:
cmp argv(uMsg),dword WM_DESTROY
jnz .default
invoke PostQuitMessage,dword 0
.default:
invoke DefWindowProcA,dword argv(hwnd),dword argv(uMsg),dword argv(wParam),dword argv(lParam)
.exit:
ret
endproc
编译方法:
到X:/Nasm安装目录 /inc/下将win32文件夹和nasmx.inc拷贝到nasm的目录下(即nasm.exe,ndisasm.exe所在目录),将X:/Nasm安装目录 /bin/下的GoLink.exe也拷贝到nasm的目录下,GoLink.exe是链接器. 如果nasm的路径已配置到环境变量Path中,假定上述代码的文件名为window.asm,路径为X:/window.asm,则在cmd中运行命令:
nasm -f win32 window.asm @rem 该命令产生一个目标对象window.obj
golink /entry _main window.obj user32.dll kernel32.dll gdi32.dll @rem 该命令产生一个win32程序 /entry指明了程序的入口点,不过这里的入口点为_main而不是WinMain是因为编译程序对符号进行了处理,将WinMain的导出名编程了_main,这个和dll类似.加上user32.dll是因为使用了MessageBox函数,后面kernel32.dll和gdi32.dll一样,有些函数后面要加上A是因为那些函数是用ANSI编码来实现的,还有一个W的版本,W的版本是基于Unicode实现的,可以在MSDN中查看所用到的函数是否有ANSI和Unicode版本.(PS:以上代码在nasm2.09能编译成功,nasm2.10下有所区别:istruc处为NASMX_ISTRUC,而且声明的proc在使用前必须进行函数原型声明或者放到调用过程之前,argv必须包含在[]中,总之觉得2.10的变化挺多的.)
代码解释 : %define处定义了按钮,文本框和列表框的ID. entry处表示程序入口点为名为EntryPoint的过程,该过程调用过程WinMain,其实此处可以直接写WinMain也一样. 下面来看[section],Nasm中可以用section来指明一个段(数据区是何种类型的段(数据区),.bss是为初始化数据区,里面的数据都是没有经过初始化的;.data为初始化数据区;.code为代码段,写成.text作用和.code一样..data段中的istruc定义了一个 WNDCLASSEX的实例, WNDCLASSEX已在Nasmx自带的windows.inc中用struc声明,所以我们只要创建一个实例就可以(注意: WNDCLASSEX中 每个属性的顺序必须和用struc声明的 WNDCLASSEX 中的顺序一致,不然编译出错...),紧接着的MSG和 WNDCLASSEX一样. 下面进入代码段:过程WinMain首先创建了窗口,窗口的创建过程分为四个步骤:定义窗口类->创建窗口->注册窗口->显示窗口,最后其实还有一步就是更新窗口,这个过程和C++创建窗口一样,这里不再敖述. 接下来看.msgloop,这就是消息循环,负责总应用程序的消息队列中取消息并转发给操作系统,由操作系统去调用窗口过程进行处理. 在这里WinProc为窗口过程,带有四个参数,argd前面的为参数名,而argd本身说明了参数类型为dword类型,要在过程中使用参数,必须用argv(param),param为argd之前的参数名.
其实我比较喜欢Nasm,感觉比较干净, 语法也比较简洁,而且能编译出纯机器码,尤其适合写操作系统代码 ,相比之下MASM32比较臃肿,不过Nasmx对Windows API的支持没有MASM32全面,比如 和ListView相关函数没有提供支持,后来看了一下nasmx中的inc文件,自己加了import也没用,不足之处还是有的. 呵呵,闲着无聊,写着玩的...