签: model语句, 使用MASM, 内存模式, 指令集 Win32汇编

使用MASM01

让编程改变世界

Change the world by program


使用MASM

经过上一讲的准备工作,相信大家已经搭建好了 Win32 汇编的工作环境,并已经知道编译、链接一个程序的过程和原理了。

现在,我们让例子回归到经典:

#include <stdio.h>
int main(void)
{Printf(“Hello, worldn”);
} // 事实上想想,这不正是初生的婴儿?!

Win32汇编源程序的结构

麻雀虽小,五脏俱全。刚刚那个C语言的”Hello, world”程序包含了C语言中的最基本的格式。

在C语言的源程序中,我们不需要为堆栈段、数据段和代码段的定义而烦恼,编译器会自己解决。

回顾一下,在DOS 下的汇编这段代码会变成什么样?

在例子中我们看到,stack、data、code都找到了自己的小窝。

回归主题,在Win32 汇编语言下,小麻雀”Hello World” 又会变成什么样子呢?

是不是又不同了?但是,我们怎么就发觉Win32 汇编其实是前边两种形态的集大成者?!

接下来,小甲鱼带大家逐段来理解和接受这个新先的语言!

模式定义

程序的第一部分是模式和源程序格式的定义语句

.386
.model flat,stdcall
option casemap:none

这些指令定义了程序使用的指令集、工作模式和格式。

1)指定使用的指令集

.386语句是汇编语句的伪指令,类似的指令还有:.8086、.186、.286、.386/.386p、.486/.486p和.586/.586p等,用于告诉编译器在本程序中使用的指令集。

在DOS的汇编中默认使用的是8086指令集,那时候如果在源程序中写入80386所特有的指令或使用32位的寄存器就会报错。

Win32环境工作在80386及以上的处理器中,所以这一句.386是必不可少的。

另外,后面带p的伪指令则表示程序中可以使用特权指令,如:mov cr0,eax

这一类指令必须在特权级0上运行,如果只指定.386,那么使用普通的指令是可以的,编译时到这一句就会报错。

如果我们要写的程序是VxD等驱动程序,中间要用到特权指令,那么必须定义.386p,在应用程序级别的Win32编程中,程序都是运行在优先级3上,不会用到特权指令,只需定义.386就够了。

80486和Pentium处理器指令是80386处理器指令的超集,同样道理,如果程序中要用80486处理器或Pentium处理器的指令,则必须定义.486或.586。

另外,Intel公司的80×86系列处理器从Pentium MMX开始增加了MMX指令集,为了使用MMX指令,除了定义.586之外,还要加上一句.mmx伪指令:

.386
.mmx

2)model语句

.model语句在低版本的宏汇编中已经存在,用来定义程序工作的模式,它的使用方法是:.model 内存模式 [,语言模式] [,其他模式]内存模式的定义影响最后生成的可执行文件,可执行文件的规模从小到大,可以有很多种类型。

详见下表:

内存模式


Windows?程序运行在保护模式下,系统把每一个Win32应用程序都放到分开的虚拟地址空间中去运行。也就是说,每一个应用程序都拥有其相互独立的4GB地址空间。

对Win32程序来说,只有一种内存模式,即flat(平坦)模式,意思是内存是很平坦地从0延伸到4GB,再没有64KB段大小限制。对比一下DOS的Hello World和Win32的Hello World开始部分的不同,DOS程序中有这样语句
mov ax,data
mov ds,ax


意思是把数据段寄存器DS指向data数据段,data数据段在前面已经用data segment语句定义,只要DS不重新设置,那么从此以后指令中涉及的数据默认将从data数据段中取得。所以下面的语句是从data数据段取出szHello字符串的地址后再显示:
mov ah,9
mov dx,offset szHello
int 21h


纵观Win32汇编的源程序,没有一处可以找到ds或es等段寄存器的使用。因为所有的4GB空间用32位的寄存器全部都能访问到了,不必在头脑中随时记着当前使用的是哪个数据段,这就是平坦内存模式带来的好处。

