您正在查看: 标签 可以得到 下的文章

C++ Builder 研究--通过端口获取IDE硬盘ID

#include

#include

#include

#include

#include

char GetAscii(unsigned int inData[], int offStart, int offEnd);

int main(void)

{

unsigned int diskData[256]; /
Disk data /

unsigned int offset; /
Disk data offset /

int loop;

int numDrv; /
Number of IDE hard drives /

union REGSregisters;

unsigned int biosCyl[2]; /
Cylinders, Heads, Sectors /

unsigned int biosHead[2];

unsigned int biosSec [2];

numDrv = peekb(0x40, 0x75); /
BIOS Data area, Number of Hard disks /

for (loop = 0; loop < numDrv; loop++)

{
while (inp(0x01f7) != 0x50); /
Wait for controller not busy /
outp(0x01f6, (loop == 0 ? 0xa0 : 0xb0)); /
Get first/second drive /
outp(0x01f7, 0xec); /
Get drive info data /
while (inp(0x1f7) != 0x58); /
Wait for data ready /
for (offset = 0; offset != 256; offset++) /
Read "sector" /
diskData[offset] = inpw(0x1f0);
/
Get BIOS drive info /
registers.h.ah = 0x08; /
Get drive info /
registers.h.dl = 0x80 + loop; /
Drive is 80H for Disk 0, 81H for Disk 1 /
int86(0x13, ?isters, ?isters);
if (!registers.x.cflag) /
All OK if carry not set /
{
biosHead[loop] = registers.h.dh + 1; /
Heads are from 0 /
biosSec[loop] = registers.h.cl & 0x3f; /
sec is bits 5 - 0 /
/
+1 because starts from 0 and +1 for FDISK leaving one out /
biosCyl[loop] = ((registers.h.cl & 0xc0) << 2) + registers.h.ch + 2;
} /
end of if /
printf("DRIVE %d:\n", loop);
printf("Model Number______________________: %s\n", GetAscii(diskData, 27, 46));
printf("Serial Number_____________________: %s\n", GetAscii(diskData, 10, 19));
printf("Controller Revision Number________: %s\n\n", GetAscii(diskData, 23, 26));
printf("Able to do Double Word Transfer___: %6s\n", (diskData[48] == 0 ? "No" : "Yes"));
printf("Controller type___________________: %04X\n", diskData[20]);
printf("Controller buffer size (bytes)____: %6u\n", diskData[21] * 512);
printf("Number of ECC bytes transferred___: %6u\n", diskData[22]);
printf("Number of sectors per interrupt___: %6u\n\n", diskData[47]);
printf("Hard Disk Reports\n");
printf("Number of Cylinders (Fixed)_______: %6u\n", diskData[1]);
printf("Number of Heads___________________: %6u\n", diskData[3]);
printf("Number of Sectors per Track_______: %6u\n\n", diskData[6]);
printf("BIOS Reports\n");
printf("Number of Cylinders_______________: %6u\n", biosCyl[loop]);
printf("Number of Heads___________________: %6u\n", biosHead[loop]);
printf("Number of Sectors per Track_______: %6u\n\n", biosSec[loop]);
printf("Press any key to continue...\n\n");
getch();

} /
end of for /

return 0;

} /
main() */

char GetAscii(unsigned int inData[], int offStart, int offEnd)

{

static char retVal[255];

int loop, loop1;

for (loop = offStart, loop1 = 0; loop <= offEnd; loop++)

{
retVal[loop1++] = (char )(inData[loop] / 256); /
Get High byte /
retVal[loop1++] = (char )(inData[loop] % 256); /
Get Low byte /

} /
end of for /

retVal[loop1] = '\0'; /
Make sure it ends in a NULL character /

return retVal;

} /
GetAscii() */

C++ Builder 研究--使用Windows虚拟设备驱动程序(VxD)之2

八、VxD初始化



VMM初始化一个VxD时做下列工作:

1、装载实模式初始化段并调用实模式初始化过程。该过程可以完成阻止装载VxD,阻止启动Windows,指定设备实例数据和在内存中选择页面给

设备专用的工作。

2、装载VxD其它段到32位平坦内存模式的保护模式内存,并丢弃实模式初始化段。

3、发送Sys_Critical_Init消息到设备控制过程。禁止硬件中断,所以VxD应该尽可能地用较少的时间完成自身初始化。

4、发送Device_Init消息到设备控制过程。允许硬件中断,所以必须准备让VxD管理来自设备的中断。

5、发送Init_Complete消息到设备控制过程。

6、丢弃初始化代码和数据段,释放其它被使用的内存。VxD不能在处理完Init_Complete消息以后试图存取这些段中的过程和数据。

在初始化过程中的任何时刻,VxD都可以设置进位标志返回到VMM以阻止装载VxD。部分VMM服务,例如初始化信息服务只在初始化过程中有效。



九、实模式初始化



任何静态设备驱动程序都可以提供实模式初始化过程以在Windows切换到保护模式之前执行初始化任务。VMM装载VxD时调用该过程,该过程检查

有关的Windows环境,包括注册表和初始化文件中的有关设置以判断是否应该装载该VxD。该过程也可以给Windows返回信息以为每个虚拟机的实

例指定物理内存页保留给设备专用和数据项地址。要获得关于实模式初始化的更多信息,请参阅相关资料。



十、VxD服务



VxD可以提供服务功能(函数或过程——译者注)给VMM和其它VxD使用。这些服务让其它VxD可以直接访问该VxD的特征,允许测试和修改该VxD

的功能和能力。

VxD不能和Windows DLL一样引出函数,代之的是VMM通过INT 20H提供到VxD服务的动态链接,该中断处理过程使用服务编号判断支持服务的

VxD,该中断处理过程也使用服务编号查询在VxD服务表中的服务地址。

下面的内容讲述怎样在VxD中定义服务,声明VxD服务表以及从一个VxD向另一个VxD中引入服务。



1、定义服务



