使用MASM04
让编程改变世界
Change the world by program
调用API函数
习惯工作于DOS汇编的程序员同志都有一个愿望:如果说,能够以功能名称作为子程序名直接调用,他们愿意以生命中的十年寿命作为交换…… 随着Win32的到来,他们的愿望实现了!这就是API函数,它事实上就是以一种新的方法代替了DOS下的中断。 与DOS中断相比,Win32的系统功能模块放在Windows的动态链接库(DLL)中。 DLL是一种Windows的可执行文件,采用的是和我们熟悉的.exe文件同样的PE(PortableExecutable)约定格式。 [caption id="attachment_752" align="aligncenter" width="593"] DLL文件的原理[/caption]关于DLL
DLL事实上只是一个大大的集装箱,装着各种系统的API函数。 应用程序在使用的时候由Windows自动载入DLL程序并调用相应的函数。 实际上,Win32的基础就是由DLL组成的。Win32API的核心由3个DLL提供,它们是:
– KERNEL32.DLL——系统服务功能。包括内存管理、任务管理和动态链接等。 – GDI32.DLL——图形设备接口,处理图形绘制。 – USER32.DLL——用户接口服务。建立窗口和传送消息等。 当然,Win32API还包括其他很多函数,这些也是由DLL提供的,不同的DLL提供了不同的系统功能。 如使用TCP/IP协议进行网络通信的DLL是Wsock32.dll,它所提供的API称为SocketAPI; 专用于电话服务方面的API称为TAPI(TelephonyAPI),包含在Tapi32.dll中。 所有的这些DLL提供的函数组成了现在使用的Win32编程环境。 我们也经常自己打包自己的“集装箱”!API函数的参数
在DOS下,我们演示过无数次,通过中断来调用系统“函数”,其中的“参数”是通过放在寄存器(ah)中。 Win32API是用堆栈来传递参数的,调用者把参数一个个压入堆栈,DLL中的函数程序再从堆栈中取出参数处理,并在返回之前将堆栈中已经无用的参数丢弃。 在Microsoft发布的《MicrosoftWin32Programmer’sReference》中定义了常用API的参数和函数声明。 [codesyntax lang="c"]intMessageBox( HWNDhWnd, //handletoownerwindow LPCTSTRlpText, //textinmessagebox LPCTSTRlpCaption, //messageboxtitle UINTuType //messageboxstyle );//注意,上边是用C语言表示![/codesyntax] [caption id="attachment_753" align="aligncenter" width="570"] API函数的参数[/caption] 上述函数声明说明了MessageBox有4个参数,这些数据类型看起来很复杂。 但有一点是很重要的,对于汇编语言来说,Win32环境中的参数实际上只有一种类型,那就是一个32位的整数,所以这些HWND,LPCTSTR和UINT实际上就是汇编中的dword(doubleword,双字型,4个字节,两个字,32位)之所以定义为不同的模样,主要是用来说明了用途。 由于Windows是用C写成的,世界上的程序员好像也是用C语言的最多,所以Windows所有编程资料发布的格式也是C格式。 上面的声明用汇编的格式来表达就是: [codesyntax lang="asm"]
MessageBoxProtohWnd:dword,lpText:dword,lpCaption:dword,uType:dword[/codesyntax] 在汇编中调用MessageBox函数的方法是: [codesyntax lang="asm"]
pushuType pushlpCaption pushlpText pushhWnd callMessageBox[/codesyntax] 在源程序编译链接成可执行文件后,callMessageBox语句中的MessageBox会被换成一个地址,指向可执行文件中的导入表的一个索引(函数名或索引号)。 导入表中指向MessageBox函数的实际地址会在程序装入内存的时候,根据User32.dll在内存中的位置由Windows系统动态填入。
使用invoke语句
API是可以调用了,另一个烦人的问题又出现了,Win32的API动辄就是十几个参数,整个源程序一眼看上去基本上都是把参数压堆栈的push指令,参数的个数和顺序很容易搞错,由此引起的莫名其妙的错误源源不断,源程序的可读性看上去也很差。 如果写的时候少写了一句push指令,程序在编译和链接的时候都不会报错,但在执行的时候必定会崩溃,原因是堆栈对不齐了。 有木有解决的办法呢?那是必须得!最好是像C语言一样,能在同一句中打入所有的参数,并在参数使用错误的时候能够提示。 好消息又来了,Microsoft终于做了一件好事,在MASM中提供了一个伪指令实现了这个功能,那就是invoke伪指令,它的格式是:invoke函数名[,参数1][,参数2]…[,参数n]
[codesyntax lang="asm"]invokeMessageBox,NULL,offsetszText,offsetszCaption,MB_OK[/codesyntax] 注意,invoke并不是80386处理器的指令,而是一个MASM编译器的伪指令,在编译的时候它把上面的指令展开成我们需要的4个push指令和一个call指令,同时,进行参数数量的检查工作,如果带的参数数量和声明时的数量不符,编译器报错:errorA2137:toofewargumentstoINVOKE 编译时看到这样的错误报告,首先要检查的是有没有少写一个参数。 对于不带参数的API调用,invoke伪指令的参数检查功能可有可无。 所以既可以用callAPI_Name这样的语法,也可以用invokeAPI_Name这样的语法。