如果定义了.model flat,MASM自动为各种段寄存器做了如下定义:ASSUME cs:FLAT, ds:FLAT, ss:FLAT, es:FLAT, fs:ERROR, gs:ERROR也就是说,CS,DS,SS和ES段全部使用平坦模式,FS和GS寄存默认不使用,这时若在源程序中使用FS或GS,在编译时会报错。

如果有必要使用它们,只需在使用前用下面的语句声明一下就可以了:assume fs:nothing, gs:nothing或者assume fs:flat, gs:flat

在Win32汇编中,.model语句中还应该指定语言模式,即子程序和调用方式。例子中用的是stdcall,它指出了调用子程序或Win32 API时参数传递的次序和堆栈平衡的方法。相对于stdcall,不同的语言类型还有C, SysCall, BASIC, FORTRAN 和PASCALL,虽然各种高级语言在调用子程序时都是使用堆栈来传递参数。Windows的API调用使用是的stdcall格式,所以在Win32汇编中没有选择,必须在.model中加上stdcall参数。 

话题:理解stdcall和cdecl

 

(1)_stdcall调用

_stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,被调函数自身在返回前清空堆栈。WIN32 Api都采用_stdcall调用方式。 

(2)_cdecl调用

_cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,传送参数的内存栈由调用者维护。_cedcl约定的函数只能被C/C++调用,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。

使用MASM02


让编程改变世界Change the world by program

 

模式定义

 

3)option语句

如例子中,我们定义了 option casemap:none 的意义是告诉编译器程序中的变量名和子程序名是否对大小写敏感。由于Win32 API 的API函数名称本质是区分大小写的,所以必须指定这个选项,否则调用API函数就会出现问题。

段的定义把上面的Win32的Hello World源程序中的语句归纳精简一下,再列在下面:
.386
.model flat,stdcall
option casemap:none
<一些include语句>
.data
<一些字符串、变量定义>
.code
<代码>
<开始标号>
<其他语句>
end 开始标号


模式定义中的模式、选项等定义并不会在编译好的可执行程序中产生什么东西,它们只是说明。而真正的数据和代码是定义在各个段中的,如上面的.data段和.code段,考虑到不同的数据类型,还可以有其他种类的数据段。

下面是包含全部段的源程序结构:
.386
.model flat,stdcall
option casemap:none
<一些include语句>
.stack [堆栈段的大小]
.data
<一些初始化过的变量定义>
.data
<一些没有初始化过的变量定义>
.const
<一些常量定义>
.code
<代码>
<开始标号>
<其他语句>
end 开始标号


.stack、.data、.data 、.const和.code是分段伪指令,Win32中实际上只有代码和数据之分,.data,.data 和.const这些都是指向数据段,.code是指向代码段。和DOS汇编不同,Win32汇编不必考虑堆栈,系统会为程序分配一个向下扩展的、足够大的段作为堆栈段,所以.stack段定义常常被忽略。 

解决之前提出的问题


前面我们不是说过Win32环境下不用段了吗?是的,这些“段”,实际上并不是DOS汇编中那种意义的段,而是内存的“分段”。上一个段的结束就是下一个段的开始,所有的分段,合起来,包括系统使用的地址空间,就组成了整个可以寻址的4GB空间。我们接着往下看会更加容易理解。

Win32汇编的内存管理使用了80386处理器的分页机制,每个页(4KB大小)可以自由指定属性,所以上一个4KB可能是代码,属性是可执行但不可写,下一个4KB就有可能是既可读也可写但不可执行的数据。