VxD使用BeginProc和EndProc宏以及Service和Async_Service选项定义服务。宏标记服务过程代码的开始和结束,选项标识该过程是一个服务。

下面的实例给出了VSAMPLED_Get_Version服务的定义:

BeginProc VSAMPLED_Get_Version, Service

mov ax, 030Ah

clc

ret

EndProc VSAMPLED_Get_Version

Async_Service选项标识该服务可以被异步调用,也就是说在处理中断的过程中调用。异步服务必须是可重入的,而且不能调用VMM和不是异步服

务的VxD服务。

VMM和标准VxD对服务使用两种调用约定:基于寄存器的调用约定和基于32位c语言的调用约定。这两种调用约定有不同的服务名格式,参数传递

和返回值方法以及寄存器保护。

对于基于寄存器的服务,服务名不应该以下划线()开头,所有的参数通过寄存器传递,结果也通过寄存器返回,服务保护所有不显式用于返回

值的寄存器。

对于基于c语言的服务,服务名必须以下划线(
)开头,所有的参数通过堆栈中的32位值传递,结果(如果存在)通过EAX寄存器(32位值)

或者EAX和EDX寄存器(64位值)返回,服务保护EBX、ES、FS和GS寄存器以及ESI和EDI寄存器,只有标志寄存器和EAX、EBX、EDX寄存器被修改。



2、声明服务



VxD使用Begin_Service_Table和End_Service_Table宏声明服务。宏标记包含服务名和可选的包含服务的段名的列表的开始和结束。声明必须建

立在进行VxD定义的文件中(也就是说在包含Declare_Virtual_Device宏的文件中),而且必须先进行设备指定符号的定义。下面的实例给出了

一个实例VxD——VSAMPLED的服务表声明:

Create_VSAMPLED_Service_Table equ 1



Begin_Service_Table VSAMPLED

VSAMPELD_Service VSAMPLED_Get_Version, Local

VSAMPLED_Service VSAMPLED_Service_1

VSAMPLED_Service VSAMPLED_Service_2, VxD_ICODE

End_Service_Table VSAMPLED

上例中,Create_VSAMPLED_Service_Table符号在紧接服务表声明之前定义,指定Begin_Service_Table宏为VSAMPLED创建服务表。声明开始以

后,VSAMPLED_Service宏定义实际服务,这个宏是Begin_Service_Table宏创建的,只在服务表声明中有效。每个服务名必须与服务定义名,也就

是BeginProc宏给出的名字完全符合。

当声明服务时,如果一个服务定义在包含服务表声明的文件中,必须在服务名后使用LOCAL选项,也就是说如果不使用LOCAL选项,服务表自动声

明一个服务是外部服务。上例中,VSAMPLED_Get_Version服务定义在包含服务表声明的文件中。

与之相似,如果一个服务不是定义在VxD_CODE段中,必须在服务名后注明段名。上例中,VSAMPLED_Service_2服务定义在VxD_ICODE段中。

服务声明的顺序是很重要的。VxD中第一个被声明的服务必须是Get_Version服务(该服务清除进位标志并在AX寄存器中返回VxD版本

号),任何加入VxD的新服务必须定义在服务表的末尾(或者定义在服务表中显式保留的空间中)。由于VMM依靠服务在服务表中的顺序正确链接

服务,在服务表中间插入一个新服务需要VxD使用的所有VxD服务被重建。

为了方便,服务表声明应该放在一个包含文件中,使得其它VxD可以通过包含该文件引入服务而不需要重新声明。



3、引入服务



一个VxD可以通过包含另一个VxD的服务表声明引入另一个VxD的服务,在这种情况下,设备指定符号的定义不能先于该服务表声明(参见上一节

的讲述)。例如:VSAMPLED.INC文件包含VSAMPLED服务的服务表声明,一个包含该文件的VxD可以调用这些服务。VxD使用VMMcall宏调用VMM服

务,使用VxDcall宏调用VxD服务。

由于计算机配置不同,一个VxD可能会在Windows启动时装载失败,这说明使用其它VxD提供的服务的VxD必须检验其它VxD服务在调用它们之前

是否有效。为了检验服务,调用服务的VxD必须尝试调用提供服务的VxD的Get_Version功能,如果VxD没有装载,VMM会设置进位标志并在AX寄

存器中返回0。



十一、VxD API过程



一个VxD提供V86模式和保护模式API过程以允许在一个虚拟机中运行的应用程序和其它软件访问该VxD的特征。如果要使这些可选的过程有效,

VxD必须将它们定义为Declare_Virtual_Device宏的参数,如果没有定义,VMM认为该VxD没有API过程。

在一个虚拟机中运行的应用程序或者其它软件通过设置BX寄存器为VxD标识并调用获取设备入口地址功能(INT 2FH 1684H功能)获取特定的虚

拟机的API过程的入口地址,VMM返回该地址使得应用程序可以间接调用该API过程。

当一个应用程序调用该入口地址时,VMM保存该应用程序的寄存器并调用VxD相应的API过程,保存当前虚拟机的句柄到BX寄存器中并保存

Client_Reg_Struc结构地址到EBP寄存器中。API过程必须检测客户寄存器的值(使用Client_Reg_Struc结构)以判断运行的API调用。

按照常规,大多数API过程使用AH寄存器指定主功能号,使用AL寄存器指定次功能号,其它客户寄存器用于附加参数。API过程通过修改客户寄

存器返回值,API过程可以修改EAX、EBX、ECX、EDX、ESI和EDI寄存器。

下面的实例给出了一个实例API过程——VSAMPLED_API_Get_Version:

BeginProc VSAMPLED_API_Get_Version

movzx eax, [ebp.Client_AX] ;取功能号

or eax, eax

jnz Undefined



Get_Version:

mov [ebp.Client_AX], 030AH ;在客户寄存器AX中返回值

and [ebp.Client_Flags], NOT CF_Mask ;清除进位标志

ret



Undefined:

or [ebp.Client_Flags], CF_Mask ;设置进位标志

