第二课 消息框
在本课中,我们将用汇编语言写一个 windows 程序,程序运行时将弹出一个消息框并显示"win32 assembly is great!"。
理论:
windows 为编写应用程序提供了大量的资源。其中最重要的是windows api (application programming interface)。 windows api是一大组功能强大的函数,它们本身驻扎在 windows 中供人们随时调用。这些函数的大部分被包含在几个动态链接库(dll)中,譬如:kernel32.dll、 user32.dll 和 gdi32.dll。 kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。除了上面主要的三个动态链接库,您还可以调用包含在其他动态链接库中的函数,当然您必须要有关于这些函数的足够的资料。
动态链接库,顾名思义,这些 api 的代码本身并不包含在 windows 可执行文件中,而是当要使用时才被加载。为了让应用程序在运行时能找到这些函数,就必须事先把有关的重定位信息嵌入到应用程序的可执行文件中。这些信息存在于引入库中,由链接器把相关信息从引入库中找出插入到可执行文件中。您必须指定正确的引入库,因为只有正确的引入库才会有正确的重定位信息。
当应用程序被加载时 windows 会检查这些信息,这些信息包括动态链接库的名字和其中被调用的函数的名字。若检查到这样的信息,windows 就会加载相应的动态链接库,并且重定位调用的函数语句的入口地址,以便在调用函数时控制权能转移到函数内部。
如果从和字符集的相关性来分,api 共有两类:一类是处理 ansi 字符集的,另一类是处理 unicode 字符集的。前一类函数名字的尾部带一个"a"字符,处理unicode的则带一个"w"字符(我想"w"也许是代表宽字符的意思吧)。我们比较熟悉的ansi字符串是以 null 结尾的一串字符数组,每一个ansi字符是一个 byte 宽。对于欧洲语言体系,ansi 字符集已足够了,但对于有成千上万个唯一字符的几种东方语言体系来说就只有用 unicode 字符集了。每一个 unicode 字符占有两个 byte 宽,这样一来就可以在一个字符串中使用 65336 个不同字符了。
这也是为什么引进 unicode 的原因。在大多数情况下我们都可以用一个包含头文件,在其中定义一个宏,然后在实际调用函数时,函数名后不需要加后缀"a"或"w"。
<译者注:如在头文件中定义函数foo();
#ifdef unicode
#define foo() foow()
#else
#define foo() fooa()
#endif
>
例子:
我先把框架程序放在下面,然后我们再向里面加东西。
.386
.model flat, stdcall
.data
.code
start:
end start
应用程序的执行是从 end
exitprocess proto uexitcode:dword
上面一行是函数原型。函数原型会告诉编译器和链接器该函数的属性,这样在编译和链接时,编译器和链接器就会作相关的类型检查。 函数的原型定义如下:
functionname proto [parametername]:datatype,[parametername]:datatype,...
简言之,就是在函数名后加伪指令proto,再跟一串由逗号相隔的数据类型链表。在前面的 exitprocess 定义中,该函数有一个 dword 类型的参数。当您使用高层调用语句 invoke 时,使用函数原型定义特别有用,您可以简单地认为 invoke 是一个有参数类型检查的调用语句。譬如,假设您这样写:
call exitprocess
若您事先没把一个dword类型参数压入堆栈,编译器和链接器都不会报错,但毫无疑问,在您的程序运行时将引起崩溃。但是,当您这样写:
invoke exitprocess
连接器将报错提醒您忘记压入一个 dword 类型参数。所以我建议您用 invoke 指令而不是call去调用一个函数。invoke 的语法如下:
invoke expression [,arguments]
expression 既可以是一个函数名也可以是一个函数指针。参数由逗号隔开。大多数api函数的原型放在头文件中。 如果您用的是 hutch 的 masm32,这些头文件在文件夹masm32/include 下, 这些头文件的扩展名为 inc,函数名和 dll 中的函数名相同,譬如:kernel32.lib 引出的函数 exitprocess 的函数原形声明于kernel.inc中。您也可以自己声明函数原型。 在我的教学课程中都使用 hutch 的windows。inc,这些头文件您可以从http://win32asm.cjb.net下载。
好,我们现在回到exitprocess 函数,参数uexitcode 是您希望当您的应用程序结束时传递 windows 的。 您可以这样写:
invoke exitprocess,0
把这一行放到开始标识符下,这个应用程序就会立即退出 windows,当然毫无疑问个应用程序本身是一个完整的 windows 程序。
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:
invoke exitprocess,0