再下面呢?有可能是可读不可写也不可执行的数据。(主要就看你放的是什么东西……)嘿嘿,大家是否有点理解了?没关系,接着往下!Win32汇编源程序中“分段”的概念实际上是把不同类型的数据或代码归类,再放到不同属性的内存页(也就是不同的“分段”)中,这中间不涉及使用不同的段选择器。(仅仅是配合分页机制搞捣鼓~)虽然使用和DOS汇编同样的.code和.data语句来定义,意思可是完全不同了!至此,相信大家和小甲鱼一样清晰啦,感谢老师,感谢拉登,感谢嫦娥^_^ 

数据段


.data、.data 和.const定义的是数据段,分别对应不同方式的数据定义,在最后生成的可执行文件中也分别放在不同的节区(Section)中。(这个在我们讲解PE结构的时候会很细致描述)

程序中的数据定义一段可以归纳为3类:– 第一类是可读可写的已定义变量。– 第二类是可读可写的未定义变量。– 第三类数据是一些常量。 

可读可写的已定义变量

这些数据在源程序中已经被定义了初始值,而且在程序的执行中有可能被更改。如一些标志等,这些数据必须定义在.data段中,.data段是已初始化数据段。其中定义的数据是可读可写的,在程序装入完成的时候,这些值就已经在内存中了,.data段存放在可执行文件的_DATA节区内。 

可读可写的未定义变量

这些变量一般是当做缓冲区或者在程序执行后才开始使用的,这些数据可以定义在.data段中,也可以定义在.data 段中。但一般把它放到.data 段中。虽然定义在这两种段中都可以正常使用,但定义在.data 段中不会增大.exe文件的大小。

举例说明,如果要用到一个100KB的缓冲区,可以在数据段中定义:szBuffer db 100 * 1024 dup ( )

如果放在.data段中,编译器认为这些数据在程序装入时就必须有效,所以它在生成可执行文件的时候保留了所有的100KB的内容,即使它们是全零!如果程序其他部分的大小是50KB,那么最后的.exe文件就会是150KB大小,如果缓冲区定义为1MB,那么.exe文件会增大到1050KB。

.data 段则不同,其中的内容编译器会认为程序在开始执行后才会用到,所以在生成可执行文件的时候只保留了大小信息,不会为它浪费磁盘空间。和上面同样的情况下,即使缓冲区定义为1MB,可执行文件同样只有50KB!总之,.data 段是未初始化数据段,其中的数据也是可读可写的,但在可执行文件中不占空间,.data 段在可执行文件中存放在_BSS节区中。 

数据是一些常量


如一些要显示的字符串信息,它们在程序装入的时候也已经有效,但在整个执行过程中不需要修改,这些数据可以放在.const段中,.const段是常量段,它是可读不可写的。一般为了方便起见,在小程序中常常把常量一起定义到.data段中,而不另外定义一个.const段。在程序中如果不小心写了对.const段中的数据做写操作的指令,会引起保护错误,Windows会显示一个提示框并结束程序。

使用MASM03


让编程改变世界Change the world by program

 

代码段


.code段是代码段,所有的指令都必须写在代码段中,在可执行文件中,代码段是放在_TEXT节区(区块)中的。Win32环境中的数据段是不可执行的,只有代码段有可执行的属性。对于工作在特权级3的应用程序来说,.code段是不可写的,在编写DOS汇编程序的时候,我们可以为非作歹。如果企图在Win32汇编下做同样的事情,结果就是和上面同样 “非法操作”!

当然事物总有两面性,在Windows95下,在特权级0下运行的程序对所有的段都有读写的权利,包括代码段。另外,在优先级3下运行的程序也不是一定不能写代码段,代码段的属性是由可执行文件PE头部中的属性位决定的。通过编辑磁盘上的.exe文件,把代码段属性位改成可写,那么在程序中就允许修改自己的代码段。

一个典型的应用就是一些针对可执行文件的压缩软件和加壳软件,如Upx和PeCompact等。这些软件靠把代码段进行变换来达到解压缩和解密的目的,被处理过的可执行文件在执行时需要由解压代码来将代码段解压缩。这就需要写代码段,所以这些软件对可执行文件代码段的属性预先做修改。