ret

EndProc VSAMPLED_API_Get_Version



十二、VxD INT 2FH功能



VxD可以通过为INT 2FH中断安装回调过程提供INT 2FH功能。INT 2FH功能允许在一个虚拟机中运行的应用程序和其它软件在VxD不提供API过

程的情况下访问VxD,例如,标准虚拟显示设备支持与Windows显示驱动程序通信的INT 2FH功能集合。

Windows安装自己的INT 2FH中断处理程序支持各种功能,以允许MS-DOS 设备驱动程序和TSR在Windows启动或者虚拟机运行过程中执行特定动

作。要获得有关这些功能的更多信息,请参阅有关资料。



十三、建立一个VxD



应该通过进行下列步骤来建立一个VxD:

1、创建VxD源文件并用32位平坦模式汇编器MASM.EXE(MASM 6.11以上——译者注)汇编源文件。

2、创建模块定义文件(DEF文件)并用32位平坦模式连接器LINK.EXE连接目标文件。按照常规,结果可执行文件应该有与VxD相同的文件名,文

件扩展名为VXD。

3、用MAPSYM.EXE为可执行文件创建调试信息。

VxD并不与Windows动态链接库兼容,基于Windows的应用程序并不能直接装载和使用VxD,然而,基于win32的应用程序可以通过使用CreateFile

和DeviceIoControl函数装载动态装载的VxD和与之相互作用。VxD模块定义文件有以下格式:

LIBRARY VSAMPLED



DESCRIPTION 'VSAMPLED Device (Version 4.0)'



EXETYPE DEV386



SEGMENTS

_LTEXT PRELOAD NONDISCARDABLE

_LDATA PRELOAD NONDISCARDABLE

_ITEXT CLASS 'ICODE' DISCARDABLE

_IDATA CLASS 'ICODE' DISCARDABLE

_TEXT CLASS 'PCODE' NONDISCARDABLE

_DATA CLASS 'PCODE' NONDISCARDABLE



EXPORTS

VSAMPLED_DDB @1

LIBRARY语句必须指定一个与在已知设备描述块(DDB)中相同的VxD名字,EXPORT语句必须指定一个DDB的名字,在任何情况下,DDB引出序号都是1。

C++ Builder 研究--使用Windows虚拟设备驱动程序(VxD)之1

一、VxD介绍



本文介绍VxD的作用。如果Windows 95提供的标准VxD对你的硬件或者软件不能提供100%的兼容支持,你需要为你的硬件或者软件(包括16位

和32位软件)创建VxD。如果你想让Windows用户使用你的硬件或者软件的某些新特征,你也可以创建VxD。

本文介绍如何写VxD,讲述了创建VxD所需的结构、过程和调用,还给出了创建和测试VxD的步骤。VxD可以是静态的或者动态装载的,下面的内

容主要讲述静态VxD,不过关于格式和功能的内容两者都适用。



二、什么是VxD



VxD是一个管理例如硬件设备或者已安装软件等系统资源的32位可执行程序,使得几个应用程序可以同时使用这些资源。Windows通过使用VxD允

许基于Windows的应用程序实现多任务。VxD在与Windows的连接工作中处理中断,并在不影响其它应用程序的执行的情况下为特定的应用程序执

行I/O操作。大多数VxD管理硬件设备,也有一些VxD管理或代替与之相关的软件,例如ROM BIOS例程。VxD可以包含必须在相应设备上执行的设备相关代码,也可以依靠其它软件去执行这些对设备的操作。任何情况下,VxD都会为每一个应用程序保留该设备状态的记录,保证无论何时一个应用程序继续执行该设备均处于正确状态。

一些VxD仅仅管理已安装软件,例如MS-DOS设备驱动程序或者TSR程序,这样的VxD通常包含仿真这些软件或者保护这些软件用于正在运行的应用

程序的数据的代码。VxD有时还用于提高已安装软件的性能,Intel兼容CPU执行32位的VxD比执行16位的MS-DOS设备驱动程序或者TSR程序有更高的效率。



三、标准VxD



Windows包括多种VxD,用于支持公共硬件设备和可安装软件。在某些情况下,可能需要修改VxD以提供新的特征或者支持非标准硬件。

Windows提供许多不准备修改,但能够辅助支持其他VxD的VxD。例如:许多VxD使用V86内存管理器(V86MMGR)和虚拟可编程中断控制器设备

(VPICD)提供的功能保存V86模式内存和允许硬件中断请求。要取得开发VxD的帮助,Windows 95设备驱动程序开发工具包(DDK)包括了大量可用设备驱动程序的源代码。



四、创建VxD



你可以通过修改VxD例子程序或者自己手工创建来创建VxD。你可以用汇编语言来写VxD,也可以用高级语言(例如c语言)来写一部分VxD。

要创建一个VxD,需要以下步骤:



1、阅读硬件手册中关于描述这种型号硬件VxD的章节。

2、写出所需的控制过程,VxD服务和API函数。

3、建立为VxD标识适当模块名的模块定义文件,并引出需要的设备描述块。

4、汇编连接VxD。

5、用调试版Windows 95测试VxD,要获得更多关于调试VxD的信息,请参阅相关资料(可以用Soft-ICE调试——译者注)。

6、为VxD和相关文件建立安装文件(INF文件),通过修改注册信息和向Windows的SYSTEM目录和相关目录拷贝文件来安装VxD。

7、建立最终发行软件包。



五、你需要怎样开始



本文假定你是一位有经验的汇编语言程序员,而且熟悉Intel兼容处理器的指令集和系统结构。在某些特殊情况下,你还应该懂得下列内容:



保护模式和虚拟8086(即V86——译者注)模式。

平坦内存模式

中断和异常处理

保护和特权级

段和页式内存管理以及错误处理

输入和输出保护以及错误处理



本文也假定你已经熟练了解专门硬件的特征和相应的ROM BIOS例程以及其他可安装软件。



六、写一个VxD



