RadASM环境,win32汇编入门教程之二
;win32汇编环境,RadAsm入门教程之二
;前面我们已经学了教程一,生成了第一个软件。那么让我们继续我们的学习旅程。本教程讲解一下基本窗口模版的原理。让我们打开RadASM后,双击右侧的ABC.Asm文件,一点点研究。
;首先,我们写了很多代码,但需要在边上写个备注什么的,这个时候用分号;来标明,是英文模式下的分号,不是中文输入法下的。你写的时候看到会变黄就可以了,不会变黄的不行。分号后面的不会被编译
;然后,我们大概的把教程一里面的内容理解一下。
;现在,我们把教程一的代码复制过来,再把解释写在后面。我们看着解释来理解。
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.DATA
ClassName db "SimpleWinClass",0
AppName db "窗口程序的模版",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.CODE
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess, eax
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInstance
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,100,100,400,600, NULL,NULL,hInst, NULL
mov hwnd,eax
invoke ShowWindow, hwnd,CmdShow
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:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start
.386 ;这句的意思是说兼容到最早的386时代的指令集。就是说386时代的系统也支持它运行。你换成486或586也可以。
.model flat,stdcall ;flat指扁平模式,一种内存展开方式,知道就行,不用理解太深。stdcall指在函数中,参数由右至左压入堆栈。
option casemap:none ;casemap:none指区分大小写。
include windows.inc ;以下的是头文件和库文件。里面包含着一些函数或数据类型的的基本定义。它一般是微软公司提供的,帮我们省很多事儿。自已写头文件也可以。
include user32.inc ;include是包含头文件的指令。includelib是包含库文件的指令。inc的是头文件,lib的是库文件。
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD ;这是函数声明,只是声明,没有实际内容,就是说一声,有这个函数,函数可能在后面的某个地方。如果没有声明,在使用函数的时候,必须在前面有明确的内容。有时候,写着写着就乱了,忘了次序。所以一般先声明一下,就不用管在哪个前面,哪个后面了。
;WinMain是函数的名字,这个其实不重要。它只是一个代号,你换其它名字也可以。但后和它一样名字的要同时改。我们一般不动它。
;proto的意思是这个函数的内容只能自已用,不能被别的程序访问。:DWORD有4个,是这个函数有4个参数,都是DWORD型的,即是4字节型的。每个字节8位,就是32位了。常说的32位系统就是这样来的,相当于跑道上并列可以站32位选手。
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.DATA ;下面是数据区,数据分成好多种。有些可以改变,有些不可以改变。有些要初始化,就是一开始就定下来内容。有些不用初始化,可以在运行时随时改变。
ClassName db "SimpleWinClass",0 ;像这种就是初始化的内容。db是dword byte的缩写,就是字符串数组。后面有?的就是没有初始化的。ClassName定义的是类名,就是给这个窗口起个祖宗名,由它可以生成儿子类窗口,儿子类可以生成孙子类窗口。这个暂时还用不上,暂时有个祖宗名就行了。名字自已取。
AppName db "窗口程序的模版",0 ;这段话的意思是设置程序的标题栏。
.DATA?
hInstance HINSTANCE ? ;模块句柄。就是运行时,在整个电脑中,这个程序的的数字代码。相当于整栋楼中,你的门牌号。HINSTANCE是模块,是dd的别一种说法,其实也是dd类,即dword型,4字节。你把HINSTANCE改为dd也一样的。
CommandLine LPSTR ? ;命令行指针。LPSTR是指针的意思,和上面HINSTANCE一样,故意取个新名,好理解。其实也是dword型,即dd型。这个是为了在低版本的系统上运行不了时,显示出来的字符。显示出来的意思是,你的系统太落后了,运行不了。
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.CODE ;上面的是数据区,从这儿开始是代码区了。
start: ;代码段开始
invoke GetModuleHandle, NULL ;得到自已程序的门牌号。不用管门牌号哪来的,是系统自动生成或调配的。
mov hInstance,eax ;把门牌号给一个叫hInstance的,后面需要时好调用它
invoke GetCommandLine ;得到命令行内容。
mov CommandLine,eax ;把命令行的地址给CommandLine,后面用时好调用它。命令行的内容由上面的GetCommandLine函数由电脑系统提供。
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT ;调用WinMain函数。这个函数的实体在后面,因为前面声明了,所以实体可以放在后面。把4个参数的值传给它,再运行到函数里面。
invoke ExitProcess, eax ;退出进程。就是告诉电脑,我要离开了,电脑会把占用的资源收回之类的工作。
;以上就是主要程序,下面的就是主要程序包含的又要自已写的内容。
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD ;这里别看这么长,其实就是4个参数。proc是指函数过程,它和后面的endp成一对,两者之间的就是此函数的具体内容。这4个参数的值在前面调用它时已经得到了。比如CmdShow的值就是SW_SHOWDEFAULT,默认显示模式的意思。那SW_SHOWDEFAULT是什么?这个在头文件里预定义了。像这些大写字母类的代号,很多都是预定义值,可以在头文件里面搜出来看看,具体值是多少。
LOCAL wc:WNDCLASSEX ;定义一个叫wc的WNDCLASSEX结构变量。什么叫结构,就是要一起用的东西打包在一起,起一个共用的名字。比如家是结构,爸爸是成员,妈妈是成员。然后赋值给爸爸叫张三。
LOCAL msg:MSG ;定义一个叫msg的MSG结构变量。这个是消息结构,很重要,是核心理念,讲起来就长了,后面再慢慢讲解。
LOCAL hwnd:HWND ;定义一个叫hwnd的主窗口句柄。这个代表着显示出来的主窗口。
mov wc.cbSize,SIZEOF WNDCLASSEX ;让wc结构变量知道有多少字节。相当于家这个结构,要多大的面积。
mov wc.style, CS_HREDRAW or CS_VREDRAW ;让wc结构设成什么风格的,比如欧式家居。
mov wc.lpfnWndProc, OFFSET WndProc ;把窗口函数的地址给wc结构。相当于告诉派出所,你家的地址在哪
mov wc.cbClsExtra,NULL ;备用的空间,设为空,就是0的意思。可以理解为家这个结构,要留多少空间给未来的孩子。
mov wc.cbWndExtra,NULL ;备用空间,为将来留用的,设为空。
push hInstance ;把hInstance压入堆栈。堆栈相当于一个本程序外的桶,电脑把hInstance压入桶。堆栈就是桶,而桶是后面进的先出来,不是先进去先出来。
pop wc.hInstance ;把hInstance弹出堆栈,弹到wc的hInstance里去。这里为什么不像上面那样赋值,因为wc的成员hInstance和上面定义的hInstance同名,所以用这个取巧的方式。其实像上面那样赋值也可以的。
mov wc.hbrBackground,COLOR_WINDOW ;设置这个程序的背景色
mov wc.lpszMenuName,NULL ;设置它的菜单,如果有的话,这里没有,就设为NULL,即为0的意思
mov wc.lpszClassName,OFFSET ClassName ;把类名地址给wc,就是前面说的祖宗名在内存里的地址给wc,wc才能找到这个名字 SimpleWinClass
invoke LoadIcon,NULL,IDI_APPLICATION ;加载图标进来
mov wc.hIcon,eax ;把图标句柄给wc的成员hIcon,显示在程序标题栏的前面。
mov wc.hIconSm,eax ;把上面图标压小了给wc的成员hIconSm,相当的一个图标显示在2个地方,一个是程序标题栏前面的图标,一个是最小化时显示的图标。这个是最小化时的图标。
invoke LoadCursor,NULL,IDC_ARROW ;加载光标。就是鼠标的样子。
mov wc.hCursor,eax ;把光标句柄给wc的成员hCursor
invoke RegisterClassEx, addr wc ;注册这个类。把addr wc告诉电脑,就是把wc的地址告诉电脑。即把祖宗名告诉电脑,并让电脑根据上面的参数作好准备。
invoke CreateWindowEx,NULL, ADDR ClassName,ADDR AppName,WS_OVERLAPPEDWINDOW,100,100,400,600, NULL,NULL,hInst, NULL ;调用系统内的函数CreateWindowEx生成窗口,并把参数传进去。参数是属于哪个新祖宗,标题栏写什么,显示模式怎么样,坐标是哪儿开始,有多长,有多宽等。
mov hwnd,eax ;上面的函数返回值在eax里,把它的值给hwnd,后面调用时才能知道主窗口在哪儿。
invoke ShowWindow, hwnd,CmdShow ;显示主窗口
invoke UpdateWindow, hwnd ;更新窗口的数据。比如移动了或被其它窗口挡住又重新恢复,里面的数据需要更新一下。
.while TRUE ;开始消息循环一直到电脑收到该程序结束的消息。即后面的PostQuitMessage函数来时,告诉电脑结束了。
invoke GetMessage, ADDR msg,NULL,0,0 ;消息循环机制相当于电脑是中间人。你点一下鼠标,就产生消息,电脑根据你点鼠标这个消息,把你点的位置,怎么点的,比如双击等信息打包,发给这个程序。
.break .if (!eax) ;如果收到WM_QUIT消息,即退出消息,则跳出循环
invoke TranslateMessage, ADDR msg ;假如设定某个按键代表不同的功能,比如设定某些键当作快捷键,例如设定CTRL+E为退出,则当按这两键时,转化为WM_QUIT消息给电脑
invoke DispatchMessage, ADDR msg ;把你的消息转发给下面的WndProc窗口过程,再等它处理完后返回
.endw
mov eax,msg.wParam ;把消息码给eax寄存器,当作返回值
ret
WinMain endp ;上面的内容基本上就是模版,在窗口程序中基本上一样。大概理解就行,主要我们还是要添加下面窗口过程的内容。
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM ;这个叫窗口过程函数。每个窗口或控件都有这样的函数。有些控件它自已设好了默认的处理内容,大部分功能都有了,所以不用显示出来。当然也可以显示出来,那就要自已重载它,就是自已再写一遍。比如,按钮控件一般直接用,里面已经有基本功能。但你想画个奇怪形状的按钮,那只能重载它,重绘它,这将是一个复杂的过程。
.if uMsg==WM_DESTROY ;窗口函数将处理许多的消息,比如初始化,比如移动了窗口,比如重新画出来等等。这个WM_DESTROY是退出消息,意思是当你点右上角的X时,就产生WM_DESTROY消息,下面就调用PostQuitMessage函数,插入一条退出消息给消息循环队列。
invoke PostQuitMessage,NULL ;调用发送退出消息函数
.else ;如果这个窗口没有任何消息,则默认处理。就是不管它,把CPU时间给别的程序。
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax ;把eax寄存器清零,即eax为零,当作返回值。函数都需要返回值,除非你想让它进到里面不再出来。如果这样,意味着卡死。
ret ;返回命令
WndProc endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start ;告诉编译程序,代码段部分到这里结束了。