为了带大家更好认识这些花花绿绿的“段”到底是什么回事,小甲鱼带大家看一张图…… 

程序结束和程序入口


在C语言源程序中,程序不必显式地指定程序由哪里开始执行,编译器已经约定好从main() 函数开始执行了。而在汇编程序中,并没有一个main函数,程序员可以指定从代码段的任何一个地方开始执行,这个地方由程序最后一句的end语句来指定:end [开始地址]

这句语句同时表示源程序结束,所有的代码必须在end语句之前。end start

上述语句指定程序从start这个标号开始执行。当然,start标号必须在程序的代码段中有所定义。但是,一个源程序不必非要指定入口标号,这时候可以把开始地址忽略不写,这种情况发生在编写多模块程序的单个模块的时候。

当分开写多个程序模块时,每个模块的源程序中也可以包括.data、.data、.const和.code段,结构就和上面的Win32 Hello World一样,只是其他模块最后的end语句必须不带开始地址。

当最后把多个模块链接在一起的时候,只能有一个主模块指定入口地址,在多个模块中指定入口地址或者没有一个模块指定了入口地址,链接程序都会报错。 

注释


注释是源程序中不可忽略的一部分,汇编源程序的注释以分号(;)开始,注释既可以在一行的头部,也可以在一行的中间,一行中所有在分号之后的字符全部当做注释处理,但在字符串的字义中包含的引号内的分号不当做是注释的开始。
;这里是注释
call _PrintChar             ;这里是注释
szChar    db    ‘Hello, world; ’,0dh,0ah

 

换行


当源程序的某一行过长,不利于阅读的时候,可以分行书写,分行的办法是在一行的最后用反斜杠()做换行符,如:
invoke MessageBox, NULL, offset szText, offset szCaption, MB_OK

可以写为:
invoke MessageBox,
NULL,                        ;父窗口句柄
offset szText,                      ;消息框中的文字
offset szCaption,          ;标题文字
MB_OK

 

调用API函数

 

首先,API 是什么?

答:Win32程序是构筑在Win32 API基础上的。在Win32 API中,包括了大量的函数、结构和消息等。它不仅为应用程序所调用,也是Windows自身的一部分,Windows自身的运行也调用这些API函数。在DOS下,操作系统的功能是通过各种软中断来实现的,如大家都知道int 21h是DOS中断,int 13h和int 10h是BIOS中的磁盘中断和视频中断。

当应用程序要引用系统功能时,要把相应的参数放在各个寄存器中再调用相应的中断,程序控制权转到中断中去执行,完成以后会通过iret中断返回指令回到应用程序中。

DOS汇编下的Hello World程序中有下列语句:
mov ah, 9
mov dx, offset szHello
int 21h


解释:这3条语句调用DOS系统模块中的屏幕显示功能,功能号放在ah中,9号功能表示屏幕显示,要输出到屏幕上的内容的地址放在dx中,然后去调用int 21h,字符串就会显示到屏幕上。这个例子说明了应用程序调用系统功能的一般过程。首先,系统提供功能模块并约定参数的定义方法,同时约定调用的方式,同时约定调用的方式,应用程序按照这个约定来调用系统功能。

在这里,ah中放功能号9,dx中放字符串地址就是约定的参数,int 21h是约定的调用方式。下面来看看这种方法的不便这处。首先,所有的功能号定义是冷冰冰的数字,int 21h的说明文档是这样的:int 21h说明文档

再进入09号功能看使用方法:
Print string (Func 09)
AH = 09h
DS:DX -> string terminated by “$”


这就是DOS时代汇编程序员都有一厚本《中断大全》的原因,因为所有的功能编号包括使用的参数定义仅从字面上看,是看不出一点头绪来的。另外,80×86系列处理器能处理的中断最多只能有256个,不同的系统服务程序使用了不同的中断号,这少得可怜的中断数量就显得太少了。结果到最后是中断挂中断,大家抢来抢去的,把好好的一个系统搞得像接力赛跑一样。API函数, invoke语句, win32汇编, 关于DLL Win32汇编 