许多情况下,写一个VxD用来代替一个由Windows 95提供的标准VxD。然而,大多数情况下,写一个VxD是用来支持新硬件设备或者软件的,在这

种情况下,通常手工创建一个VxD比修改已经存在的VxD要容易,原因是大多数VxD都是设备相关的。然而,已存在VxD的源代码需要尽可能地仔

细分析,原因是它们可能包含通用的格式和结构,以及说明怎样使用VMM和VxD服务去实现有用的功能。



写一个VxD需要下列步骤:

1、建立包含VxD各个段,VxD声明,设备控制过程,处理系统控制消息过程的基本部分和API过程的基本部分的VxD框架。

2、加入实模式初始化过程(可选)。

3、完成处理初始化消息的过程。这些过程应该能够初始化控制块,分配全局内存以及安装中断、I/O捕获和页错误回调过程。

4、完成处理不同中断和错误的回调过程。

5、为服务加入服务表定义和声明(可选)。

6、完成API过程(可选)

7、完成处理系统控制消息的过程以建立和删除虚拟机。



在写一个VxD的过程中,你可以安装该VxD并在调试器控制下运行Windows,在该VxD中设置断点监视该VxD管理的中断,这样可以帮助你查明该VxD是否正确工作。



1、VxD段



VxD可以包含下面5个段的一些组合:

1、VxD_CODE段:保护模式代码段(必须)。该段包含VxD系统控制过程、回调过程、服务和API过程。该段用宏VxD_CODE_SEG和VxD_CODE_ENDS

定义开始和结束,也可命名为_LTEXT。

2、VxD_DATA段:保护模式数据段(必须)。该段包括设备描述表、服务表和部分VxD全局数据。该段用宏VxD_DATA_SEG和VxD_DATA_ENDS定义开

始和结束,也可命名为_LDATA。

3、VxD_ICODE段:保护模式初始化代码段(可选)。该段一般包括只在VxD初始化过程中使用的过程和服务,VMM在Init_Complete消息发生后丢弃

此段。该段用宏VxD_ICODE_SEG和VxD_ICODE_ENDS定义开始和结束,也可命名为_ITEXT。

4、VxD_IDATA段:保护模式初始化数据段(可选)。该段一般包括初始化过程和服务使用的数据,VMM在Init_Complete消息发生后丢弃此段。该

段用宏VxD_IDATA_SEG和VxD_IDATA_ENDS定义开始和结束,也可命名为_IDATA。

5、VxD_REAL_INIT段:实模式初始化段(可选)。该段包含实模式初始化过程和数据,VMM在装载VxD其它部分之前调用此过程,过程返回后丢弃

此段,该段用宏VxD_REAL_INIT_SEG和VxD_REAL_INIT_ENDS定义开始和结束,也可命名为_RTEXT。



除实模式初始化段以外,所有代码和数据段均为32位平坦内存模式的保护模式段,这就是说定义在保护模式段中的过程和数据均为32位的偏移

量。当VMM装载VxD时,按照VxD在内存中的实际位置修正所有的偏移量。因此,在保护模式段中使用普通OFFSET命令(伪操作,下同——译者注)

处应该使用OFFSET32宏,OFFSET32宏定义的偏移量为连接器确定了正确的偏移量修正信息。

VxD不能改变CS、DS、ES和SS段寄存器,VxD能够使用FS和GS段寄存器。



2、保护模式指令



VxD的源程序文件必须以.386p命令开始,以通知汇编器允许保护模式指令。虽然VxD工作在0特权级,但也不应该用保护模式指令去修改CPU的

运行,例如修改全局描述符(选择子——译者注)或中断描述符以及修改任务状态段或寄存器,这样做可能会对Windows运行有不利影响。唯一的

例外情况是当该VxD为虚拟数学协处理器设备驱动程序(VMCPD),允许修改CR0寄存器中的80387位。



3、包含(Include)文件



包含文件定义了VxD需要的宏、结构、符号和服务表,用于声明段和过程以及使用VMM和其它VxD服务。下面是每个包含文件包含的公共服务定义、

宏和符号定义列表:

1、VMM.INC:包含所有的VMM服务以及所需的宏和符号,例如Declare_Virtual_Device和VMMCall。

2、DEBUG.INC:包含在调试终端上输出信息和执行各种数据检查的宏。这些宏的功能由定义了调试符号的VxD在汇编时该文件生成的代码实现。

3、VPICD.INC:包含为虚拟可编程中断控制器设备(VPICD)定义的所有服务、宏和符号。VPICD处理所有的中断,所以许多VxD需要VPICD服务。

4、shell.INC:包含虚拟外壳设备提供的公共服务的定义。虚拟外壳设备提供对例如MessageBox这样的Windows函数的调用,可以让VxD显示对话框。



4、VxD声明



每一个VxD都要声明一个名称、一个版本号、一个初始化顺序和一个设备控制过程,许多虚拟设备驱动程序还声明一个设备标识和一些API过程。

VxD一般使用Declare_Virtual_Device宏来实现这些声明,例如:

Declare_Virtual_Device VSAMPLED, 4, 0, VSAMPLED_Control, \

VSAMPLED_Device_ID, VSAMPLED_Init_Order, \

VSAMPLED_V86_API_Handler, \

VSAMPLED_PM_API_Handler

本例声明了一个VxD实例——VSAMPLED V4.0,在对应的源文件必须定义名字为VSAMPLED_Control的设备控制过程。符号VSAMPLED_Device_ID和

VSAMPLED_Init_Order说明非标准VxD的标识和初始化顺序,该VxD支持V86模式和保护模式API过程。

VMM用宏定义的信息来初始化VxD并发送系统控制消息给VxD,并且允许MS-DOS应用程序、设备驱动程序和TSR调用VxD。为了使VMM存取这些信

息,相应的宏建立一个设备描述块(DDB)并将其保存在保护模式数据段中(DDB的格式与VxD_Desc_Block结构相同),宏为DDB建立了一个必须

