在LE文件中,代码和数据被存放在几类运行属性不同的段中。以下是一些可用的段类。
VXD FIRSTVXD第一个声明定义了VxD的名称,一个VxD的名称必须是全部大写的,我曾经试过用小写,结果VxD除了把自己载入内存外什么也不干。
SEGMENTS
_LPTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LDATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_DATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
CONST CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TLS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_BSS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LMGTABLE CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL
_LMSGDATA CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL
_IMSGTABLE CLASS 'MCODE' PRELOAD DISCARDABLE IOPL
_IMSGDATA CLASS 'MCODE' PRELOAD DISCARDABLE IOPL
_ITEXT CLASS 'ICODE' DISCARDABLE
_IDATA CLASS 'ICODE' DISCARDABLE
_PTEXT CLASS 'PCODE' NONDISCARDABLE
_PMSGTABLE CLASS 'MCODE' NONDISCARDABLE IOPL
_PMSGDATA CLASS 'MCODE' NONDISCARDABLE IOPL
_PDATA CLASS 'PDATA' NONDISCARDABLE SHARED
_STEXT CLASS 'SCODE' RESIDENT
_SDATA CLASS 'SCODE' RESIDENT
_DBOSTART CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBOCODE CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBODATA CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_16ICODE CLASS '16ICODE' PRELOAD DISCARDABLE
_RCODE CLASS 'RCODE'
EXPORTS
FIRSTVXD_DDB @1
_LTEXT | VxD_LOCKED_CODE_SEG |
_PTEXT | VxD_PAGEABLE_CODE_SEG |
_DBOCODE | VxD_DEBUG_ONLY_CODE_SEG |
_ITEXT | VxD_INIT_CODE_SEG |
_LDATA | VxD_LOCKED_DATA_SEG |
_IDATA | VxD_IDATA_SEG |
_PDATA | VxD_PAGEABLE_DATA_SEG |
_STEXT | VxD_STATIC_CODE_SEG |
_SDATA | VxD_STATIC_DATA_SEG |
_DBODATA | VxD_DEBUG_ONLY_DATA_SEG |
_16ICODE | VxD_16BIT_INIT_SEG |
_RCODE | VxD_REAL_INIT_SEG |
每个宏都有它相对应的结束宏,例如,如果你要在你的源文件中定义一个_LTEXT段,你应该这样写:
VxD_LOCKED_CODE_SEG(把你的代码写在这里)
VxD_LOCKED_CODE_ENDS
.386p
include vmm.incDECLARE_VIRTUAL_DEVICE FIRSTVXD,1,0, FIRSTVXD_Control, UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER
Begin_control_dispatch FIRSTVXD
End_control_dispatch FIRSTVXDend
这段源程序给人的第一印象就是:它并不像一个汇编源程序。那是因为它用了很多宏。让我们来分析一下源程序以便你能很快理解它。
.386p告诉编译器我们要使用包括CPU特权指令的80386指令系统。你也可以使用.486p或者.586p.
include vmm.inc你的每个VxD源程序都必须包含imm.inc,因为它包含了你在源程序里所要用到的宏的定义。你还可以根据需要包含其他的库文件。
DECLARE_VIRTUAL_DEVICE FIRSTVXD,1,0, FIRSTVXD_Control, UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER正如我们刚才说的,VMM通过VxD程序的设备描述块(DDB)来获取它所需要知道的关于VxD的所有信息。一个设备描述块是一个结构,它包含了许多关于VxD的重要信息,比如VxD的名字,它的设备ID,它的VxD服务函数入口(如果有的话),等等。你可以在imm.inc里查一查这个结构,它被定义为VxD_Desc_Block。你必须在.DEF 文件里导出这个结构。这个结构有22个数据,但是你只用填写其中的几个。然后vmm.inc包含的一个宏会为你初始化并填写这些数据。这个宏叫做DECLARE_VIRTUAL_DEVICE。它的格式如下:
Declare_Virtual_Device Name, MajorVer, MinorVer, CtrlProc, DeviceID, InitOrder, V86Proc, PMProc, RefData
你可以看到:VxD源程序中的标号是不区分大小写的,你可以用大写,小写或者混合起来用都可以。让我们来看一下Declare_virtual_device里的每一个参数。
你可以看到:VMM,
DEBUG和
DEBUGCMD是首先加载的VxD程序,然后是PERF和APM。初始化顺序值越低的VxD程序越先被加载。如果你的VxD程序在初始化时需要用到其他VxD程序提供的服务,那么你必须把初始化顺序的值设得比你所要调用的那个VxD程序的大,这样,当你的VxD程序加载时,你所要的VxD就已经在内存中为你准备好了。如果不想去管你的VxD的初始化顺序,就把这个参数填写为UNDEFINED_INIT_ORDER 。
Begin_control_dispatch FIRSTVXD这两个宏定义了设备控制函数,当VxD的控制消息发生时,VMM就调用这个函数。你必须填写设备控制函数名字的前半部分,在本例中,我们用的是 FIRSTVXD。这个宏会在你输入的前半部分后加上_Control作为设备控制函数的名字。这个名字一定要和你在Declare_virtual_device 宏中给参数CtrlProc填的名字一致。设备控制函数总是放在锁定段(VxD_LOCKED_CODE_SEG)内的。上面定义的设备控制函数什么也不干。你需要说明你的VxD程序要响应什么控制消息,以及处理这个消息的函数,你可以用Control_Dispatch宏来实现这一点。
End_control_dispatch FIRSTVXD
Control_Dispatch message, function例如,如果你的VxD程序只要处理Device_Init 消息,你的设备控制程序要这样写:
Begin_Control_Dispatch FIRSTVXDOnDeviceInit就是要处理Device_Init消息的函数的名字。你可以给你的函数取任何你想取的名字。
Control_Dispatch Device_Init, OnDeviceInit
End_Control_DispatchFIRSTVXD
ml -coff -c -Cx -DMASM6 -DBLD_COFF -DIS_32 firstvxd.asm
-coff 表明COFF数据格式
-c
只汇编,不调用连接程序来连接,这样我们就可以在调用link.exe的时候使用跟多的参数。
-Cx 保存公共,外部标记。
-D<text>
定义一个文本宏,例如,-DBLD_COFF定义了一个文本宏BLD_COFF,这个宏用来作为编译的条件。如果你有兴趣,你可以在库文件中查找BLD_COFF,自己亲眼看看它对汇编过程起什么作用。上面的命令行定义了三个文本宏:BLD_COFF,IS_32和MASM6。如果你对C编程熟悉的话,你会知道这些定义相当于完成以下功能:
#define BLD_COFFlink -vxd -def:firstvxd.def firstvxd.obj
#define IS_32
#define MASM6
-vxd
表明我们要根据obj文件来生成一个VxD文件。
-def:<.DEF file> 指定该VxD文件的模式定义文件。
我觉得用makefile很方便,如果你不喜欢用makefile,你也可以创建批处理文件来自动完成编译过程。我的makefile如下:
NAME=firstvxd
$(NAME).vxd:$(NAME).obj
link -vxd -def:$(NAME).def
$(NAME).obj
$(NAME).obj:$(NAME).asm
ml -coff -c -Cx -DMASM6
-DBLD_COFF -DIS_32 $(NAME).asm