使用MASM04

让编程改变世界

Change the world by program


调用API函数

习惯工作于DOS汇编的程序员同志都有一个愿望:如果说,能够以功能名称作为子程序名直接调用,他们愿意以生命中的十年寿命作为交换……

随着Win32的到来,他们的愿望实现了!这就是API函数,它事实上就是以一种新的方法代替了DOS下的中断。

与DOS中断相比,Win32的系统功能模块放在Windows的动态链接库(DLL)中。

DLL是一种Windows的可执行文件,采用的是和我们熟悉的.exe文件同样的PE(PortableExecutable)约定格式。

DLL文件的原理

关于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的参数和函数声明。

intMessageBox(
HWNDhWnd,           //handletoownerwindow
LPCTSTRlpText,      //textinmessagebox
LPCTSTRlpCaption,   //messageboxtitle
UINTuType           //messageboxstyle
);//注意,上边是用C语言表示!

Win32汇编语言012

标签:API函数, invoke语句, win32汇编, 关于DLL Win32汇编

API函数的参数

上述函数声明说明了MessageBox有4个参数,这些数据类型看起来很复杂。

但有一点是很重要的,对于汇编语言来说,Win32环境中的参数实际上只有一种类型,那就是一个32位的整数,所以这些HWND,LPCTSTR和UINT实际上就是汇编中的dword(doubleword,双字型,4个字节,两个字,32位)之所以定义为不同的模样,主要是用来说明了用途。

由于Windows是用C写成的,世界上的程序员好像也是用C语言的最多,所以Windows所有编程资料发布的格式也是C格式。

上面的声明用汇编的格式来表达就是:

MessageBoxProtohWnd:dword,lpText:dword,lpCaption:dword,uType:dword

在汇编中调用MessageBox函数的方法是:

pushuType
pushlpCaption
pushlpText
pushhWnd
callMessageBox

在源程序编译链接成可执行文件后,callMessageBox语句中的MessageBox会被换成一个地址,指向可执行文件中的导入表的一个索引(函数名或索引号)。

导入表中指向MessageBox函数的实际地址会在程序装入内存的时候,根据User32.dll在内存中的位置由Windows系统动态填入。

使用invoke语句

API是可以调用了,另一个烦人的问题又出现了,Win32的API动辄就是十几个参数,整个源程序一眼看上去基本上都是把参数压堆栈的push指令,参数的个数和顺序很容易搞错,由此引起的莫名其妙的错误源源不断,源程序的可读性看上去也很差。

如果写的时候少写了一句push指令,程序在编译和链接的时候都不会报错,但在执行的时候必定会崩溃,原因是堆栈对不齐了。

有木有解决的办法呢?那是必须得!最好是像C语言一样,能在同一句中打入所有的参数,并在参数使用错误的时候能够提示。

好消息又来了,Microsoft终于做了一件好事,在MASM中提供了一个伪指令实现了这个功能,那就是invoke伪指令,它的格式是:

invoke函数名[,参数1][,参数2]…[,参数n]

invokeMessageBox,NULL,offsetszText,offsetszCaption,MB_OK

注意,invoke并不是80386处理器的指令,而是一个MASM编译器的伪指令,在编译的时候它把上面的指令展开成我们需要的4个push指令和一个call指令,同时,进行参数数量的检查工作,如果带的参数数量和声明时的数量不符,编译器报错:errorA2137:toofewargumentstoINVOKE

编译时看到这样的错误报告,首先要检查的是有没有少写一个参数。

对于不带参数的API调用,invoke伪指令的参数检查功能可有可无。

所以既可以用callAPI_Name这样的语法,也可以用invokeAPI_Name这样的语法。

API函数的返回值

有的API函数有返回值,如MessageBox定义的返回值是int类型的数,返回值的类型对汇编程序来说也只有dword一种类型,它永远放在eax中。