在VxD连接时被显式引出的标号。在上例中,DDB的名称是VSAMPLED_DDB。



5、VxD标识(ID)



一个VxD提供一个VxD标识,以区别于其它VxD。VMM动态连接例程使用VxD标识为合适的VxD连接服务调用,如果VxD提供服务或者提供V86模式和

保护模式API过程以及其它需要唯一标识的情况,VxD就必须有唯一标识。虽然标准VxD使用预定义VxD标识(符号定义在VMM.INC文件中),支持

新设备和新软件接口的VxD还是必须全部有新标识。为了防止与其他新VxD冲突,Microsoft通过请求和注册标识来保证没有其它厂商使用自己的VxD

的标识,Microsoft保留0—01FFH之间的所有VxD标识自己使用。不提供服务或者API过程,或者不需要唯一标识的VxD应该使用Undefined_Device_ID符号来定义VxD标识。



6、初始化顺序



每一个VxD都有一个用于指定VMM应该何时初始化该VxD的初始化顺序值,VMM按照该值从小到大的顺序初始化虚拟机(VM——译者注)。如果两个或

者两个以上的VxD有相同的值,VMM会按照SYSTEM.INI文件中出现的顺序来初始化,但指定顺序是没有保证的。

对于需要调用其它VxD服务或者需要在其它VxD之前拦截中断的VxD,初始化顺序是很重要的。如果一个VxD需要在标准VxD之前或者之后初始化,

它的初始化顺序值应该通过在标准VxD预定义的初始化顺序符号(在VMM.INC文件中定义)上加上或者减去一个小数值来创建。

如果一个VxD不需要初始化顺序值,应该使用Undefined_Init_Order符号代替初始化顺序值。



7、设备控制过程



每一个VxD都有一个设备控制过程,VMM通过调用此过程给VxD发送VxD系统控制消息。系统控制消息指导VxD完成动作,例如自身初始化或者通

知VxD虚拟机的变化(例如创建虚拟机)等。大多数VxD通过使用Begin_Control_Dispatch、Control_Dispatch和End_Control_Dispatch

宏来定义设备控制过程,例如:

Begin_Control_Dispatch VSAMPLED

Control_Dispatch Sys_Critical_Init, VSAMPLED_Crit_Init

Control_Dispatch Device_Init, VSAMPLED_Device_Init

Control_Dispatch Sys_Critical_Exit, VSAMPLED_Crit_Exit

End_Control_Dispatch VSAMPLED

上例中,宏创建了一个名字为VSAMPLED_Control的设备控制过程,并生成了检查Sys_Critical_Init、Device_Init和Sys_Critical_Exit消息

的指令。当这些消息发送到该过程时,该过程通过控制相应的过程(例如SAMPLED_Crit_Init)来处理消息,这些消息处理过程必须在VxD中定义。



七、系统控制消息



VMM发送系统控制消息给VxD,以通知VxD影响系统和虚拟机的变化。大多数VxD需要跟踪虚拟机的创建和状态,所以无论何时创建、初始化或者

终止虚拟机,VMM都会发送消息给VxD。VMM也会在执行焦点移动到一个虚拟机或者从一个虚拟机移走时,以及虚拟外壳设备需要给用户显示一个消

息框时发送消息给VxD。

下面是公共消息和VxD应该怎样处理这些消息的方法列表:

Begin_Message_Mode消息:当虚拟外壳设备需要给用户显示一个消息框但不能使用系统虚拟机和Windows函数时VxD收到此消息。虚拟键盘、鼠标

和显示设备保存当前状态,允许任何消息模式服务并为消息模式处理初始化相应设备。

Create_VM消息:这是当一个新的虚拟机被创建时VxD收到的第一条消息。VxD应该初始化与虚拟机有关的数据,特别是控制块。

Debug_Query消息:VxD从WDEB386调试器收到此消息。VxD可以显示调试列表和从调试终端读取用户命令。

Destroy_VM消息:这是VxD收到的第三条虚拟机终止消息。Simulate_Int和Exec_Int服务对获得此消息的虚拟机不再有效。

Device_Init消息:这是VxD收到的第二条消息。允许中断,大多数VxD分配和拷贝初始状态到系统虚拟机控制块中的设备指定部分,安装中断回

调函数和I/O保护异常以及指定实例数据。这时,Simulate_Int和Exec_Int服务变得有效。

End_Message_Mode消息:当虚拟外壳设备不再需要显示消息框时VxD收到此消息。虚拟键盘、鼠标和显示器设备恢复获得此消息的虚拟机以前保存

的状态,禁止任何消息模式服务。

Init_Complete消息:这是VxD收到的第三条消息,也是最后一条与系统初始化有关的消息。虽然大多数VxD都不处理此消息,但使用V86内存的

VxD应该在此消息返回前定位和申请内存。当VxD返回此消息时,VMM丢弃VxD的初始化代码和数据段。

Query_Destory消息:当虚拟外壳设备需要决定是否可以删除虚拟机时VxD收到此消息。VxD可以通过设置进位标志(CF——译者注)返回以阻止虚

拟机被删除,在这种情况下VxD应该使用shell_Message服务来通知用户问题。

Reboot_Processor消息:当用户试图重新启动计算机时VxD收到此消息。可以重新启动计算机的VxD,例如键盘设备应该完成该工作。

Set_Device_Focus消息:当执行焦点从一个虚拟机移动到另一个虚拟机时VxD收到此消息。VxD恢复硬件设备到与虚拟机有关的状态。如果VxD使

用I/O捕获管理没有执行焦点时的虚拟机,VxD应该尽可能地移走太多的I/O捕获,使虚拟机运行尽可能地快一些。

Sys_Critical_Exit消息:这是VxD收到的最后一条消息。禁止中断,Simualte_Int和Exec_Int服务不再有效。VxD应该复位与之相关的硬件

设备,保证能够无问题地返回到实模式。

