Windows 汇编语言编程教程 (zz)


                        目录
                        
                        介 绍   ……………………………………………………………………………2
                        
                        为什么选用汇编语言 …………………………………………………………2
                        
                        为什么选择Windows系统……………………………………………………2
                        
                        Ⅰ开始学习之旅 ……………………………………………………………….…3
                        
                        编译器   ………………………………………………………………………3
                        
                        编辑器 …………………………………………………………………………3
                        
                        Ⅱ第一个程序  ……………………………………………………………………4
                        
                        控制台程序 ……………………………………………………………………4
                        
                        窗体程序 ………………………………………………………………………6
                        
                        ADDR 与 OFFSET  ………………………………………………………6
                        
                        Ⅲ汇编基础…………………………………………………………………………7
                        
                        cpu寄存器 ……………………………………………………………………7
                        
                        指令集基础 ……………………………………………………………………8
                        
                             Push 和 Pop…………………………………………………………………8
                        
                             Invoke   ……………………………………………………………………9
                        
                        程序例子 ………………………………………………………………………9
                        
                        IV. 窗体程序基础…………………………………………………………………10
                        
                        预备知识………………………………………………………………………10
                        
                             宏……………………………………………………………………………10
                        
                             过程…………………………………………………………………………10
                        
                        变量…………………………………………………………………………10
                        
                        一个简单的窗体程序…………………………………………………………11
                        
                        IV. 深入汇编和系统………………………………………………………………13
                        
                        字符串操作……………………………………………………………………13
                        
                        文件管理………………………………………………………………………13
                        
                             存储…………………………………………………………………………14
                        
                           程序例子……………………………………………………………………14
                        
                        控制……………………………………………………………………………15
                        
                        附加资源 …………………………………………………………………………16
                        
                             互联网………………………………………………………………………16
                        
                             书籍…………………………………………………………………………16
                        
                             MASM32……………………………………………………………………16
                        
                             MSDN………………………………………………………………………16
                        
                             新闻组………………………………………………………………………16
                        
                             IRC …………………………………………………………………………16
                        

                        介  绍

                        

                        
                        
                        
                        
                        
                        “This is for all you folks out there,who want to learn
                        the magic art of Assembly programming”
                        
                        -MAD
                        

                        

                        介 绍
                        
                        我最近才开始学习windows系统汇编语言编程,这个教程是我在学习汇编语言的过程中写下来的。我阅读大量的在线教程、书本,以及通过新闻组以及IRC通讯工具请问他人,本人就是通过这些方式学习汇编语言的。互联网上有很多的汇编编程的教程,但这些教程只是侧重于X86汇编。因为这些教材都假定读者已经掌握了高级编程语言以及基本的计算机系统知识。
                        

                        

                        为什么选用汇编语言?
                        
                        汇编语言具有若干的特色,使得在某此情况下,汇编语言是一种很好的选择。
                        
                        1 快速 汇编语言程序运行的速度比高级语言程序要快。通常,要求运行效率高的子程序是用汇编语言编写的。
                        
                        2 强大 运用汇编语言,你能得到不受限制的权力。相对的,高级语言则有种种限制,在实现某些特定的要求时变得困难。
                        
                        3 体积小 汇编语言程序通常比其他语言程序要小得多。这种特性在空间有限的情况下是非常有用的。
                        
                        为什么选择Windows系统?
                        
                        在任何操作系统和处理器模式下,都可以编写相应的汇编语言程序的。但是当前,多数人在使用基于x86处理器的Windows系统,所以从编写运行于此种环境下的程序开始我们的教程。一旦一种汇编语言的基础知识掌握了,我们就会很容易写出在其他运行环境下汇编程序。
                        

                        第一章

                        
                        Ⅰ开始学习之旅
                        
                        编写汇编程序,我们必须具备一些工具,它们是编译器以及编辑器。我们选择了一些能胜任这些工作的运行于Windows系统的工具如下。
                        
                        编译器
                        
                        编译器能把写下的汇编程序代码转换成机器码。通常,它附带有一个连接器。连接器用来连接可编译文件并从中生成可执行文件。Windows系统的可执行文件是以.exe为后缀的。下面给出一些流行的编译器:
                        
                        1 MASM
                        这个编译器是本教程所选用的,在学习本教程的过程中,你可以使用它。它原先由微软公司开发,现在被包括在MASM32v8程序包内了。MASM32v8程序包还包括了其他的工具。你可以从这个网址得到它:http://www.masm32.com/.
                        
                        注意:教程中有一些指令和宏指令,只有在MASM编译器才是有效的,所以强烈建议您从开始学习时选用MASM。

                        
                        

                        2. TASM
                        这是另一个受欢迎的编译器。由Borland公司开发,现在依然是个商业软件,所以你不能免费地获取到它。
                        
                        3. NASM
                        一个免费开放源码的编译器,它也能在其他系统平台上使用。它可以从这个网址获取到http://sourceforge.net/projects/nasm/
                        记住
                        
                        编辑器
                        
                        编辑器是在编译前编写程序代码的软件。编辑器可以个人自由选择。现在在很多种编辑器,你可以试用一下它们并选择一种你喜欢的。
                        
                        1 Notepad 记事本,Windows系统自带的。虽然它缺少很多功能,但它使用简便。
                        
                        2 Visual Studio 它不是免费的编辑器,但它出色的语法高亮显示功能能让你的代码更易于阅读。
                        
                        3. 其他 – 还有很多其他的编辑器,在些不一一列出它们的名字。其中一些很受欢迎:
                        
                        a. Ultraedit (我个人最喜欢的e) http://www.ultraedit.com/
                        
                        b. Textpad http://www.textpad.com/
                        
                        c. VIM http://www.vim.org/
                        
                        d. Emacs http://www.gnu.org/software/emacs/emacs.html
                        
                        e. jEdit http://www.jedit.org/
                        
                        
                        
                        

                        第二章

                        

                        
                        
                        
                        Ⅱ第一个程序
                        
                        现在我们有了自己的工具,打开你的文本编辑器,跟着下面的介绍,开始学习编程吧。这是世上最普通的程序,“Hello
                        World”程序。
                        
                        控制台程序
                        
                        控制台程序是运行在系统控制台的(也就大家所知的命令行)。为创建这个程序,首先粘贴下面的代码到你的文本编辑器上,并保存为文件“hello.asm”。
                        
                        .386
                        
                        .model flat, stdcall
                        
                        option casemap :none
                        
                        include \masm32\include\windows.inc
                        
                        include \masm32\include\kernel32.inc
                        
                        include \masm32\include\masm32.inc
                        
                        includelib \masm32\lib\kernel32.lib
                        
                        includelib \masm32\lib\masm32.lib
                        
                        .data
                        
                        HelloWorld db "Hello World!", 0
                        
                        .code
                        
                        start:
                        
                        invoke StdOut, addr HelloWorld
                        
                        invoke ExitProcess, 0
                        
                        end start
                        
                        现在,通过开始菜单,点“运行…”选项,输入“cmd”(没有引号)并回车,就能进入到命令行。接着在cmd下转到保存有”hello.asm”的目录,输入"\masm32\bin\ml
                        /c /Zd /coff
                        hello.asm"。期望编译器不会提示错误,你的程序能被正确编译!然后,我们还得连接它,所以接着输入"\masm32\bin\Link
                        /SUBSYSTEM:CONSOLE
                        hello.obj"。祝贺你!你已经成功编译了第一个汇编语言程序。在文件夹里出现了一个中Hello.exe的文件。在命令行下打"hello"来运行你的程序。它会输出"Hello
                        World!"。可见,为了显示"Hello World!",我们只要编写很少的代码就可以了。
                        
                        这些代码都起了什么作用呢?让我们一行一行地看下去。
                        
                        .386
                        
                        这条指令的作用是告知编译器使用.386指令集。当前,几乎没有处理器使用比.386更老的指明令集了。我们还可以选择使用.486.或586,但是.386是兼容性最好的指令集。
                        
                        

                        .model flat, stdcall
                        
                        .MODEL
                        是一条指定你程序的内存模式的汇编指令。Flat是一种方便的系统程序模式,因为在这种模式下不再区分远指针(far)和近指针(far)。Stdcall
                        是一种系统函数传递参数的方法,它意味着你得以从右到左的顺序传递你的参数。
                        
                        option casemap :none
                        
                        强制你的程序代码大小写敏感,这意味着Hello和hello被看做不同的。很多高级编程语言同样是大小写敏感的,所以这是个编程的良好习惯。
                        
                        include \masm32\include\windows.inc
                        
                        include \ masm32\include\kernel32.inc
                        
                        include \masm32\include\masm32.inc
                        
                        这是系统程序必需的包含文件。windows.inc通常必须包含的,因为它包含了Win32
                        API常量和定义的声明。kernel32.inc包含了我们所使用的ExitProcess函数。masm32.inc包含有StdOut函数。StdOut函数不是Win32函数,它是MASM32v8新增进去的。
                        
                        includelib \masm32\lib\kernel32.lib
                        
                        includelib \masm32\lib\masm32.lib
                        
                        函数依赖库,基于这个目的,这些库得包含进去。
                        
                        .data
                        
                        程序中所有初始化的数据必须放在这条指令下面。此外,还有别的指令比如.data?和.const。它们分别位于未初始化数据和常量的前面,但是,在我们的”Hello
                        World”程序中并没有用到它们。
                        
                        HelloWorld db "Hello World!", 0
                        
                        db代表“字节”,并声明HelloWorld为一个字符串。"Hello
                        World!"后面跟着一个”NULL”字母,这是因为ANSI字符串必须以NULL结尾。
                        
                        .code
                        
                        这代表程序代码段的开始。
                        
                        start:
                        
                        你程序的代码位于这个标号的后面,但位于” end start”前面。
                        
                        invoke StdOut, addr HelloWorld
                        
                        Invoke调用一个函数及其参数,addr HelloWorld位于它后面。这一行所做的是传递"Hello
                        World!"的地址和调用StdOut。注意StdOut函数只是在MASM32中有效的,它是一个调用其它函数来输出文件的宏。在别的编译器里,你得使用更多的代码并要用到win32函数WriteConsole.。
                        
                        invoke ExitProcess, 0
                        
                        显而易见,它传递参数0到ExitProcess函数,从而退出进程。
                        窗体程序
                        
                        我们也可以编写一个有窗体版本的“Hello
                        World”程序。粘贴下面文本到你的文件编辑器里并保存为文件"hellow.asm".
                        
                        .386
                        
                        .model flat, stdcall
                        
                        option casemap :none
                        
                        include \masm32\include\windows.inc
                        
                        include \masm32\include\kernel32.inc
                        
                        include \masm32\include\user32.inc
                        
                        includelib \masm32\lib\kernel32.lib
                        
                        includelib \masm32\lib\user32.lib
                        
                        .data
                        
                        HelloWorld db "Hello World!", 0
                        
                        .code
                        
                        start:
                        
                        invoke MessageBox, NULL, addr HelloWorld, addr
                        HelloWorld, MB_OK
                        
                        invoke ExitProcess, 0
                        
                        end start
                        
                        现在,再打开命令行并转到"hellow.asm"所在目录。输入"\masm32\bin\ml /c /Zd
                        /coff hellow.asm"回车,接着输入"\masm32\bin\Link
                        /SUBSYSTEM:WINDOWS
                        hellow.obj"并回车。注意,subsystem是WINDOWS不再是CONSOLE。这个程序弹出一个显示"Hello
                        World!"的信息框。
                        
                        与控制台版相比,窗体版本的代码只有3行是不同的。其中有两行把masm32包含文件和库文件更换为user32包含文件和库文件,这是因为我们现在是使用MessageBox函数,而不是使用StdOut了。第3个不同的行是用MessageBox函数代替了StdOut函数。不同之处就这么多而已!
                        
                        ADDR 与 OFFSET
                        
                        在我们“Hello World!”程序例子中,我们使用了'addr' 来获取字符串"Hello
                        World!"的地址。还有另外一个类似的指令'offset',虽然两者的目的都是在程序执行中获取变是变量的地址。它们主要的区别是'offset'
                        只能获取全局变量的地址,然而addr能获取全局变以及局部变量的地址。然而我们不讨论局部变量,所以不用担心这种区别。但是我们还是要记住这种区别的。
                        

                        Ⅲ 第三章

                        
                        汇编基础
                        
                        cpu寄存器
                        
                        现在我们已经能够编写并运行一个简单的程序了。让我们转到本教程的核心内容-汇编基本语法吧。你要写出自己的汇编程序,这些基本的知识是要掌握的。
                           
                        32位通用寄存器有8个。它们其中前面四个也就是eax,ebx,ecx,edx,也能用它们16位或8位的名字形式进行存取。比如,ax存取eax的低16位,al存取低8位,还有ah存取的是9到16位。其余的寄存器也能以类似的方式进行存取。就如大家想象的那样,这些通用寄存器虽然大多有专用的用途,但它们有通用的地方。
                        
                        地址

                         名称

                         描述

                        
                        EAX*

                         累加寄存器

                         进行计算操作和保存数据结果

                        
                        EBX

                         基址寄存器

                         指向数据寄存器中的数据

                        
                        ECX*

                         计数寄存器

                         字符串以及循环的计数

                        
                        EDX*

                         数据寄存器

                         输入/输出的指针

                        
                        ESI

                         源变址寄存器

                         字符串操作中的源指针

                        
                        EDI

                         目的变址寄存器

                         字符串操作中的目的指针

                        
                        ESP

                         堆栈指针寄存器

                         堆栈指针,不能人为使用它

                        
                        EBP

                         堆栈基址寄存器

                         指向堆栈中的数据

                        
                        
                        

                        注意:虽然它们被称为通用寄存器,但是只有那些标有*号的才能在窗体程序编程中使用。

                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        下面是6个16位的段寄存器。它们定义在存储器的段。
                        
                        地址

                         名称

                         描述

                        
                        CS

                         代码段寄存器

                         保存要运行的指令

                        
                        DS,ES,FS,GS

                         数据段寄存器

                         数据段

                        
                        SS

                         堆栈段寄存器

                         当前程序的堆栈

                        
                        
                        

                        最后,还有2个32位的没有归类的寄存器
                        
                        地址

                         名称

                         描述

                        
                        EFLAGE

                         标志寄存器

                         状态,控制,系统标志

                        
                        EIP

                         指令指针寄存器

                         下一个要执行的指针的偏移

                        
                        
                        
                        
                        
                        指令集基础
                        
                        x86指令集非常宏大,但是我们通常并没全都使用到了它们。下面介绍一些我们应该掌握的指令。
                        
                        指令

                         描述

                        
                        ADD* reg/memory, reg/memory/constant

                         把两个操作数相加并把结果保存进第一个操作数。如果有进位的话,它会设置CF标志位

                        
                        SUB* reg/memory, reg/memory/constant

                         第一个操作数减去第二个操作数,并把结果保存到第一个操作数里

                        
                        AND* reg/memory, reg/memory/constant

                         两个操作数逻辑与,并把结果存到第一个操作数里

                        
                        OR* reg/memory, reg/memory/constant

                         两个操作数逻辑或,并把结果存到第一个操作数里

                        
                        XOR* reg/memory, reg/memory/constant

                         两者异或,并把结果存到第一个操作数里。注意你不能对
                        
                        两个存储器操作数进行异或操作

                        
                        MUL reg/memory

                         操作数与累加器寄存器相乘,而后把结果存进累加器寄存
                        
                        器

                        
                        DIV reg/memory

                         累加器寄存器被操作数除并把结果存到累加器

                        
                        INC reg/memory

                         操作数的值增1并把结果存进操作数

                        
                        DEC reg/memory

                         操作数的值减1并把结果存进操作数

                        
                        NEG reg/memory

                         操作数的值取补并把结果存进操作数

                        
                        NOT reg/memory

                         操作数的值取反并把结果存进操作数

                        
                        PUSH reg/memory/constant

                         把操作数压进堆栈顶端

                        
                        POP reg/memory

                         弹出堆栈顶端的值并保存到操作数

                        
                        MOV* reg/memory, reg/memory/constant

                         把第二个操作数的值保存到第一个操作数里面

                        
                        CMP* reg/memory, reg/memory/constant

                         第一个操作数减第二个操作数,并设置相应当的标志位。通常与JMP,REP等指令一起使用

                        
                        JMP** label

                         跳转到标号处

                        
                        LEA reg, memory

                         取第二个操作数的地址偏移,并把结果保存进第一个操作数

                        
                        CALL subroutine

                         调用另一个过程直到程序返回

                        
                        RET

                         程序返回到调用者

                        
                        INT constant

                         调用操作数指定的中断

                        

                        *指令不能有两个存储器操作数
                        
                        **这个指令可以结合条件来使用。比如,JNB(不小于),是只有在CF=0这一条件下才会跳转。
                        
                        最新的全部指令集参考可以从下面这个网址得到
                        
                        http://www.intel.com/design/pentium4/manuals/index.htm.
                        
                        Push 和 Pop
                        
                        Push和pop是操作堆栈的指令。Push获取一个数据并把它压进堆栈的顶端。Pop获取堆栈顶端的数据,弹出并保存它。因此,堆栈是使用一个先进后出的存取方式(LIFO)。堆栈是计算机中一个常见的数据结构,所以如果你在编程过程中对堆栈操作感到不顺手的话我建议你先掌握这一知识。
                        Invoke
                        
                        Invoke是MASM特有的一个伪指令。它使得在调用函数前不必先传递参数。这让我们省略了很多的代码。
                        
                        举个例子说明如下
                        
                        invoke SendMessage, [hWnd], WM_CLOSE, 0, 0
                        
                        等效于:
                        
                        push 0
                        
                        push 0
                        
                        push WM_CLOSE
                        
                        push [hWnd]
                        
                        call [SendMessage]
                        
                        程序例子
                        
                        下面是一个完整的程序。它说明了如何去使用指令和寄存器。看看是否全部弄懂了它。
                        
                        .386
                        
                        .model flat, stdcall
                        
                        option casemap :none
                        
                        include \masm32\include\windows.inc
                        
                        include \masm32\include\kernel32.inc
                        
                        include \masm32\include\masm32.inc
                        
                        includelib \masm32\lib\kernel32.lib
                        
                        includelib \masm32\lib\masm32.lib
                        
                        .data
                        
                        ProgramText db "Hello World!", 0
                        
                        BadText db "Error: Sum is incorrect value", 0
                        
                        GoodText db "Excellent! Sum is 6", 0
                        
                        Sum sdword 0
                        
                        .code
                        
                        start:
                        
                        ; eax
                        
                        mov ecx, 6 ; set the counter to 6 ?
                        
                        xor eax, eax ; set eax to 0 0
                        
                        _label: add eax, ecx ; add the numbers ?
                        
                        dec ecx ; from 0 to 6 ?
                        
                        jnz _label ; 21
                        
                        mov edx, 7 ; 21
                        
                        mul edx ; multiply by 7 147
                        
                        push eax ; pushes eax into the stack
                        
                        pop Sum ; pops eax and places it in Sum
                        
                        cmp Sum, 147 ; compares Sum to 147
                        
                        jz _good ; if they are equal, go to _good
                        
                        _bad: invoke StdOut, addr BadText
                        
                        jmp _quit
                        
                        _good: invoke StdOut, addr GoodText
                        
                        _quit: invoke ExitProcess, 0
                        
                        end start
                        
                        注意:“;”符号表示注释。所有跟在它后面的字符都不会被编译。把提示和注意点放在注释中是个好主意,它能让你的代码易读。

                        
                        第四章

                        

                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        IV. 窗体程序基础
                        
                        窗体程序通常由一个或几个窗体组成。因此,做为windows程序员至少要懂得怎么创建一个简单的窗体。很不幸,它不是那么容易的事,但是本教程会指导你怎么去做。
                        
                        预备知识
                        
                        在编写窗体程序前我们还要讨论几个主题。让我们花点时间复习一下预备知识。
                        
                        宏
                        
                        MASM有几个让汇编编程变得非常容易的宏。我们已经接触到’invoke’,它简单地调用一个函数。下面列出其他几种,之前在你用高级语言编程时它们的用法是很明显的。
                        
                        o .if, .else, .endif
                        
                        o .while, .break, .endw
                        
                        过程
                        
                        与高级语言类似,MASM让你定义各种过程使得你的代码易于阅读。它们的格式如下所示:
                        
                        <name> proc <var1>:<var1 type>, <var2>:<var2 type>, ...
                        
                        <function code>
                        
                        ret
                        
                        <name> endp
                        
                        返回值保存在eax寄存器里,这个过程用下面格式来调用
                        
                        invoke <name>, param1, param2, ...
                        
                        返回值可以用下面指令来获取
                        
                        mov RetVal, eax
                        
                        变量
                        
                        变量被分配在内存中的,并用来存储你的数据。在没有足够的寄存器可用的情况下,变量是非常有用的。变量有两种类型—全局变量和局部变量。如果全局变量已被初始化,它们被置于.data块;如果它们没被初始化的话,就被置于.data?块。还有,如果全局变量被初始化并且不会被改变的话,它们就被置于.const块。声明全局变量的格式如下:
                        
                        <name> <type> <value, or ? if uninitialized>
                        
                        局部变量是被放于过程内部的,暂时保存,供过程内部使用。它们在创建时不能被初始化。格式如下:
                        
                        local <name>:<type>
                        
                        有几种变量类型以后将会遇到。其中有几种常见,比如’byte’,’word’ (4
                        bytes),’dword’(8
                        bytes)。还有更多,但是它们常常与这三种类型中的一种是相同的,只不过名称不同。
                        
                        

                        一个简单的窗体程序
                        
                        窗体程序有两个主要的部分。第一部分是WinMain,它创建窗体还包含叫实现消息循环的代码。“消息循环”监视消息并分派消息。第二部分是过程返回WndProc,它是接收消息的,这部分处理你的鼠标事件及刷新窗口等。
                        
                        .386
                        
                        .model flat, stdcall
                        
                        option casemap :none
                        
                        include \masm32\include\windows.inc
                        
                        include \masm32\include\user32.inc
                        
                        include \masm32\include\kernel32.inc
                        
                        includelib \masm32\lib\user32.lib
                        
                        includelib \masm32\lib\kernel32.lib
                        
                        以上是我们通常必需的
                        
                        WinMain proto :DWORD, :DWORD, :DWORD, :DWORD
                        
                        This is a function prototype. It let's us call the
                        WinMain function later in the program.
                        
                        It can be compared to a C/C++ function declaration.
                        
                        这是函数原型。在稍后的程序里我们称它为WinMain函数。
                        
                        .data
                        
                        ClassName db "WinClass", 0
                        
                        AppName db "Simple Window", 0
                        
                        我们声明我们的字符变量
                        
                        .data?
                        
                        hInstance HINSTANCE ?
                        
                        变量hInstance保存模块实例的句柄,以便与窗体相关联。稍后我们将把它传递到CreateWindow函数中。
                        
                        .code
                        
                        start:
                        
                        invoke GetModuleHandle, NULL
                        
                        mov hInstance, eax
                        
                        invoke WinMain, hInstance, NULL, NULL, 0
                        
                        invoke ExitProcess, eax
                        
                        获取模块句柄并把它保存到变量hInstance中。接着调用WinMain函,而已退出。WinMain是这个程序的核心,所以我们将深入研究它。
                        
                        注意:从这点来看,我们假设你能够从MSDN中查找windows函数。它有函数参数,返回值,还有其它你必须了解的信息。你可以在附加资源这个章节里获取关于MSDN的信息。

                        
                        
                        
                        
                        
                        
                        
                        
                        WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE,
                        CmdLine:LPSTR,
                        
                        CmdShow:DWORD
                        
                        local wc:WNDCLASSEX
                        
                        local msg:MSG
                        
                        local hwnd:HWND
                        
                        这是WinMain函数的开头部分。我们声明了三个局部变量:wc,msg,,还有hwnd。Wc保存我们创建的窗口类,窗体类是一个创建窗体的模板。Msg保存消息循环中返的消息。Hwnd保存我们窗本的句柄。
                        
                        mov wc.cbSize, SIZEOF WNDCLASSEX
                        
                        mov wc.style, CS_HREDRAW or CS_VREDRAW
                        
                        mov wc.lpfnWndProc, offset WndProc
                        
                        mov wc.cbClsExtra, NULL
                        
                        mov wc.cbWndExtra, NULL
                        
                        注意:在窗体程序中,’or’操作运算符常常用来联合参数中的标志。

                        
                        push hInstance
                        
                        pop wc.hInstance
                        
                        mov wc.hbrBackground, COLOR_WINDOW+1
                        
                        mov wc.lpszMenuName, NULL
                        
                        mov wc.lpszClassName, offset ClassName
                        
                        invoke LoadIcon, NULL, IDI_APPLICATION
                        
                        mov wc.hIcon, eax
                        
                        mov wc.hIconSm, eax
                        
                        invoke LoadCursor, NULL, IDC_ARROW
                        
                        mov wc.hCursor, eax
                        
                        invoke RegisterClassEx, addr wc
                        
                        这些是填充我们先前声明的wc结构。然后以wc为参数调用RegisterClassEx。至于更多关于wc的每个成员的信息,请在MSDN中查找WNDCLASSEX结构的资料。

                        
                        invoke CreateWindowEx, 0, addr ClassName, addr AppName,
                        WS_OVERLAPPEDWINDOW
                        
                        or WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,
                        CW_USEDEFAULT, CW_USEDEFAULT, NULL,
                        
                        NULL, hInst, NULL
                        
                        mov hwnd, eax
                        
                        调用CreateWindowEx函数创建窗体。其中有很多参数被传递进去来表明怎么创建窗体。窗体的句柄会返回并保存到变量hwnd中。
                        
                        .while TRUE
                        
                        invoke GetMessage, addr msg, NULL, 0, 0
                        
                        .break .if (!eax)
                        
                        invoke TranslateMessage, addr msg
                        
                        invoke DispatchMessage, addr msg
                        
                        .endw
                        
                        这个while循环也就是先前提到的消息循环。当一个输入事件发生,系统会传递这个事件到一个消息里,并把消息放进程序的消息队列中去。GetMessage取回这些消息并保存进变量msg里。TranslateMessage把键盘消息转换成字符消息。最后,DispatchMessage把这些消息发送到WndProc函数里。在WndProc函数中,这些消息将会被处理。
                        
                        mov eax, msg.wParam
                        
                        ret
                        
                        WinMain endp
                        
                        返回值保存进msg.wParam,WinMain函数结束。
                        
                        WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM,
                        lParam:LPARAM
                        
                        .if uMsg == WM_DESTROY
                        
                        invoke PostQuitMessage, 0
                        
                        .else
                        
                        invoke DefWindowProc, hWnd, uMsg, wParam, lParam
                        
                        ret
                        
                        .endif
                        
                        xor eax, eax
                        
                        ret
                        
                        WndProc endp
                        
                        WndProc函数是处理消息的地方。唯一一个一定要处理的消息是WM_DESTROY,它通过调用PostQuitMessage来退出程序。如果有其它你要处理的事件,你可以在这里把它们加进来。普通要处理的消息是WM_CREATE(当创建窗体时),WM_PAINT
                        (当窗体必须重画时), 还有WM_CLOSE
                        (关闭窗体时)。其它没有处理的消息被传递给DefWindowProc函数对消息进行默认处理
                        
                        end start
                        
                        就这些了。你已经了解了怎么去创建一个窗体!
                        
                        
                        
                        

                        第五章

                        
                        IV. 深入汇编和系统
                        
                        下面有一些资料来扩展你关于汇编以及系统编程方面的知识:字符串操作,文件处理,还有系统窗体的控制。
                        
                        字符串操作
                        
                        字符串,数组都是程序中的基本部分。如果你想显示文本或者请求使用者输入,它们就用得了。它们使用到了如下的寄存器:esi,edi,ecx,eflag中的方向控制标志。方向控制标志是指定移动字符串时的方向。一些常见的字符串操作指令是movsb,
                        cmpsb, stasb, and
                        stosb.为了操作字符串,你可以在字符串控制指令中使用某些rep?的形式。下面是在串操作指令中可以使用到rep?前缀
                        

                        

                        前缀

                         串操作指令

                         描述

                        
                        rep

                         movsb

                         复制字符串

                        
                        repe

                         cmpsb

                         比较字符串

                        
                        repne

                         scasb

                         扫描字符串中一个字符

                        
                        rep

                         stosb

                         保存一个字符到字符串中

                        

                        下面给个复制字符串的例子
                        
                        cld ; sets the direction flag to forward
                        
                        mov esi, source ; move the source address in to esi
                        
                        mov edi, dest ; move the destination address in to edi
                        
                        mov ecx, length ; move the length to copy in to ecx
                        
                        rep movsb ; copy length bytes from esi to edi
                        
                        文件管理
                        
                        在旧的DOS系统世界时,文件是通过使用中断进行操作的。在windows系统里,我们通过使用系统函数访问文件。其中可供我们使用的4种函数是:
                        
                        CreateFile –创建或打开一个文件,并返回它的句柄
                        
                        ReadFile – 从文件中读取数据
                        
                        WriteFile – 写数据到文件里
                        
                        CloseHandle – 关闭你用CreateFile函数得到的句柄
                        存储
                        
                        为了读取文件内容,我们必须分配一些内存来存储数据。内存可以如你所愿地被分配,锁定,但是最后记得解锁和释放。做这些工作的函数是GlobalAlloc,
                        GlobalLock, GlobalUnlock, 还有GlobalFree。相当容易,呵呵!
                        
                        程序例子
                        
                        这个程序读取"c:\test.txt"的内容,并通过一个消息框输出。
                        
                        .386
                        
                        .model flat, stdcall
                        
                        option casemap :none
                        
                        include \masm32\include\windows.inc
                        
                        include \masm32\include\user32.inc
                        
                        include \masm32\include\kernel32.inc
                        
                        includelib \masm32\lib\user32.lib
                        
                        includelib \masm32\lib\kernel32.lib
                        
                        一些通常的包含文件
                        
                        .data
                        
                        FileName db "c:\test.txt", 0
                        
                        .data?
                        
                        hFile HANDLE ?
                        
                        hMemory HANDLE ?
                        
                        pMemory DWORD ?
                        
                        ReadSize DWORD ?
                        
                        我们定义字符串,还声明了四个将会用到的变量
                        
                        .const
                        
                        MEMORYSIZE equ 65535
                        
                        这是说明分配多大的内存,这样有足够的空间保存我们的文件
                        
                        .code
                        
                        start:
                        
                        invoke CreateFile, addr FileName, GENERIC_READ,
                        FILE_SHARE_READ,
                        
                        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
                        
                        mov hFile, eax
                        
                        调用CreateFile函数并保存文件句柄到hFile变量。通常,放置一个'h'在句柄之前,而放置'p'在指针之前
                        
                        invoke GlobalAlloc, GMEM_MOVEABLE or GMEM_ZEROINIT,
                        MEMORYSIZE
                        
                        mov hMemory, eax
                        
                        invoke GlobalLock, hMemory
                        
                        mov pMemory, eax
                        
                        分配并锁定我们的内存
                        
                        invoke ReadFile, hFile, pMemory, MEMORYSIZE-1, addr
                        ReadSize, NULL
                        
                        invoke MessageBox, NULL, pMemory, addr FileName, MB_OK
                        
                        这些行是读文件到内存中并输出其内容
                        
                        invoke GlobalUnlock, pMemory
                        
                        invoke GlobalFree, hMemory
                        
                        invoke CloseHandle, hFile
                        
                        invoke ExitProcess, NULL
                        
                        end start
                        
                        可别忘清除工作啊
                        控制
                        
                        一旦创建了一个窗体程序,我们就会想在上面放置一些按钮还有文件框在上面。幸运地,这是容易的事!它的语法非常类似于创建一个窗体,除此外,我们不必调用RegisterClassEx,因为我们的类将为我们预先定义了。为了做到这些,从第4章内容那里编辑WndProc函数来响应WM_CREATE消息
                        
                        .elseif uMsg == WM_CREATE
                        
                        invoke CreateWindowEx, NULL, addr ButtonClassName, addr
                        ButtonText, WS_CHILD
                        
                        or WS_VISIBLE or BS_DEFPUSHBUTTON, 10, 50, 80, 30, hWnd,
                        ButtonID, hInstance, NULL
                        
                        mov hButton, eax
                        
                        invoke CreateWindowEx, WS_EX_CLIENTEDGE, addr
                        EditClassName, NULL, WS_CHILD
                        
                        or WS_VISIBLE, 10, 10, 100, 20, hWnd, EditID, hInstance,
                        NULL
                        
                        mov hEdit, eax
                        
                        在.data块,你还要增加几个变量。定义EditClassName为"edit",ButtonClassName
                        为"button"。还有,你要定义EditID
                        和ButtonID为常量。它们的值是什么并没关系,只要它们在其它控制里不出现相同的ID。此外,你不能对变量hEdit和hButton进行初始化,因为它们都是句柄类型。最后,ButtonText必须赋值为字符串,它将显示在按钮上。现在我们还想知道按钮是什么时候被按下的。这可以通过监视WM_COMMAND消息获知,一个如果按下按钮将会发送的消息。
                        
                        .elseif uMsg == WM_COMMAND
                        
                        mov eax, wParam
                        
                        .if ax == ButtonID
                        
                        shr eax, 16
                        
                        wParam参数包含了有关消息的信息。我们可以检查它是否是那个按钮发送的消息,因为既然我们不想处理其他控制的消息。Shr是右移位操作指令,它把wParam右移16位。这是一种存取32位寄存器的高16位好方法,这样我们通过访问ax就可以很容易做到了。
                        
                        .if ax == BN_CLICKED
                        
                        <code for what happens if the button is pressed>
                        
                        .endif
                        
                        .endif
                        
                        现在我们知道按钮已经被按下,我们可以对其做一些事了。如果有兴趣学习更多的窗体知识,看一下附加资源这章节。它列出了一些不错有关系统常规编程的参考书和网站
                        
                        

                        附加资源
                        
                        互联网
                        
                        http://www.xs4all.nl/~smit/ -有有用的x86汇编编程教程
                        
                        http://win32asm.cjb.net/ -有优秀的关于windows汇编编程的教程
                        
                        http://board.win32asmcommunity.net/
                        -.一个很活跃的讨论关于windows汇编编程的在线论坛
                        
                        书籍
                        
                        Programming Windows, 第四版,Charles
                        Petzold著作,是一本出色的系统编程书。它给出了很多系统编程的代码例子,覆盖了关于系统编程的很大范围的主题。
                        
                        Intel Pentium 4 Processors Manuals,
                        可从http://www.intel.com/design/pentium4/manuals/
                        获取到。它是x86汇编编程的完整的参考。
                        
                        The Art of Assembly Programming, Randall Hyde所著, 可从
                        
                        http://webster.cs.ucr.edu/AoA.html获取到。它是我见过最好的和最全面的的x86汇编编程书中
                        
                        MASM32
                        
                        在你的\masm32\HELP\目录里。它是一个叫masm32.hlp的文件,包含了MASM32指南。它有所有关于宏,标志,代码优化等资料。这是学习汇编特别是MASM32非常好的参考。
                        
                        MSDN
                        
                        MSDN通常是Visual
                        Studio自带有的,你也可以从网站http://msdn.microsoft.com/在线游览。它包括了关于Windows的系统函数,常量,及所有能够被想象到资料。
                        
                        新闻组
                        
                        目前有两个涉及x86汇编的新闻组。它们是comp.lang.asm.x86 和
                        alt.lang.asm。两者都有相当高的访问量,拥有很多知识渊博的读者
                        
                        IRC
                        
                        有一个关于windows汇编编程的IRC(internet relay chat)频道,#win32asm on
                        EFNet [http://www.efnet.org/]

                        
                        (完)

转载于:https://www.cnblogs.com/tongzhiyong/archive/2007/05/01/734031.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
××××××××××××××××××××××××××× × 《Windows环境下32位汇编语言程序设计》 × × 附书代码说明 × ××××××××××××××××××××××××××× 1. 编译器和链接器 本附书代码全部采用 MASM 格式编写,推荐使用 MASM32 软 件包作为编译环境,MASM32 软件包可以在以下地址下载: MASM32官方站点: http://www.masm32.com 安装及开发的操作系统建议采用32位的Windows 7,在64位操作系统 上安装 MASM32 软件包,安装生成的Lib文件可能不兼容。 安装完成以后请将本光盘根目录下的环境设置批处理文件Var.bat 拷贝到 Masm32\bin 目录下,并根据 MASM32 的安装位置编辑修改 Var.bat 文件中的相关目录名称。 2. 代码维护工具 每个例子都包括了描述编译、链接方法的 makefile 文件, 使用 nmake 工具可以自动根据此文件进行编译链接,nmake 工 具可以从 Visual C++ 的 bin 目录中找到,也可以从作者的网 站中下载。 3. 编译环境和编译方法 建议使用命令行方式进行编译,以编译 Chapter02\Test 目录中 Test.asm 为例,步骤是: I. 打开一个“命令提示符”窗口。 II. 进入环境设置批处理文件 Var.bat 所在目录并执行 它,以后就可以使用这个“命令提示符”窗口编译 文件了。 x: cd \masm32\bin var III. 进入源代码目录: cd \chapter02\test IV. 使用 nmake 工具进行编译链接: nmake V. 执行编译好的可执行文件。 如果需要对源代码进行修改,不必关闭“命令提示符”窗口, 只要切换到编辑器窗口,在修改 *.asm 文件后重复进行第 IV 和 第 V 步骤即可。 # 特别注意:将光盘中的代码拷贝到硬盘后,必须将文件的只读属性去除! 4. 本光盘所包含目录的说明 根目录下的 *.pdf ;附录A、B、C的电子版文档 Chapter02\Test ;测试编译环境 Chapter03\HelloWorld ;Hello World Chapter04\FirstWindow ;用Win32汇编写第一个窗口 Chapter04\FirstWindow-1 ;用Win32汇编写第一个窗口 Chapter04\SendMessage ;窗口间的消息互发 Chapter04\SendMessage-1 ;窗口间的消息互发 Chapter05\Menu ;使用资源 - 使用菜单 Chapter05\Icon ;使用资源 - 使用图标 Chapter05\Dialog ;使用资源 - 使用对话框 Chapter05\Listbox ;使用资源 - 使用列表框 Chapter05\Control ;使用资源 - 使用子窗口控件 Chapter05\ShowVersionInfo ;使用资源 - 显示版本信息资源的程序 Chapter05\VersionInfo ;使用资源 - 使用版本信息资源 Chapter06\Timer ;定时器的使用 Chapter07\DcCopy ;在两个窗口的 DC 间互相拷贝屏幕 Chapter07\Clock ;模拟时钟程序 Chapter07\BmpClock ;用 Bitmap 图片做背景的模拟时钟程序 Chapter07\TestObject ;一些常见的绘图操作 Chapter08\CommDlg ;使用通用对话框 Chapter09\Toolbar ;使用工具栏 Chapter09\StatusBar ;使用状态栏 Chapter09\Richedit ;使用丰富编辑控件 Chapter09\Wordpad ;一个完整的文本编辑器例子 Chapter09\SubClass ;窗口的子类化例子 Chapter09\SuperClass ;窗口的超类化例子 Chapter10\MemInfo ;显示当前内存的使用情况 Chapter10\FindFile ;全盘查找文件的例子 Chapter10\FormatText ;文件读写例子 Chapter10\FormatText\FileMap ;使用内存映射文件进行文件读写的例子 Chapter10\MMFShare ;使用内存映射文件进行进程间数据共享 Chapter11\Dll\Dll ;最简单的动态链接库例子 - 编写 DLL Chapter11\Dll\MASM Sample ;最简单的动态链接库例子 - 使用 DLL Chapter11\Dll\VC++ Sample ;最简单的动态链接库例子 - 在VC++中使用汇编编写的DLL Chapter11\KeyHook ;Windows 钩子的例子 - 监听键盘动作 Chapter11\RecHook ;Windows 日志记录钩子的例子 - 监听键盘动作 Chapter12\Counter ;有问题的程序 - 一个计数程序 Chapter12\Thread ;用多线程的方式解决上一个程序的问题 Chapter12\Event ;使用事件对象 Chapter12\ThreadSynErr ;一个存在同步问题的多线程程序 Chapter12\ThreadSyn\UseCriticalSection ;使用临界区对象解决多线程同步问题 Chapter12\ThreadSyn\UseEvent ;使用事件对象解决多线程同步问题 Chapter12\ThreadSyn\UseMutex ;使用互斥对象解决多线程同步问题 Chapter12\ThreadSyn\UseSemaphore ;使用信号灯对象解决多线程同步问题 Chapter13\CmdLine ;使用命令行参数 Chapter13\Process ;创建进程的例子 Chapter13\ProcessList ;显示系统中运行的进程列表 Chapter13\Patch1 ;一个内存补丁程序 Chapter13\Patch2 ;一个内存补丁程序 Chapter13\Patch3 ;一个内存补丁程序 Chapter13\HideProcess9x ;Windows 9x下的进程隐藏 Chapter13\RemoteThreadDll ;用 DLL 注入的方法实现远程进程 Chapter13\RemoteThread ;不依靠任何外部文件实现远程进程 Chapter14\TopHandler ;使用筛选器处理异常 Chapter14\SEH01 ;最基本结构化异常处理例子 Chapter14\SEH02 ;改进后的结构化异常处理例子 Chapter14\Unwind ;异常处理中的展开操作例子 Chapter15\Ini ;使用 INI 文件 Chapter15\Reg ;操作注册表的例子 Chapter15\Associate ;操作注册表实现文件关联 Chapter16\TcpEcho ;实现 TCP 服务器端的简单例子 Chapter16\Chat-TCP ;用 TCP 协议实现的聊天室例子 Chapter17\PeInfo ;查看 PE 文件的基本信息 Chapter17\Import ;查看 PE 文件的导入表 Chapter17\Export ;查看 PE 文件的导出表 Chapter17\Resource ;查看 PE 文件的资源列表 Chapter17\Reloc ;查看 PE 文件的重定位信息 Chapter17\NoImport ;不使用导入表调用 API 函数 Chapter17\AddCode ;在 PE 文件上附加可执行代码的例子 Chapter18\OdbcSample ;用ODBC操作数据库的例子 Appendix A\EchoLine ;控制台输入输出的例子 Appendix B\MsgWindow01 ;消息机制试验 1 Appendix B\MsgWindow02 ;消息机制试验 2 Appendix B\MsgWindow03 ;消息机制试验 3 Appendix B\MsgWindow04 ;消息机制试验 4 Appendix C\BrowseFolder ;浏览目录对话框 6. 联系作者 虽然本书中所有的例子代码都已经在32位的Windows 98、Windows 2000、 Windows XP、Windows Vista和Windows 7下测试通过,但也有存在Bug的可能,如果 发现代码存在错误或者有其它问题,请告知作者,联系方法: E-mail:[email protected] 感谢您的支持! 作者:罗云彬

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值