;运行效果
;RadASM环境,win32汇编入门教程之六
;在上一个教程里面,我们学习了如何定义数据,那么在这一章节里面,我们来学习一下,再说明怎么把这些数据显示出来
;下列就是显示出这些数据的示例程序,可以直接复制下来,然后编译运行看看效果
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc ;增加的内容
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib ;增加的内容
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.DATA
ClassName db "SimpleWinClass",0
AppName db "窗口程序的模版",0
sF db "%d",0
hA dd 168
hD db "世界,你好!",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hB dd ?
.const;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
hC equ 16800
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.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,300, 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
LOCAL @stPs:PAINTSTRUCT
LOCAL @szBuffer[256]:byte
LOCAL @hDc
.if uMsg == WM_CREATE
.elseif uMsg == WM_PAINT
invoke BeginPaint,hWnd,addr @stPs
mov @hDc,eax
;下面显示168
invoke wsprintf,addr @szBuffer,addr sF,hA
invoke lstrlen,addr @szBuffer
invoke TextOut,@hDc,100,30,addr @szBuffer,eax ;显示函数
;下面显示1680
mov hB,1680
invoke wsprintf,addr @szBuffer,addr sF,hB
invoke lstrlen,addr @szBuffer
invoke TextOut,@hDc,100,60,addr @szBuffer,eax
;下面显示16800
invoke wsprintf,addr @szBuffer,addr sF,hC
invoke lstrlen,addr @szBuffer
invoke TextOut,@hDc,100,90,addr @szBuffer,eax
;下面显示"世界,你好!"
invoke lstrlen,addr hD
invoke TextOut,@hDc,100,120,addr hD,eax
invoke EndPaint,hWnd,addr @stPs
.elseif uMsg == WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start
;在这个示例里面,我们显示4个东西,分别是数字168、1680、16800和字符串"世界,你好!"
;首先,需要增加1个头文件和库文件,就是上面添加的
include gdi32.inc
includelib gdi32.lib
;这些文件里面定义了一些基本的数据结构之类的,就是微软公司先把一些最基础的定义好了,后面就可以直接调用一些函数。
;经常编译不过的时候,提示没有定义某个函数之类的,往往就是缺少了这些头文件。gdi32文件是关于一些绘画的函数。
;先看这句: hA dd 168 ,这就是定义一个叫hA的4字节数字168,hA是名称,dd是dword的简称,即4字节,就是32位的意思,因为每个字节是8位
;可不可以定义成hA db 168 呢,也可以,这样定义的话就是定义1个单字节的值。但很少这样做,因为后面调用的函数参数基本是32位的,如果定义成1个字节的话
;后面要转成32位的值,还要保证高位字节都是零,才能当做参数调用函数,所以默认的都是定义成dd类型。
;上面有个单词 .DATA,这个指的是数据区,设定初值的数据放在这个区里面。
;看第2个定义: hB dd ?
;它放在 .DATA? 这里,这是未初始化的数据区,就是先定义,不赋值先。
;看第3个定义: hC equ 16800
;它放在 .const ,这个是固定数据区,就是放在这儿的是不能改变的数据。这里用了一个命令equ,它是等同的意思。就是说hC就是16800,但不能说16800就是hC,因为在程序中或程序运行中,可能也会产生16800的值。
;看第4个定义: hD db "世界,你好!",0
;它放在 .DATA,即放在数据区,是设定了初值的,在程序中也可以改变
;它是定义1个字符串,db是dword byte的缩写,就是4字节作为地址值。我们需要这样理解,hD并不等于这个字符串,而是这个字符串的地址,也叫指针值。
;后面研究一下,在窗口函数WndProc中有哪些内容
;先定义1个名叫@stPs的PAINTSTRUCT结构变量,这个名字@stPs随便取的,一般约定成俗的局部变量前面加@而已。
;那什么叫局部变量,局部变量就是在函数运行期内,它是有效的,离开函数时就被电脑收回了。
;那什么时候离开?当程序运行时,走到最后一个命令ret,就是返回上一层,即调用它的那一层,就是离开了这个函数了。
;像@stPs这个变量,它定义在WndProc函数内,当WndProc函数运行完,返回上一层后,它就不能再被其它的函数调用了,不能再使用了。
;PAINTSTRUCT这个结构是什么东西,看它的字面意思就知道,它是画的结构
;简单地说,就是重新画这个窗口时,根据这个结构内的设定值重新画它。
;为什么要重画它?
;很多情况要重画,比如里面要显示的内容发生变化,或最小化状态恢复原状,或被其它窗口挡住又恢复了等等。
;具体的我们以后再研究,这里可以先定义,当然,只是定义,结构内的成员还没有实际值的。
;@szBuffer是准备中转用的字符串数组的变量。因为前面定义的像hA,hB,hC是数值,不是字符串,后面在显示前要转化为字符串才行。
;我们要知道,数值是不能直接显示出来的,就算你看到窗口上写着123之类的这些东西,它并不是数值,而是字符串。
;就像你拍了一张文档,看起来是文字,但其实它是相片。
;@hDc是为后面储值用的变量,也可以这样写 @hDc:dword,只是后面的dword省略了,后面没有像:dword这样或类似的类型说明的,都是默认为dword型,即4字节型。
;看.elseif uMsg == WM_PAINT 这句,这句的意思是,当要重绘时,要干什么
;就是说,当重绘消息来时,要怎么画这个窗口
;比如电脑刚把程序运行起来,需要重绘
;比如最小化之后又恢复原状,怎么重新画出来,需要重绘等等
;电脑要怎么重绘时,就在WM_PAINT下面找这些代码
;invoke BeginPaint,hWnd,addr @stPs 这句的意思是,得到窗口原来的设定,并把它保存到@stPs里面去。
;就是把窗口有多长,有多宽,要不要在重绘之前,把原来没被挡住的部分保存原样,或者要不要原来显示的擦除掉等等。
;这些具体的我们不用管它,让电脑自已处理。我们要做的就是把我们需要增加上去的内容,告诉电脑,一起画出来。
;这些内容,就是我们想显示的4个东西了。
;mov @hDc,eax 就是把得到的设定保存起来,可以理解为把这个程序窗口保存下来。后面要显示什么的,告诉电脑,把要显示的东西显示到这个程序窗口上去,而不是其它的程序窗口。
;invoke wsprintf,addr @szBuffer,addr sF,hA 这句的意思是,因为hA是个数字,不能直接显示,要先通过格式化函数wsprintf把hA转化为字符串,并保存到@szBuffer里面去。
;invoke lstrlen,addr @szBuffer 这句的意思是,得到@szBuffer这个字符串的长度,后面紧跟着的eax就是这个函数的返回值,即字符串的长度
;invoke TextOut,@hDc,100,30,addr @szBuffer,eax 这句的意思是在坐标为100.30的地方把字符串@szBuffer显示出来,坐标从左上角开始算。
;显示1680时,因为前面是未初始化的,就是没有给定值的,所以这里要赋值给它。mov hB,1680
;显示16800时,前面也相当于初始化了,所以同样操作进行显示。
;而显示"世界,你好!"时,因为它是已经初始化了的,且定义的是字符串,所以可以直接使用函数TextOut把它显示在窗口上。
;invoke EndPaint,hWnd,addr @stPs 这一句的意思是,在指定窗口中标记绘制的结束
;BeginPaint与EndPaint是配对出现在,在它们的之间是重绘的内容。
;以上是显示数字或字符串的示例,当然,仅仅是显示了内容。
;看起来还是不太好看,还有一些东西可以改变,比如字体、字体大小、前景色或文本色等。这些我们在下一个教程里再学习。