让编程改变世界
Change the world by program
子程序名 proc [距离] [语言类型] [可视区域] [USES寄存器列表] [,参数:类型]…[VARARG] local 局部变量列表proc 和 endp 伪指令定义了子程序开始和结束的位置,proc 后面跟的参数是子程序的属性和输入参数。指令
子程序名 endp
error A2006: undefined symbol: _ProcWinMain
学过类似C语言的朋友就会举手说话啦,这个问题其实很简单:不就是没有定义的错误表现嘛~ invoke 伪指令无法得知子程序的定义情况,所以无法进行参数的检测。相当于 没有声明。 在这种情况下,为了让 invoke 指令能正常使用,必须在程序的头部用 proto 伪操作定义子程序的信息。 功能是提前告诉 invoke 语句关于子程序的信息,当然,如果子程序定义在前的话,用 proto 的定义就可以省略了。 由于程序的调试过程中可能常常对一些子程序的参数个数进行调整,为了使它们保持一致,就需要同时修改 proc 语句和 proto 语句。 在写源程序的时候有意识地把子程序的位置提到invoke 语句的前面,省略掉 proto 语句,可以简化程序和避免出错。 因此,我们看到的Win32 汇编代码基本是先写子程序,然后最后才是主程序的。push Var3
push Var2
push Var1
call SubRouting
add esp, 12
也就是说,调用者首先把参数压入堆栈,然后调用子程序,在完成后,由于堆栈中先前压入的数不再有用,调用者或者被调用者必须有一方把堆栈指针修正到调用前的状态,即堆栈的平衡。 我们看到参数是最右边的先入堆栈还是最左边的先入堆栈、还有由调用者还是被调用者来修正堆栈都必须有个约定,不然就会产生错误的结果。 因为有几种不同的约定,所以就是在上述文字中使用“可能”这两个字的原因。 各种语言中调用子程序的约定是不同的,所以在proc以及proto语句的语言属性中确定语言类型后,编译器才可能将invoke伪指令翻译成正确的样子。 注:VARARG表示参数的个数可以是不确定的,如wsprintf函数,本表中特殊的地方是StdCall的堆栈清除平时是由子程序完成的,但使用VARARG时是由调用者清除的。 我们来反汇编一个程序看下我们都学习了些什么:Example & Disassembly 我们发现,虽然是完成一样的功能,但是实现的方式各不相同,所谓殊途同归。 Win32约定的类型是StdCall,所以在程序中调用子程序或系统API后,不必自己来平衡堆栈,免去了很多麻烦。 [buy] 获得所有教学视频、课件、源代码等资源打包 [/buy] [Downlink href='http://urlxf.qq.com/?BJBj2q6']视频下载[/Downlink]