程序结束和程序入口
在WIN32中如果是只编写单个模块,那么可以像8086汇编一样:start : ......
end start
如果编写多个模块的话按之前的8086汇编,那么每个模块都要加个入口地址,但是现在的WIN32汇编可以也必须只在一个主模块指定入口地址,然后这个主模块最后的end要加上入口地址,而其他的模块不能在最后的end后加上入口地址,否则会报错。
注释和换行
注释一样也是分号后面的是注释,然后换行就是加一根斜杠:'\' 。比如 invoke MessageBox,\
Null,\
.......
就是在要换行的地方加一个 :' \ '。
调用API
在以前的DOS 环境中,要实现操作系统的各种功能都是通过各种中断来实现的,到了WIN32环境中我们就可以像调用函数一样调用
操作系统中的一些功能,也就是API函数,不仅我们写的WIN32环境要调用API函数,Windows系统本身也要调用这些函数,比如显示一个窗口什么的。
而这些功能模块,也就是这些API函数都被放在一个叫Windows动态链接库的文件里面了,这个文件也叫DLL。DLL也是一中Windows可执行文件,它采用和.exe一样的PE结构,它把能提供调用的函数列表用字符串的形式放在PE文件头部的导出表中。应用程序在被使用或者在执行的时候Windows系统会自动将DLL装入并调用相应的函数。
实际上 WIN32的基础就是由DLL组成的,WIN32API有很多函数,其中WIN32API核心由三个DLL组成,它们就是:
- KERNEL32.DLL——系统服务功能。包括内存管理,任务管理,动态链接等等。
- GDI32.DLL——图形设备接口。利用VGA与DRV之类的设备显示驱动程序完成显示文本和矩形等功能。
- USER32.DLL——用户接口服务。建立窗口和传送消息。
那么,在我们现在的WIN32汇编中如何来调用这些API函数呢,其实就像C++调用函数一样,我们要指定函数名,然后还要传递一些参数,要注意的是我们传递参数使用的方法是堆栈传递,有一个先进后出的原则,所以,比如MessageBox的函数声明长这样的话:
int MessageBox{
HWND hwnd, //hwnd类型的窗口句柄
LPCTSTR lpText, //要显示的内容的地址
LPCTSTR lpCaption, //要显示的标题的地址
UINT uType //消息框类型
};
那么如果我们要调用这个函数,我们就应该先向栈里面压入参数,并且参数的顺序要和上面的顺序相反,因为参数在栈中是先进后出的,我们将参数反顺序压入栈后,函数取出参数时正好会是正方向。压入参数后我们就调用函数。具体操作如下:
push uType
push lpCaption
push lpText
push hWnd
call MessageBox
上面的MssageBox函数声明只是这个messageBox函数的一部分,在这个函数的最后还有一个说明:Library: Use User32.lib.
这一句说明了这个函数包含在User32.DLL中。
在我们的源程序被编译成可执行文件之后,我们那个call MessageBox ,中的MessageBox会被换成一个地址,但这还不是我们最后的API的地址,这个地址指向可执行文件中的导入表,导入表又指向许多API函数的实际内存地址,然后在程序装入内存的时候,导入表中指向MessageBox函数的实际内存地址会根据User32.DLL在内存中的位置由系统自动填入。也就是说我们一开始写入的Messagebox类似于一个C++中指针的指针,它指向导入表,导入表才指向最后API函数的实际内存地址,而这个实际内存地址是由系统在程序装入内存的时候根据User32.DLL在内存中的位置自定填入的。