Sys_Critical_Init消息:这是VxD收到的第一条消息。中断仍然不被允许,所以VxD应该尽可能快地完成任务。大多数VxD完成下列任务:

安装和初始化需要支持来自设备的硬件中断和来自VMM或者其它VxD的软件中断的任何函数,为设备提供需要单独使用V86模式内存页的申请,例

如虚拟显示设备申请显示内存。初始化VxD服务需要的任何数据,这通常包括读取SYSTEM.INI文件中的设置。当处理此消息时,Simualte_Int和Exec_Int服务必须不被使用。

Sys_VM_Init消息:在Init_Complete消息之后VxD收到此消息。VxD应该初始化系统虚拟机的硬件和软件状态。如果VxD设置进位标志返回,VMM

终止所有进程并退出Windows。

Sys_VM_Terminate消息:这是VxD收到的第一条系统虚拟机终止消息。VxD可以开始为虚拟机终止做准备。Simulate_Int和Exec_Int服务有效,系

统虚拟机总是最后一个被终止的虚拟机。

System_Exit消息:这是当系统终止时VxD收到的第一条消息。VMM在发送Sys_VM_Terminate消息之后发送此消息,允许中断,但Simualte_Int

和Exec_Int服务不再有效。如果此消息来自一个致命错误的结果,VxD可以通过修改系统虚拟机的内存以恢复系统状态,使得Windows能够终止而

不死机。

VM_Critical_Init消息:这是当一个新的VxD(虚拟机——译者注)被创建时VxD收到的第二条消息。VxD可以通过设置进位标志返回以阻止虚拟

机被建立。禁止中断,Simualte_Int和Exec_Int服务不再有效。

VM_Init消息:这是当一个新的VxD被创建时VxD收到的第三条消息。VxD应该初始化虚拟机的硬件和软件状态,例如虚拟显示设备执行INT 10H功

能设置初始显示模式。

VM_Not_Execute消息:这是VxD收到的第二条虚拟机终止消息(如果虚拟机已经被虚拟外壳设备删除,这是收到的第一条消息)。VxD可以通过检查

EDX寄存器中的标志来查明终止原因。Simulate_Int和Exec_Int服务对获得此消息的虚拟机不再有效。

VM_Resume消息:当虚拟机的执行被恢复时VxD收到此消息,例如切换到前台时。VxD应该锁定任何资源和为虚拟机重新开始准备内部结构。如果

VxD设置进位标志返回,VMM不恢复执行虚拟机。

VM_Suspend消息:当虚拟机已经被挂起时VxD收到此消息,例如切换到后台时。VxD应该解锁任何与虚拟机有关的资源。

VM_Terminate消息:这是VxD收到的第一条虚拟机终止消息。VxD可以开始准备虚拟机的终止。Simulate_Int和Exec_Int服务有效。

C++ Builder 研究--如何在C++Builder中检测硬件

 在我们编写的程序中常常要和硬件打交道,那么如何在程序中确定系统中是否有该 设备,它的运行状态又是怎样的呢?对于初学者来说,这个问题常常不好解决,其实只需 简单地利用几个API函数,硬件的问题并不神秘。下面就让我们一起看看在C++ Build er中是如何检测硬件的。

  1. 检测CPU的型号

  先让我们从最简单的做起,看一看自己的CPU型号。首先,在C++ Builder中画 出图1所示的窗体,在下面的几个例子中我们将一直使用这个窗体作示范,它包括一个用 来激活测试的Button和一个用来显示结果的Memo。我们可以用GetSystemInfo这个API获 得CPU的型号。将下列代码添加到Button的Click事件里就可以了:

  void __fastcall TForm1::Button1Click(TObject Sender)

  {

  //获得CPU型号

  SYSTEM_INFO systeminfo;

  GetSystemInfo (&systeminfo);

  Memo1->Lines->Add("您的CPU类型是:"+String( systeminfo.dwProcessorTy pe ));

  }

  运行它,点击Test试试,CPU型号出来了吧!

  2.检测内存状态

  获得内存状态的方法和CPU型号差不多,只是他用到的是另外一个API:GlobalMe moryStatus。其中,成员dwTotalPhys用来获得物理内存总量,而dwAvailPhys顾名思义 是有效物理内存的意思。我们只要把下面几行代码加到上面程序的后面就可以了(不用重做,下同):

  //获得内存状态

  MEMORYSTATUS memory;

  memory.dwLength =sizeof(memory); //初始化

  memory.dwLength =sizeof(memory); //初始化

  GlobalMemoryStatus(&memory);

  Memo1->Lines->Add("您的物理内存是(Mb):"+String(int(memory.dwTotalPh ys /1024/1024)));

  Memo1->Lines->Add("其中可用内存是(Kb):"+String(int( memory. /1024)) );

  怎么样,看出点门道了么?两段程序的格式几乎一模一样,其实,GetSystemInf o和GlobalMemoryStatus还可以获得许多其他有关CPU和内存的信息,就按照上面的格式 去套就行了,更详细的资料可以去看C++ Builder4的Help。

  3. 检测可用硬盘空间

  好了,经过前面两个简单问题的热身,我们来处理一个稍微复杂的问题:我们知 道安装程序大都有一个检测硬盘空间的过程,那么这是怎么实现的呢?他用到的是 API函 数GetDiskFreeSpace,这个函数输入一个参数:目标盘的路径;返回四个参数,依次是 每簇的扇区数、每扇区的字节数、空闲的簇数、总簇数。假如我们需要检测C盘的总容量 和可用容量,那么可以把以下代码加到上面的程序中:

  //获得C盘可用空间

  DWORD sector,byte,cluster,free;

  long int freespace,totalspace;

  GetDiskFreeSpace("C:",&sector,&byte,&free,&cluster); //获得返回参 数

  totalspace=int(cluster)