如果要返回的内容不是一个eax所能容纳的,Win32API采用的方法一般是返回一个指针,或者在调用参数中提供一个缓冲区地址,干脆把数据直接返回到缓冲区中去。

Change the world by program


函数的声明

在调用API函数的时候,函数原型也必须预先声明,否则,编译器会不认这个函数。invoke伪指令也无法检查参数个数。声明函数的格式是:

函数名 proto [距离] [语言] [参数1]:数据类型, [参数2]:数据类型

句中的proto是函数声明的伪指令,距离可以是NEAR,FAR,NEAR16,NEAR32,FAR16或FAR32。

Win32中只有一个平坦的段,无所谓距离,所以在定义时是忽略的;语言类型就是.model那些类型,如果忽略,则使用.model定义的默认值。

对Win32汇编来说只存在dword类型的参数,所以所有参数的数据类型永远是dword。

另外对于编译器来说,它只关心参数的数量,参数的名称在这里是无用的,仅是为了可读性而设置的,可以省略掉。

所以下面两句消息框函数的定义实际上是一样的:

MessageBox Proto hWnd:dword, lpText:dword, lpCaption:dword, uType:dword

MessageBox Proto :dword, :dword, :dword, :dword

在Win32环境中,和字符串相关的API共有两类,分别对应两个字符集:一类是处理ANSI字符集(1B)的,另一类是处理Unicode字符集(2B)的。

前一类函数名字的尾部带一个A字符,处理Unicode的则带一个W字符。

我们比较熟悉的ANSI字符串是以NULL结尾的一串字符数组,每一个ANSI字符占一个字节宽。

对于欧洲语言体系,ANSI字符集已足够了,但对于有成千上万个不同字符的几种东方语言体系来说,Unicode字符集更有用。

MessageBox和显示字符串有关,同样它有两个版本,严格地说,系统中有两个定义:

MessageBoxA Proto hWnd:dword, lpText:dword, lpCaption:dword, uType:dword

MessageBoxB Proto hWnd:dword, lpText:dword, lpCaption:dword, uType:dword

虽然《Microsoft Win32 Programmer’s Reference》中只有一个MessageBox定义,但User32.dll中确确实实没有MessageBox,而只有MessageBoxA和MessageBoxW,那么为什么还是可以使用MessageBox呢?Follow

由于并不是每个Win32系统都支持W系统的API,例如在Windows 9x系列中,对Unicode是不支持的,很多的API只有ANSI版本,只有Windows NT系列才对Unicode完全支持。

为了编写在几个平台中通用的程序,一般应用程序都使用ANSI版本的API函数集。

这样的话,为了使程序更有移植性,在源程序中一般不直接指明使用Unicode还是ANSI版本,而是使用宏汇编中的条件汇编功能来统一替换。

如在源程序中使用MessageBox,但在头文件中定义:

if UNICODE
MessageBox    equ   <MessageBoxW>
else
MessageBox    equ   <MessageBoxA>
endif

所有涉及版本问题的API都可以按此方法定义,然后在源程序的头指定UNICODE=1或UNICODE=0,重新编译后就能产生不同的版本。

include语句

对于所有要用到的API函数,在程序的开始部分都必须预先声明,但这一个步骤显然是比较麻烦的,为了简化操作,可以采用各种语言通用的解决办法,就是把所有的声明预先放在一个文件中,在用到的时候再用include语句包含进来。

现在回到Win32 Hello World程序,这个程序用到了两个API函数:MessageBox和ExitProcess,它们分别在User32.dll和Kernel32.dll中。

MASM32工具包中已经包括了所有DLL的API函数声明列表,每个DLL对应<DLL名.inc>文件(这些文件就是存放对应的函数声明),在源程序中只要使用include语句包含进来就可以了:

include user32.inc

include kernel32.inc

当用到其他的API函数时,只需相应增加对应的include语句。

编译器对include语句的处理仅是简单地把这一行用指定的文件内容替换掉而而已。

