友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
windows环境下32位汇编语言程序设计-第3部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
call MessageBox
在源程序编译链接成可执行文件后,call MessageBox语句中的MessageBox会被换成一个地址,指向可执行文件中的导入表,导入表中指向MessageBox函数的实际地址会在程序装入内存的时候,根据User32。dll在内存中的位置由Windows系统动态填入。
1。 使用invoke语句
API是可以调用了,另一个烦人的问题又出现了,Win32的API动辄就是十几个参数,整个源程序一眼看上去基本上都是把参数压入堆栈的push指令,参数的个数和顺序很容易搞错,由此引起的莫名其妙的错误源源不断,源程序的可读性看上去也很差。如果写的时候少写了一句push指令,程序在编译和链接的时候都不会报错,但在执行的时候必定会崩溃,原因是堆栈对不齐了。
有没有解决的办法呢?最好是像C语言一样,能在同一句中打入所有的参数,并在参数使用错误的时候能够提示。
好消息又来了,Microsoft终于做了一件好事,在MASM中提供了一个伪指令实现了这个功能,那就是invoke伪指令,它的格式是:
invoke 函数名',参数1'',参数2'……
对MessageBox的调用在MASM中可以写成:
invoke MessageBox;NULL;offset szText;offset szCaption;MB_OK
注意,invoke并不是80386处理器的指令,而是一个MASM编译器的伪指令,在编译的时候它把上面的指令展开成我们需要的4个push指令和1个call指令,同时,进行参数数量的检查工作,如果带的参数数量和声明时的数量不符,编译器会报错:
error A2137: too few arguments to INVOKE
编译时看到这样的错误报告,首先要检查的是有没有少写了一个参数。对于不带参数的API调用,invoke伪指令的参数检查功能可有可无,所以既可以用call API_Name这样的语法也可以用invoke API_Name这样的语法。
2。 API函数的返回值
有的API函数有返回值,如MessageBox定义的返回值是int类型的数,返回值的类型对汇编程序来说也只有dword一种类型,它永远放在eax中。如果要返回的内容不是一个eax所能容纳的,Win32 API采用的方法一般是返回一个指针,或者在调用参数中提供一个缓冲区地址,干脆把数据直接返回到缓冲区中去。
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第3章 使用MASM
3。2 调用API(2)
3。 函数的声明
在调用API函数的时候,函数原型也必须预先声明,否则,编译器会不认这个函数。invoke伪指令也无法检查参数个数。声明函数的格式是:
函数名 proto '距离' '语言' '参数1':数据类型,'参数2':数据类型,……
句中的proto是函数声明的伪指令,距离可以是NEAR,FAR,NEAR16,NEAR32,FAR16或FAR32,Win32中只有一个平坦的段,无所谓距离,所以在定义时是忽略的;语言类型就是 。model那些类型,如果忽略,则使用 。model定义的默认值。
后面就是参数的列表了,对Win32汇编来说只存在dword类型的参数,所以所有参数的数据类型永远是dword,另外对于编译器来说,它只关心参数的数量,参数的名称在这里是“无用”的,仅是为了可读性而设置的,可以省略掉,所以下面两句消息框函数的定义实际上是一样的:
MessageBox Proto hWnd:dword;lpText:dword;lpCaption:dword;uType:dword
MessageBox Proto :dword;:dword;:dword;:dword
在Win32环境中,和字符串相关的API共有两类,分别对应两个字符集:一类是处理 ANSI 字符集的,另一类是处理 Unicode 字符集的。前一类函数名字的尾部带一个“A”字符,处理Unicode的则带一个“W”字符。我们比较熟悉的ANSI字符串是以 NULL 结尾的一串字符数组,每一个ANSI字符占一个字节宽。对于欧洲语言体系,ANSI 字符集已足够了,但对于有成千上万个不同字符的几种东方语言体系来说,Unicode 字符集更有用。每一个Unicode字符占两个字节的宽度,这样一来就可以在一个字符串中使用65 336个不同的字符了。
MessageBox和显示字符串有关,同样它有两个版本,严格地说,系统中有两个定义:
MessageBoxA Proto hWnd:dword;lpText:dword;lpCaption:dword;uType:dword
MessageBoxW Proto hWnd:dword;lpText:dword;lpCaption:dword;uType:dword
虽然《Microsoft Win32 Programmer's Reference》中只有一个MessageBox定义,但User32。dll中确确实实没有MessageBox,而只有MessageBoxA和MessageBoxW,那么为什么还是可以使用MessageBox呢?实际上在程序的头文件user32。inc中有一句:
MessageBox equ
它把MessageBox偷梁换柱变成了MessageBoxA。在源程序中继续沿用MessageBox是 为了程序的可读性以及保持和手册的一致性,但对于编译器来说,实际是在使用MessageBoxA。
由于并不是每个Win32系统都支持W系列的API,在Windows 9x系列中,对Unicode是不支持的,很多的API只有ANSI版本,只有Windows NT系列才对Unicode完全支持。为了编写在几个平台中通用的程序,一般应用程序都使用ANSI版本的API函数集。
为了使程序更有移植性,在源程序中一般不直接指明使用Unicode还是ANSI版本,而是使用宏汇编中的条件汇编功能来统一替换,如在源程序中使用Messagebox,但在头文件中定义:
if UNICODE
MessageBox equ
else
MessageBox equ
endif
所有涉及版本问题的API都可以按此方法定义,然后在源程序的头部指定UNICODE=1或UNICODE=0,重新编译后就能产生不同的版本。
4.include语句
对于所有要用到的API函数,在程序的开始部分都必须预先声明,但这一个步骤显然是比较麻烦的,为了简化操作,可以采用各种语言通用的解决办法,就是把所有的声明预先放在一个文件中,在用到的时候再用include语句包含进来。现在回到Win32 Hello World程序,这个程序用到了两个API函数:MessageBox和ExitProcess,它们分别在User32。dll和Kernel32。dll中,在MASM32工具包中已经包括了所有DLL的API函数声明列表,每个DLL对应文件,在源程序中只要使用include语句包含进来就可以了:
include user32。inc
include kernel32。inc
当用到其他的API函数时,只需相应增加对应的include语句。
include语句还用来在源程序中包含别的文件,当多个源程序用到相同的函数定义、常量定义,甚至源代码时,可以把相同的部分写成一个文件,然后在不同的源程序中用include语句包含进来。
编译器对include语句的处理仅是简单地把这一行用指定的文件内容替换掉而已。
include语句的语法是:
include 文件名
或 include
当遇到要包括的文件名和MASM的关键字同名等可能会引起编译器混淆的情况时,可以用将文件名括起来。
5。 includelib语句
在DOS汇编中,使用中断调用系统功能是不必声明的,处理器自己知道到中断向量表中去取中断地址。在Win32汇编中使用API函数,程序必须知道调用的API函数存在于哪个DLL中,否则,操作系统必须搜索系统中存在的所有DLL,并且无法处理不同DLL中的同名函数,这显然是不现实的,所以,必须有个文件包括DLL库正确的定位信息,这个任务是由导入库来实现的。
在使用外部函数的时候,DOS下有函数库的概念,那时的函数库实际上是静态库,静态库是一组已经编写好的代码模块,在程序中可以自由引用,在源程序编译成目标文件,最后要链接成可执行文件的时候,由link程序从库中找出相应的函数代码,一起链接到最后的可执行文件中。DOS下C语言的函数库就是典型的静态库。库的出现为程序员节省了大量的开发时间,缺点就是每个可执行文件中都包括了要用到的相同函数的代码,占用了大量的磁盘空间,在执行的时候,这些代码同样重复占用了宝贵的内存。
Win32环境中,程序链接的时候仍然要使用函数库来定位函数信息,只不过由于函数代码放在DLL文件中,库文件中只留有函数的定位信息和参数数目等简单信息,这种库文件叫做导入库,一个DLL文件对应一个导入库,如User32。dll文件用于编程的导入库是User32。lib,MASM32工具包中包含了所有DLL的导入库。
为了告诉链接程序使用哪个导入库,使用的语句是:
includelib 库文件名
或 includelib
和include的用法一样,在要包括让编译器混淆的文件名时加方括号。Win32 Hello world程序用到的两个API函数MessageBox和ExitProcess分别在User32。dll和Kernel32。dll中,那么在源程序使用的相应语句为:
includelib user32。lib
includelib kernel32。lib
和include语句的处理不同,includelib不会把 。lib文件插入到源程序中,它只是告诉链接器在链接的时候到指定的库文件中去找而已。
3。2。3 API参数中的等值定义
再回过头来看显示消息框的语句:
invoke MessageBox;NULL;offset szText;offset szCaption;MB_OK
在uType这个参数中使用了MB_OK,这个MB_OK是什么意思呢,先来看《Microsoft Win32 Programmer's Reference》中的说明:
uType —— 定义对话框的类型,这个参数可以是以下标志的合集:
要定义消息框上显示按钮,用下面的某一个标志:
MB_ABORTRETRYIGNORE —— 消息框有三个按钮:“终止”,“重试”和“忽略”
MB_HELP —— 消息框上显示一个“帮助”按钮,按下后发送WM_HELP消息
MB_OK —— 消息框上显示一个“确定”按钮,这是默认值
MB_OKCANCEL —— 消息框上显示两个按钮:“确定”和“取消”
MB_RETRYCANCEL —— 消息框上显示两个按钮:“重试”和“忽略”
MB_YESNO —— 消息框上显示两个按钮:“是”和“否”
MB_YESNOCANCEL —— 消息框上显示三个按钮:“是”、“否”和“取消”
要在消息框中显示图标,用下面的某一个标志:
MB_ICONWARNING —— 显示惊叹号图标
MB_ICONINFORMATION —— 显示消息图标
MB_ICONASTERISK —— 显示危险图标
MB_ICONQUESTION —— 显示问号图标
MB_ICONSTOP —— 显示停止图标
……
这些是uType参数说明中的一小半,可以看出,参数中可以用的值有很多种,让我们换一个值试试看,把语句改为:
invoke MessageBox;NULL;offset szText;offset szCaption; MB_ICONWARNING or MB_YESNO
再编译执行看,屏幕上出现了一个不一样的消息框,如图3。3所示。
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第3章 使用MASM
3。2 调用API(3)
和参数说明中的一样!消息框中出现了一个惊叹号图标,按钮也变成了“是”和“否”两个按钮!MB_ICONWARNING和MB_YESNO等参数究竟是什么意思呢,MASM中显然没有这样的预定义,让我们先来找Visual C++的头文件,在WinUser。h中可以找到下面一段:
/*
* MessageBox() Flags
*/
#define MB_OK Ox00000000L
#define MB_OKCANCEL Ox00000001L
#define MB_ABORTRETRYIGNORE Ox00000002L
#define MB_YESNOCANCEL Ox00000003L
#define MB_YESNO Ox00000004L
#define MB_RETRYCANCEL Ox00000005L
#define MB_ICONHAND Ox00000010L
#define MB_ICONQUESTION Ox00000020L
#define MB_ICONEXCLAMATION Ox00000030L
#define MB_ICONASTERISK Ox00000040L
#if(WINVER 》= Ox0400)
#define MB_USERICON Ox00000080L
#define MB_ICONWARNING MB_ICONEXCLAMATION
#define MB_ICONERROR MB_ICONHAND
#endif /* WINVER 》= 0x0400 */
#define MB_ICONINFORMATION MB_ICONASTERISK
#define MB_ICONSTOP MB_ICONHAND
……
显然,MB_YESNO就是4,MB_ICONWARNING就是30h,默认的MB_OK就是0,Win32 API的参数使用这样的定义方法显然是为了免除程序员死记数值定义的麻烦。在编写Win32汇编程序的时候,MASM32工具包中的Windows。inc也包括了所有这些参数的定义,只要在程序的开头包含这个定义文件:
include windows。inc
就可以方便地完全按照API手册来使用Win32函数。
打开masm32include目录下的Windows。inc查看一下,可以发现整个文件总共有两万六千多行,包括了几乎所有的Win32 API参数中的常量和数据结构定义。正是有了这个文件中详尽的定义,Win32ASM才得以流行起来,试想一下,哪个程序员愿意每使用一个API语句,就到函数手册中去看参数,然后到Microsoft发布的Visual C++的头文件中
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!