int(byte)int(sector)/1024/1024; //计算总容量   freespace=int(free)int(byte)int(sector)/1024/1024; //计算可用空间

  Memo1->Lines->Add("C盘总空间(Mb):"+String(totalspace));

  Memo1->Lines->Add("C盘可用空间(Mb):"+String(freespace));

  怎么样?现在可以自己做安装程序了吧!

  4. 检测CD-ROM

  我们在编写程序时常常需要读取CD-ROM,可是究竟哪一个盘符是光驱呢?有人是 桓雠谭枪馇兀坑腥耸? 将最后一个盘符当作光驱的,但是当遇到双光驱或者MO的情况时常常会出错。其实这个 问题用一个API来解决并不困难,这就是:GetDriveType(),这个函数返回一个0~6之间 的值,依次代表:0—未知盘、1—不存在、2—可移动磁盘、3—固定磁盘、4—网络磁盘 、5—CD-ROM、6—内存虚拟盘。因此我们可以添加下面代码来寻找CD-ROM:

  // 获得CD-ROM信息

  UINT type;

  char name;

  for (name=‘C’;name<=‘Z’;name++) //循环检测A~Z

  { type = GetDriveType((String(name)+String(‘:’)).c_str()); //获得磁 盘类型

  if (type==5)

  Memo1->Lines->Add("您的光驱盘符为:"+String(name));

   }

  得到光驱盘符之后我们可以进一步利用API函数GetVolumeInformation检测光驱中 是否有光盘,这个函数如果成功调用,会得到磁盘的卷标序列号等信息;如果调用失败 则可知光驱中无光盘,程序如下://检测光盘(假设光驱为G:)

  char volname[255],filename[100];//buffer[512];

  DWORD sno,maxl,fileflag ;

  if (!(GetVolumeInformation("G:", volname,255,&sno,&maxl,&fileflag ,filename,100)))

   //如果返回值为假

  Memo1->Lines->Add ("G驱中没有发现光盘");

   else

   //如果返回值为真

  {Memo1->Lines->Add ("G驱中光盘卷标为:"+String(volname));

  Memo1->Lines->Add ("G驱中光盘序号为:"+String(sno));

   }

  5. 检测声卡配置

  在编制多媒体程序时,我们常常会用到声音文件,而当这些程序在没有配置声卡 的机器上运行时,我们应该给出必要的警告。对于声卡的检测,可以分别通过waveOutG etNumDevs()和midiOutGetNumDevs()检测波形设备和MIDI设备,再利用waveOutGetDevc aps()和midiOutGetDevcaps()获得声音设备的细节资料。将下面一段代码加入上面的程 序即可,但要注意将#include 添至程序首部:

  //检测声卡

  int wavedevice,mididevice;

  WAVEOUTCAPS wavecap;

  MIDIOUTCAPS midicap;

  wavedevice=(int)waveOutGetNumDevs(); //波形设备信息   mididevice=(int)midiOutGetNumDevs(); // MIDI设备信息   mididevice=(int)midiOutGetNumDevs(); // MIDI设备信息

  if (wavedevice==0)

  Memo1->Lines->Add ("没有发现波形设备");

  else

  {waveOutGetDevCaps(0,&wavecap,sizeof(WAVEOUTCAPS));

  Memo1->Lines->Add ("当前波形设备:"+String(wavecap.szPname));

   }

  if (mididevice==0)

  Memo1->Lines->Add ("没有发现MIDI设备");

  else

  {midiOutGetDevCaps(0,&midicap,sizeof(MIDIOUTCAPS));

  Memo19->Lines->Add ("当前MIDI设备:"+String(midicap.szPname));

   }

  6. 检测显示器信息

  编写和图形图像有关的程序时常常需要检测显示器的分辨率和色深,最后我们来 看看这个问题的解决办法。分辨率的求法很简单,直接调用Screen对象的属性就行了。 而要求色深则要利用API函数GetDeviceCaps获得每像素的比特数和色彩的页面数,然后 计算2的"每像素的比特数"次幂即得色彩的梯度数,再计算"色彩的梯度数"的"色彩 的页面数"次幂即得色深。程序如下:

  //检测显示器

  int tcs;

  long int bpp,cp,tc;

  Memo1->Lines->Add ("当前分辨率为:"+String(Screen->Width)+"
"+S tring(Screen->Height));

  bpp=GetDeviceCaps(Form1->Canvas->Handle ,BITSPIXEL);

  bpp=GetDeviceCaps(Form1->Canvas->Handle ,BITSPIXEL);

  tcs=pow(2,bpp); //计算色彩的梯度数

  cp= GetDeviceCaps(Form1->Canvas->Handle,PLANES);

  tc= pow(tcs,cp); //计算色深

  Memo1->Lines->Add("当前色深为:"+String(tc));

  好了,现在在让我们点击一下Test吧,其实本文所涉 及的API函数的功能不止这些,大家下去可以查一查win32 API手册,或者直接在C++ Builder 4中察看Help。相信自己开发一个硬件检测软件也不是难事哦!

  以上程序均在Windows98中文版、C++ Builder 4中调试通过,如果大家在硬件 检测方面有什么问题,请与本文作者探讨,AlexYoung@263.net。(西安 杨洪辰)

C++ Builder 研究--如何将CB创建的Activex设定为脚本安全

深入解析ATL上就有,可惜我来晚了,昨天刚解决这个问题。



写在这个里面的public,就可以了



class ATL_NO_VTABLE TSignActiveFormXImpl:

vcLCONTROL_IMPL(TSignActiveFormXImpl, SignActiveFormX, TSignActiveFormX, ISignActiveFormX, DIID_ISignActiveFormXEvents)

{

void __fastcall ActivateEvent(TObject *Sender);

void __fastcall ClickEvent(TObject *Sender);

void __fastcall CreateEvent(TObject *Sender);

void __fastcall DblClickEvent(TObject *Sender);

void __fastcall DeactivateEvent(TObject *Sender);

void __fastcall DestroyEvent(TObject *Sender);

void __fastcall KeyPressEvent(TObject *Sender, char &Key);

void __fastcall PaintEvent(TObject *Sender);

public:



void InitializeControl()

{
m_vclCtl->OnActivate = ActivateEvent;
m_VclCtl->OnClick = ClickEvent;
m_VclCtl->OnCreate = CreateEvent;
m_VclCtl->OnDblClick = DblClickEvent;
m_VclCtl->OnDeactivate = DeactivateEvent;
m_VclCtl->OnDestroy = DestroyEvent;
m_VclCtl->OnKeyPress = KeyPressEvent;
m_VclCtl->OnPaint = PaintEvent;

}



// The COM MAP entries declares the interfaces your object exposes (through

// QueryInterface). CComRootObjectEx::InternalQueryInterface only returns

// pointers for interfaces in the COM map. VCL controls exposed as OCXes

// have a minimum set of interfaces defined by the

// VCL_CONTROL_COM_INTERFACE_ENTRIES macro. Add other interfaces supported

// by your object with additional COM_INTERFACE_ENTRY[_xxx] macros.

//

BEGIN_COM_MAP(TSignActiveFormXImpl)

VCL_CONTROL_COM_INTERFACE_ENTRIES(ISignActiveFormX)

END_COM_MAP()





//在这里,看到了吗?

BEGIN_CATEGORY_MAP(CYourComObject)

IMPLEMENTED_CATEGORY(CATID_SafeForScripting)

IMPLEMENTED_CATEGORY(CATID_SafeForInitializing)

END_CATEGORY_MAP()





// The PROPERTY map stores property descriptions, property DISPIDs,

// property page CLSIDs and IDispatch IIDs. You may use use

// IPerPropertyBrowsingImpl, IPersistPropertyBagImpl, IPersistStreamInitImpl,

// and ISpecifyPropertyPageImpl to utilize the information in you property

// map.

//

// NOTE: The BCB Wizard does NOT maintain your PROPERTY_MAP table. You must

// add or remove entries manually.

//

BEGIN_PROPERTY_MAP(TSignActiveFormXImpl)

// PROP_PAGE(CLSID_SignActiveFormXPage)

END_PROPERTY_MAP()



/* DECLARE_VCL_CONTROL_PERSISTENCE(CppClass, VclClass) is needed for VCL

  • controls to persist via the VCL streaming mechanism and not the ATL mechanism.
  • The macro adds static IPersistStreamInit_Load and IPersistStreamInit_Save
  • methods to your implementation class, overriding the methods in IPersistStreamImpl.
  • This macro must be manually undefined or removed if you port to C++Builder 4.0. */



    DECLARE_VCL_CONTROL_PERSISTENCE(TSignActiveFormXImpl, TSignActiveFormX);



    // The DECLARE_ACTIVEXCONTROL_REGISTRY macro declares a static 'UpdateRegistry'

    // routine which registers the basic information about your control. The

    // parameters expected by the macro are the ProgId & the ToolboxBitmap ID of

    // your control.

    //

    DECLARE_ACTIVEXCONTROL_REGISTRY("SignActiveFormProj1.SignActiveFormX", 1);



    protected:

    STDMETHOD(_set_Font(IFontDisp** Value));

    STDMETHOD(get_Active(VARIANT_BOOL* Value));

    STDMETHOD(get_AlignDisabled(VARIANT_BOOL* Value));

    STDMETHOD(get_AutoScroll(VARIANT_BOOL* Value));

    STDMETHOD(get_AutoSize(VARIANT_BOOL* Value));

    STDMETHOD(get_AxBorderStyle(TxActiveFormBorderStyle* Value));

    STDMETHOD(get_BorderWidth(long* Value));

    STDMETHOD(get_Caption(BSTR* Value));

    STDMETHOD(get_Color(::OLE_COLOR* Value));

    STDMETHOD(get_DoubleBuffered(VARIANT_BOOL* Value));

    STDMETHOD(get_DropTarget(VARIANT_BOOL* Value));

    STDMETHOD(get_Enabled(VARIANT_BOOL* Value));

    STDMETHOD(get_Font(IFontDisp** Value));

    STDMETHOD(get_HelpFile(BSTR* Value));

    STDMETHOD(get_KeyPreview(VARIANT_BOOL* Value));

    STDMETHOD(get_PixelsPerInch(long* Value));

    STDMETHOD(get_PrintScale(TxPrintScale* Value));

    STDMETHOD(get_Scaled(VARIANT_BOOL* Value));

    STDMETHOD(get_Visible(VARIANT_BOOL* Value));

    STDMETHOD(get_VisibleDockClientCount(long* Value));

    STDMETHOD(set_AutoScroll(VARIANT_BOOL Value));

    STDMETHOD(set_AutoSize(VARIANT_BOOL Value));

    STDMETHOD(set_AxBorderStyle(TxActiveFormBorderStyle Value));

    STDMETHOD(set_BorderWidth(long Value));

    STDMETHOD(set_Caption(BSTR Value));

    STDMETHOD(set_Color(::OLE_COLOR Value));

    STDMETHOD(set_DoubleBuffered(VARIANT_BOOL Value));

    STDMETHOD(set_DropTarget(VARIANT_BOOL Value));

    STDMETHOD(set_Enabled(VARIANT_BOOL Value));

    STDMETHOD(set_Font(IFontDisp* Value));

    STDMETHOD(set_HelpFile(BSTR Value));

    STDMETHOD(set_KeyPreview(VARIANT_BOOL Value));

    STDMETHOD(set_PixelsPerInch(long Value));

    STDMETHOD(set_PrintScale(TxPrintScale Value));

    STDMETHOD(set_Scaled(VARIANT_BOOL Value));

    STDMETHOD(set_Visible(VARIANT_BOOL Value));

    STDMETHOD(get_SignStr(BSTR* Value));

    STDMETHOD(set_SignStr(BSTR Value));

    STDMETHOD(test(BSTR str));

    STDMETHOD(test2(BSTR* str));

    STDMETHOD(mySign());

    STDMETHOD(errorCode(BSTR* errorCode));

    };

    //---------------------------------------------------------------------------

    #endif