include语句的语法是:

include 文件名

include <文件名>

当遇到要包括的文件名和MASM的关键字同名等可能会引起编译器混淆的情况时,可以用<>将文件名括起来。

includelib语句

在DOS汇编中,使用中断调用系统功能是不必声明的,处理器自己知道到中断向量表中去取中断地址。

在Win32汇编中使用API函数,程序必须知道调用的API函数存在于哪个DLL中。

否则,操作系统必须搜索系统中存在的所有DLL,并且无法处理不同DLL中的同名函数,这显然是不现实的。

所以,必须有个文件包括DLL库正确的定位信息,这个任务是由导入库来实现的。

在使用外部函数的时候,DOS下有函数库的概念,那时的函数库实际上是静态库,静态库是一组已经编写好的代码模块,在程序中可以自由引用。

在源程序编译成目标文件,最后要链接可执行文件的时候,由link程序从库中找出相应的函数代码,一起链接到最后的可执行文件中。

DOS下C语言的函数库就是典型的静态库。

库的出现为程序员节省了大量的开发时间,缺点就是每个可执行文件中都包括了要用到的相同函数的代码,占用了大量的磁盘空间,在执行的时候,这些代码同样重复占用了宝贵的内存。

Win32环境中,程序链接的时候仍然要使用函数库来定位函数信息,只不过由于函数代码放在DLL文件中。

库文件中只留有函数的定位信息和参数数目等简单信息,这种库文件叫做导入库。

一个DLL文件对应一个导入库,如User32.dll文件用于编程的导入库是User32.lib,MASM32工具包中包含了所有DLL的导入库。

为了告诉链接程序使用哪个导入库,使用的语句是:

includelib 库文件名

includelib <库文件名>

和include的用法一样,在要包括让编译器混淆的文件名时加括号。

Win32 Hello World程序用到的两个API函数MessageBox和ExitProcess分别在User32.dll和Kernel32.dll中,那么在源程序使用的相应语句为:

includelib user32.lib

includelib kernel32.lib

和include语句的处理不同,includelib不会把.lib文件插入到源程序中,它只是告诉链接器在链接的时候到指定的库文件中去找而已。

Dll文件中的函数没有包括声明,所以才需要将.inc文件插进去!

使用MASM06

让编程改变世界

Change the world by program


API参数中的等值定义(宏)

回过头来看显示消息框的语句:

invoke MessageBox, NULL, offset szText, offset szCaption, MB_OK

还是这个函数,不过这次我们关注的焦点有所改变:MB_OK

地球人都知道,MB_OK 就是使得程序弹出来的时候有个“确定”的选项!

我们这次来探索他背后的数字含义。

回顾一下原型:

int MessageBox(
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType );

在uType这个参数中使用了MB_OK,这个MB_OK是什么意思?小甲鱼带大家着手来查找文档!

实例演练

1. MB_OK 事实上是 1

2. 修改helloworld显示一个问号、一个确定按钮、一个取消按钮

3. 在以上基础上当按下确定的时候弹出另一个对话框,说”您刚刚按下了确定按钮”,按下取消的时候同样要弹一个对话框提醒

4. 事实上用 je, jmp 已经OUT 啦,在MASM下,我们可以用 if, elseif , else……

5. 探究 .if 背后的真相!

win32 009 masm32相关推荐

  1. Win10系统VS2022开发环境中(X86)Win32汇编(MASM32)环境配置和一些示例源码及解释

    抱歉,还是只能用米国的软件以及技术以及等等等等......,所以各位勿怪. 如果配置完成,在vs2022调试环境下正确编译运行后,可以看到如下画面: 在VS022中开发学习汇编必然很方便. 这里可以下 ...

  2. win32 009

    让编程改变世界 Change the world by program Win32可执行文件的开发过程 在DOS下生成一个可执行文件一般步骤: 在DOS下生成一个可执行文件一般步骤 如图,Win32的 ...

  3. Win32汇编环境搭建教程(MASM32 SDK)

    一.说明 常用的32位汇编编译器有微软的MASM.Borland的TASM和NASM. 编译器 开发者 优点 缺点 MASM 微软 微软自家软件和系统兼容性好:支持invoke/.if等伪指令将汇编变 ...

  4. win32 masm32 汇编学习 及 远程线程实例

    "门" 指向某个优先级高的程序所规定的入口点,所有优先级低的程序调用优先级高的程序只能通过门重定向 门:中断门,自陷门,任务门. masm32.zip copy D:\Progra ...

  5. 在RadASM中导入masm32项目和win32汇编odbc示例

    以下以罗云琳<Win32汇编程序设计>第18章odbc例子为例:在RadASM中导入该项目,并为该项目略添加功能: 一 masm32 项目导入radasm 先看一下radasm示例项目的结 ...

  6. win32 010 使用masm32

    使用MASM07 让编程改变世界 Change the world by program 标号.变量和数据结构 当程序中要跳转到另一位置时,需要有一个标识来指示位置,这就是标号. 通过在目的地址的前面 ...

  7. 各种汇编器masm masm32 fasm nasm yasm gas的区别

    masm MASM是微软公司开发的汇编开发环境,拥有可视化的开发界面,使开发人员不必再使用DOS环境进行汇编的开发,编译速度快,支持80x86汇编以及Win32Asm是Windows下开发汇编的利器. ...

  8. win32汇编使用win32 api实现字符串拷贝

    字符串拷贝,调用win32的lstrcpy函数:拷贝了以后用消息框显示一下: .386 .model flat, stdcall option casemap :noneinclude S:\masm ...

  9. Win32汇编基本编程框架

    Win32汇编编程框架如下: .386 .model flat,stdcall option casemap:none <一些include语句> .stack [堆栈段的大小] .dat ...

最新文章

  1. X5同层播放器应用实践
  2. Linux安全事件应急响应排查方法总结
  3. c# winform 关于给静态全局变量赋值的问题
  4. 关于PHP 使用 unset 销毁变量 但是 没有销毁内存的 记录
  5. 如何正确解码用户的“玄学需求”?
  6. python按日期排序_你如何在python中获得按创建日期排序的目录列表?
  7. Gradle技巧–显示buildscript依赖性
  8. 如何在Java中读取CSV文件-Iterator和Decorator的案例研究
  9. 计算机局域网有哪些硬件组成,局域网的硬件组成有哪些
  10. LCD1602液晶显示
  11. 微信小程序获取二维码中URL中带的参数
  12. django之路由分组,路由分发,FBV,CBV,ORM框架
  13. windows.old可以删除吗_C盘里的文件夹都有何用?可以删除吗?哪些可以删除?
  14. Windows 7 With SP1 MSDN版 含简体中文版
  15. 支持mysql8的客户端_mysql8 参考手册--mysql客户端帮助
  16. 未能正确加载“Microsoft.VisualStudio.Editor.Implementation.EditorPackage”
  17. bootstrap modal 关闭时右侧滚动条消失,页面左移的解决方法
  18. html中css鼠标手势样式,CSS鼠标手势
  19. 如何注册Google Voice账号(电话号码)
  20. Silverlight轻量级查询对比

热门文章

  1. oppo小游戏接入代码
  2. 虚拟主机做app服务器,虚拟主机做app服务器吗
  3. 面对困境最大的困难是走出第一步的勇气
  4. Matlab 色图控制
  5. PHP电商的sku,PHP 商品SKU表怎么设计
  6. 手机如何制作两寸照片
  7. 游戏体验之穿越火线手游
  8. 3d打印光固化好还是热固化好_光固化3D打印机的优势在哪里?
  9. 老照片修复的方法哪个好用?老照片修复技巧分享
  10. 网件路由器使用计算机mac,网件路由器怎么ip与mac绑定(2)