Windows Driver Model 2
第二章:WDM驱动程序的基本结构
在第一章中,我介绍了Windows 2000和Windows 98操作系统的基本架构。我引入这样一个概念:设备驱动程序是一个包含了许多操作系统可调用例程的容器,这些例程可以使硬件设备执行相应的动作。本章将描述驱动程序容器应包含的基本内容、驱动程序的层次结构,还将讲述DriverEntry和AddDevice函数,这两个函数存在于每个WDM驱动程序中。
设备和驱动程序的层次结构
WDM模型使用了如图2-1的层次结构。图中左边是一个设备对象堆栈。设备对象是系统为帮助软件管理硬件而创建的数据结构。一个物理硬件可以有多个这样的数据结构。处于堆栈最底层的设备对象称为物理设备对象(physical device object),或简称为PDO。在设备对象堆栈的中间某处有一个对象称为功能设备对象(functional device object),或简称FDO。在FDO的上面和下面还会有一些过滤器设备对象(filter device object)。位于FDO上面的过滤器设备对象称为上层过滤器,位于FDO下面(但仍在PDO之上)的过滤器设备对象称为下层过滤器。
图2-1. WDM中设备对象和驱动程序的层次结构
操作系统的PnP管理器按照设备驱动程序的要求构造了设备对象堆栈,在本书中,我们用通用术语“总线(bus)”来描述与设备进行电气连接的硬件。这是一个广义的定义,它不仅包括PCI总线,还包括SCSI卡、并行口、串行口、USB集线器(hub),等等。实际上,它可以是任何能插入多个设备的硬件设备。总线驱动程序的一个任务就是枚举总线上的设备,并为每个设备创建一个PDO。一旦总线驱动程序检查到新硬件存在,PnP管理器就创建一个PDO,之后便开始描绘如图2-1所示的结构。
创建完PDO后,PnP管理器参照注册表中的信息查找与这个PDO相关的过滤器和功能驱动程序,它们出现在图的中部。系统安装程序负责添加这些注册表项,而驱动程序包中控制硬件安装的INF文件负责添加其它表项。这些表项定义了过滤器和功能驱动程序在堆栈中的次序。PnP管理器先装入最底层的过滤器驱动程序并调用其AddDevice函数。该函数创建一个FiDO,这样就在过滤器驱动程序和FiDO和之间建立了水平连接。然后,AddDevice把PDO连接到FiDO上,这就是设备对象之间连线的由来。PnP管理器继续向上执行,装入并调用每个低层过滤器、功能驱动程序、每个高层过滤器,直到完成整个堆栈。
层次结构可以使I/O请求过程更加明了,见图2-1的右侧。每个影响到设备的操作都使用I/O请求包。通常IRP先被送到设备堆栈的最上层驱动程序,然后逐渐过滤到下面的驱动程序。每一层驱动程序都可以决定如何处理IRP。有时,驱动程序不做任何事,仅仅是向下层传递该IRP。有时,驱动程序直接处理完该IRP,不再向下传递。还有时,驱动程序既处理了IRP,又把IRP传递下去。这取决于设备以及IRP所携带的内容。
在单个硬件的驱动程序堆栈中,不同位置的驱动程序扮演了不同的角色。功能驱动程序管理FDO所代表的设备。总线驱动程序管理计算机与PDO所代表设备的连接。过滤器驱动程序用于监视和修改IRP流。由于设备对象与驱动程序软件之间关系紧密,有时使我用FDO驱动程序来代表功能驱动程序,用PDO驱动程序来代表总线驱动程序。
听我讲座的一个学生曾看到过与图2-1类似的图,他把图中描述的层次结构误认为是C++中的类继承。设计设备驱动程序架构的一个好方法是定义基类,程序员可以从它派生出更多的专用类。以这种方式,你可以用一组抽象类来管理不同种类的PDO,还可以用它们派生出FDO驱动程序。系统把IRP发送到虚拟函数,其中一些IRP被PDO驱动程序中的基类处理,另一些被FDO驱动程序中的派生类处理。但是,WDM并不是以这种方式工作,PDO驱动程序执行的工作与FDO驱动程序完全不同。FDO通过下传IRP把某些工作委托给PDO驱动程序去做,这种关系更象链条上的环节,而不象类间的继承关系。
系统怎样装入驱动程序
介绍完WDM驱动程序层次结构后,我们可以更深入一步,但这里有一个明显的“鸡生蛋”问题需要解决。我曾说过总线驱动程序创建了PDO,然后PnP管理器根据该PDO的注册表项装入它的驱动程序。那么总线驱动程序从哪来?下面我将解释这个问题。在装入和配置驱动程序过程中,注册表扮演了一个关键角色。所以我将解释与这个过程相关的注册表键,以及它的内容是什么。
递归枚举
首先,PnP管理器有一个内建的驱动程序,它与一个实际不存在的根总线相对应。根总线概念性地把计算机与所有那些不能用电子方式声明自己存在的设备连接起来,这包括主硬件总线(如PCI)。根总线驱动程序从注册表中获取有关计算机的信息。而这些关于计算机本身的注册表信息是由Windows 2000系统安装程序初始化的。安装程序通过运行一个精心制作的硬件检测程序以及向用户提出一些适当的问题来获取这些信息。所以,根总线驱动程序有足够的信息为主总线创建PDO。
然后,主总线的功能驱动程序用电子方式枚举自己的硬件。例如,PCI总线提供了一种访问每个插入设备的硬件配置空间的方法,配置空间保存了设备的资源需求和描述信息。当总线驱动程序枚举硬件时,它的动作就象一个普通的功能驱动程序。检测完一个硬件后,它改变角色,变成总线驱动程序并为查到的硬件创建PDO。然后PnP管理器装入该PDO的驱动程序。有时,设备的功能驱动程序还要枚举其它硬件,如果是这样,整个过程将递归重复。结果将出现如图2-2的树形结构,总线设备堆栈将分叉成其它设备堆栈,每个设备堆栈代表着插入到该总线上的一个硬件设备:
图2-2. 设备递归枚举过程
注册表的角色
有三种注册表键负责配置。它们是硬件(hardware)键、类(class)键、服务(service)键。必须明确一点,这些名字(指hardware、class、service)并不是某个专用子键的名称:它们是这三种键的一般称谓,其具体的路径名要取决于它们所属的设备。概括地讲,硬件键包含单个设备的信息,类键涉及所有相同类型设备的共同信息,服务键包含驱动程序信息。有时人们用“实例(instance)键”和“软件(software)键”来代表硬件键和服务键。这个命名差异是由于Windows 95/98与Windows 2000是由两个不同的项目组开发所造成的。
硬件(或实例)键 设备的硬件键出现在注册表local machine分支的\System\CurrentControlSet\Enum子键上。通常你不能查看到该键的内部信息,系统只允许拥有系统帐号的用户访问该键。即只有内核模式程序和运行在系统帐号下的用户模式服务可以读写Enum键和其子键,但是即使是管理员也不应该直接修改这些键的内容。要想查看Enum键的内容,可以在Administrator特权级帐户下使用REGEDIT32.EXE工具查看。图2-3显示了Enum键的内部情况:
图2-3. 注册表中的硬件键
Enum键下的第一级子键与系统中的各种总线枚举器相对应。\Enum\USB子键中包含了所有以前用过和现在存在的USB设备的描述。在USB42例子中,我将阐述怎样把设备的硬件ID(vendor 0574, product 102A)转换成键名(Vid_0574&Pid_102A),以及如何使有该ID的设备实例被表示为下一层的子键7&2。7&2就是该设备的硬件(或实例)键名。
硬件键中的某些键值提供的描述性信息可以被象设备管理器这样的用户模式部件所使用。图2-4显示了设备管理器给出的USB42设备属性。参见文字框“从用户模式中访问设备键”解释设备管理器如何收集这些信息,虽然它自身并不能跨过Enum键的正常安全保护。
图2-4. 设备管理器显示的设备属性页
硬件键还包含几个标识设备所属类和驱动程序的值。ClassGUID是设备类GUID(全局唯一标识符)的ASCII形式;在效果上,它是一个指向该设备类键的指针。Service指向服务键。可选值(USB42中没有)LowerFilters和UpperFilters分别标识低层过滤器和高层过滤器的服务名。
最后,硬件键还可以有名为Security、Exclusive、DeviceType、和DeviceCharacteristics的超越值,这四种值强制驱动程序创建有某种属性的设备对象。我将在后面讲述如何创建设备对象时再讨论这些超越值的重要性。
硬件键中的大部分值是在安装过程中自动填入的,或者在安装开始后的某个时刻,系统识别出了新硬件(或者经由硬件安装向导)并由系统自动填入的。有些值是由于INF(硬件安装文件)中曾指明要放入这里的。我将在第十二章讨论INF文件。
类键 所有设备类的类键都出现在HKLM\System\CurrentControlSet\Control\Class键中。它们的键名是由Microsoft赋予的GUID值。图2-5显示了SAMPLE设备的类键,USB42例子和本书中所有其它例子设备都属于该类。
图2-5. 注册表中的类键
Sample类并不是特别有代表性,因为它缺少某些可选值,如:
- LowerFilters和UpperFilters,为该类所有设备指定过滤器驱动程序。
- Security、Exclusive、DeviceType,和DeviceCharacteristics,如果类键的一个Properties子键中出现这些值,则指定值将超越该类所有设备对象的某些默认参数设置。但这些超越值要比硬件键中的超越值优先级低。系统管理员能通过管理控制台设置这些超越值。
每个设备还可以在所属类键下拥有自己的子键。该键的键名就是设备硬件键中的Driver值。图2-6显示了这种子键,这个子键的作用是把所有这些注册表项与安装设备使用的INF文件相关联。
图2-6. 存在于类键中的设备专用子键
服务(或软件)键 对设备驱动程序重要的最后一个键是服务键。它指出驱动程序执行文件的位置,以及控制驱动程序装入的一些参数。服务键位于HKLM\System\CurrentControlSet\Services键中。图2-7是USB42的服务键。
图2-7. 注册表中的一个服务键
服务键的内容在平台SDK的“Service Install”中有详尽描述,因此我不想再重述。在USB42的例子中,这些值有如下意义:
- ImagePath 指出驱动程序执行文件为USB42.SYS,路径为%SystemRoot%\system32\drivers,注意这个路径名起始于系统根目录。
- Type(1) 指出该表项描述一个内核模式驱动程序。
- Start(3) 指出系统应动态装入这个驱动程序。(该值与CreateService中的SERVICE_DEMAND_START常量对应,用于内核模式驱动程序时它代表不必明确调用StartService函数或发出NET START命令来启动驱动程序)
- ErrorControl(1) 指出如果装入该驱动程序失败,系统应登记该错误并显示一个消息框。
驱动程序装入顺序
当PnP管理器遇到一个新设备时,它打开设备的硬件键和类键,然后按如下顺序装入驱动程序:
- 硬件键中指定的任何低层过滤器驱动程序。由于LowerFilter值的类型是REG_MULTI_SZ,所以它可以指定多个驱动程序,其装入顺序由它们在串中的位置决定。
- 类键中指定的任何低层过滤器驱动程序。同样,它也可以指定多个驱动程序,装入顺序同1。
- 硬件键中Service值指定的驱动程序。
- 硬件键中指定的任何高层过滤器驱动程序,同样,它也可以指定多个驱动程序,装入顺序同1。
- 类键中指定的任何高层过滤器驱动程序,同样,它也可以指定多个驱动程序,装入顺序同1。
当我说系统“装入”一个驱动程序时,是指系统把驱动程序的映像映射到虚拟内存中,并重定位内存参考,最后调用驱动程序的主入口点。主入口点通常命名为DriverEntry。我将在本章后面讲述DriverEntry函数。如果驱动程序已经在内存中,则装入过程仅仅是增加驱动程序映像的参考计数。
你可能注意到了,属于类和设备实例的上下层过滤器驱动程序在装入过程中并没有出现象你想象的嵌套方式。在我知道这个事实前,我猜想设备级的过滤器驱动程序应比类级过滤器驱动程序更靠近功能驱动程序。但是,正如我后面讲到的,驱动程序的装入顺序并不是很重要。系统以PnP管理器装入驱动程序的顺序调用驱动程序的AddDevice函数,而这个顺序与设备对象在设备堆栈中出现的顺序完全一致。
设备对象之间如何关联
图2-2显示了一个由多个设备堆栈组成的树形结构,但这并不表示IRP必须从上一层的PDO流向下一层的FiDO。实际上,一个堆栈的PDO驱动程序就是其下一层堆栈的FDO驱动程序,见图2-2中的阴影块。当驱动程序以PDO角色接收到一个IRP时,它对该IRP执行一些操作但不发出这个或其它IRP到设备(以FDO角色)。相反,当总线驱动程序以FDO角色接收到一个IRP时,它可能需要向设备发送某些IRP(以PDO角色)。
举一些例子可以澄清FiDO、FDO、PDO之间的关系。第一个例子是PCI总线(PCI总线本身是通过PCI-to-PCI桥芯片附着到主总线上)上的一个直接到设备的读操作。为了使问题更简单,我们假设该设备仅有一个FiDO,如图2-8,该操作被系统转换成一个主功能码为IRP_MJ_READ的请求(如何转换我们以后再讨论)。这个请求首先流到上层FiDO,然后再传递给该设备的功能驱动程序(即图中标为FDOdev的设备对象)。最后功能驱动程序直接调用硬件抽象层执行读操作,所以图中其它驱动程序看不到该IRP。
图2-8. 设备(位于二级总线)读请求处理流程
我们对第一个例子稍做修改,如图2-9。这次我们的读请求将发往一个USB设备(USB设备插入USB hub,USB hub再插入主机控制器),因此,USB设备堆栈、hub设备堆栈、主机控制器设备堆栈将加入设备树。IRP_MJ_READ请求先穿过FiDO到达功能驱动程序,然后功能驱动程序生成一个或多个不同种类的IRP并发送到自己的PDO。USB设备的PDO驱动程序就是USBHUB.SYS,该驱动程序把这些IRP送到主机控制器堆栈的最上层驱动程序,跳过中间的USB集线器堆栈(含有两个驱动程序)。
图2-9. USB设备的读请求处理流程
第三个例子与第一个类似,不同的是这里的IRP是一个通知IRP,涉及到一个PCI总线上的磁盘驱动器是否被用做系统分页文件库。该通知以主功能码为IRP_MJ_PNP副功能码为IRP_MN_DEVICE_USAGE_NOTIFICATION的IRP形式出现。在这里,FiDO驱动程序把请求传递到FDOdev驱动程序,FDOdev驱动程序得到通知再把请求传递到PDOdev驱动程序。这个通知暗示驱动程序该如何处理PnP或电源管理请求,所以PDOdev驱动程序向二级总线堆栈(它的FDObus就是现在设备堆栈中的PDOdev)发出相同的通知,如图2-10。(注意并不是所有的总线驱动程序都这样做,但PCI总线是这样的)
图2-10. 一个设备用途通知的处理流程
检查设备堆
最好能形象地表现一下设备对象和驱动程序的层次结构,所以我写了一个辅助工具DevView。把名为USB42的设备插入USB hub,然后运行DevView,屏幕上将出现图2-11,图2-12所示的对话框。
这个设备仅使用两个设备对象。PDO由USBHUB.SYS管理,FDO由USB42.SYS(USB42设备的驱动程序)管理。在图2-11中你可以看到关于PDO的一些信息。对照注册表中与USB42相关的键,现在你应该知道这些信息是从哪来的吧。
用DevView查看你自己的系统,可以更深刻地了解你的计算机。
图2-11. DevView显示USB42的PDO信息
图2-12. DevView显示的USB42的FDO信息
驱动程序对象
I/O管理器使用驱动程序对象来代表每个设备驱动程序,见图2-13。就象我们将要讨论的许多数据结构一样,驱动程序对象是部分不透明的。这意味着虽然DDK头中公开了整个结构,但我们仅能直接访问或修改结构中的某些域。在图中,我把驱动程序对象的不透明域用灰背景表示。这些不透明域类似于C++类中的私有成员或保护成员,而透明域类似于公共成员。
图2-13. DRIVER_OBJECT数据结构
DDK头文件中声明了包括驱动程序对象在内的所有内核模式数据结构,其声明遵循如下形式(摘自WDM.H):
typedef struct _DRIVER_OBJECT { CSHORT Type; CSHORT Size; ...} DRIVER_OBJECT, *PDRIVER_OBJECT; |
这就是类型名为DRIVER_OBJECT的结构的声明形式,它同时还声明了一个PDRIVER_OBJECT指针类型和结构标签_DRIVER_OBJECT。这种结构声明方式在DDK中大量使用。DDK头文件还声明了一组类型名(如CSHORT),这些类型名用于描述内核模式中的原子数据类型。表2-1列出了一些这样的类型。例如,CSHORT代表用作基数的有符号短整型数。基数(cardinal number)用于表示数值,序数(ordinal number)用于表示计数器。
表2-1. 内核模式驱动程序可以使用的公用类型名
类型名 | 描述 |
---|---|
PVOID, PVOID64 | 通用指针(默认长度和64位长度) |
NTAPI | 用于声明服务函数,强制使用__stdcall调用约定,用于x86平台上 |
VOID | 等价于“void” |
CHAR, PCHAR | 8位字符,指向8位字符的指针(有无符号取决于编译器默认设置) |
UCHAR, PUCHAR | 无符号8位字符,无符号8位字符指针 |
SCHAR, PSCHAR | 有符号8位字符,有符号8位字符指针 |
SHORT, PSHORT | 有符号16位整数,有符号16位整数指针 |
USHORT, PUSHORT | 无符号16位整数,无符号16位整数指针 |
LONG, PLONG | 有符号32位整数,有符号32位整数指针 |
ULONG, PULONG | 无符号32位整数,无符号32位整数指针 |
WCHAR, PWSTR | Unicode字符,Unicode字符串 |
PCWSTR | 常量Unicode串指针 |
NTSTATUS | 状态代码(类型为有符号长整型) |
LARGE_INTEGER | 有符号64位整数 |
ULARGE_INTEGER | 无符号64位整数 |
PSZ, PCSZ | ASCIIZ(单字节)串指针,常量ASCIIZ(单字节)串指针 |
BOOLEAN, PBOOLEAN | TRUE或FALSE (等价于UCHAR) |
现在我们简要地讨论一下驱动程序对象中的透明域。
DeviceObject(PDEVICE_OBJECT) 指向一个设备对象链表,每个设备对象代表一个设备。I/O管理器把多个设备对象连接起来并维护这个域。非WDM驱动程序的DriverUnload函数利用这个域来遍历设备对象列表,以便删除其中的设备对象。WDM驱动程序没有必要使用这个域。
DriverExtension(PDRIVER_EXTENSION) 指向一个不大的子结构,其中只有AddDevice(PDRIVER_ADD_DEVICE)成员可以直接访问,见图2-14。AddDevice是一个指针,它指向驱动程序中创建设备对象的函数;该函数内容较多,我将在本章的后面讨论它。
图2-14. DRIVER_EXTENSION数据结构
HardwareDatabase(PUNICODE_STRING)指向一个串,该串为设备的硬件数据库键名。内容与“\Registry\Machine\Hardware\Description\System”类似,这个注册表键保存着该设备的资源分配信息。WDM驱动程序没有必要访问该键下的信息,因为PnP管理器自动执行资源分配。该名字以Unicode方式存储。(实际上,所有内核模式串都使用Unicode编码),我将在下一章讨论UNICODE_STRING结构的格式和用法。
FastIoDispatch(PFAST_IO_DISPATCH)指向一个函数指针表,这些函数是由文件系统和网络驱动程序输出的。它们的使用已经超出了本书的范围。如果你想学习更多的关于文件系统驱动程序的知识,请参考Rajeev Nagar的《Windows NT File System Internals: A Developer's Guide (O'Reilly & Associates, 1997)》。
DriverStartIo(PDRIVER_STARTIO)指向驱动程序中处理串行I/O请求的函数,I/O管理器自动为驱动程序串行化多个I/O请求。我将在第五章详细讨论该函数和请求排队。
DriverUnload (PDRIVER_UNLOAD)指向驱动程序中的清除函数。我将在DriverEntry函数后面讨论该函数,实际上,WDM驱动程序根本就没有什么重要的清除工作要做。
MajorFunction (array of PDRIVER_DISPATCH)是一个函数指针表,指向存在于驱动程序中的二十多种IRP处理函数。这个表同样是一个大话题,因为它定义了I/O请求如何进入驱动程序。
设备对象
图2-15给出了设备对象的格式,阴影处代表不透明域。WDM驱动程序可以调用IoCreateDevice函数创建设备对象,但设备对象的管理则由I/O管理器负责。
DriverObject(PDRIVER_OBJECT)指向与该设备对象相关的驱动程序对象,通常就是调用IoCreateDevice函数创建该设备对象的驱动程序对象。过滤器驱动程序有时需要用这个指针来寻找被过滤设备的驱动程序对象,然后查看其MajorFunction表项。
NextDevice(PDEVICE_OBJECT)指向属于同一个驱动程序的下一个设备对象。是这个域把多个设备对象连接起来,起始点就是驱动程序对象中的DeviceObject成员。WDM驱动程序没有必要使用这个域。
图2-15. DEVICE_OBJECT结构
CurrentIrp(PIRP)指向最近发往驱动程序StartIo函数的I/O请求包。在第五章中,当讨论取消例程时,我还要更详细地讨论这个域。
Flags(ULONG)包含一组标志位。表2-2列出了其中可访问的标志位。
表2-2. DEVICE_OBJECT结构中的Flags标志
标志 | 描述 |
---|---|
DO_BUFFERED_IO | 读写操作使用缓冲方式(系统复制缓冲区)访问用户模式数据 |
DO_EXCLUSIVE | 一次只允许一个线程打开设备句柄 |
DO_DIRECT_IO | 读写操作使用直接方式(内存描述符表)访问用户模式数据 |
DO_DEVICE_INITIALIZING | 设备对象正在初始化 |
DO_POWER_PAGABLE | 必须在PASSIVE_LEVEL级上处理IRP_MJ_PNP请求 |
DO_POWER_INRUSH | 设备上电期间需要大电流 |
Characteristics(ULONG)是另一组标志位,描述设备的可选特征,见表2-3。I/O管理器基于IoCreateDevice的一个参数初始化这些标志。过滤器驱动程序沿设备堆栈向上方向传播这些标志。
表2-3. DEVICE_OBJECT结构中的Characteristics标志
标志 | 描述 |
---|---|
FILE_REMOVABLE_MEDIA | 可更换媒介设备 |
FILE_READ_ONLY_DEVICE | 只读设备 |
FILE_FLOPPY_DISKETTE | 软盘驱动器设备 |
FILE_WRITE_ONCE_MEDIA | 只写一次设备 |
FILE_REMOTE_DEVICE | 通过网络连接访问的设备 |
FILE_DEVICE_IS_MOUNTED | 物理媒介已在设备中 |
FILE_DEVICE_SECURE_OPEN | 在打开操作中检查设备对象的安全属性 |
DeviceExtension(PVOID)指向一个由用户定义的数据结构,该结构可用于保存每个设备实例的信息。I/O管理器为该结构分配空间,但该结构的名字和内容完全由用户决定。一个常见的做法是把该结构命名为DEVICE_EXTENSION。使用给定的设备对象指针fdo可访问这个用户结构,代码如下:
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; |
也许是巧合,在内存中,设备扩展(DeviceExtension)的位置紧接在设备对象后面。但依赖这种巧合,放弃使用指针访问设备扩展却是个完全错误的想法。
DeviceType(DEVICE_TYPE)是一个枚举常量,描述设备类型。I/O管理器基于IoCreateDevice的一个参数初始化这个成员。过滤器驱动程序有可能需要探测该值。在本书写作时,该成员大约有50多种可能值,见表2-4。
表2-4. 设备类型代码和默认的安全属性
设备类型 | 默认安全属性 |
---|---|
FILE_DEVICE_BEEP | Public Open Unrestricted |
FILE_DEVICE_CD_ROM | Modified Public Default Unrestricted |
FILE_DEVICE_CD_ROM_FILE_SYSTEM | Public Default Unrestricted |
FILE_DEVICE_CONTROLLER | Public Open Unrestricted |
FILE_DEVICE_DATALINK | Public Open Unrestricted |
FILE_DEVICE_DFS | Public Open Unrestricted |
FILE_DEVICE_DISK | Modified Public Default Unrestricted |
FILE_DEVICE_DISK_FILE_SYSTEM | Public Default Unrestricted |
FILE_DEVICE_FILE_SYSTEM | Public Default Unrestricted |
FILE_DEVICE_INPORT_PORT | Public Open Unrestricted |
FILE_DEVICE_KEYBOARD | Public Open Unrestricted |
FILE_DEVICE_MAILSLOT | Public Open Unrestricted |
FILE_DEVICE_MIDI_IN | Public Open Unrestricted |
FILE_DEVICE_MIDI_OUT | Public Open Unrestricted |
FILE_DEVICE_MOUSE | Public Open Unrestricted |
FILE_DEVICE_MULTI_UNC_PROVIDER | Public Open Unrestricted |
FILE_DEVICE_NAMED_PIPE | Public Open Unrestricted |
FILE_DEVICE_NETWORK | Modified Public Default Unrestricted |
FILE_DEVICE_NETWORK_BROWSER | Public Open Unrestricted |
FILE_DEVICE_NETWORK_FILE_SYSTEM | Modified Public Default Unrestricted |
FILE_DEVICE_NULL | Public Open Unrestricted |
FILE_DEVICE_PARALLEL_PORT | Public Open Unrestricted |
FILE_DEVICE_PHYSICAL_NETCARD | Public Open Unrestricted |
FILE_DEVICE_PRINTER | Public Open Unrestricted |
FILE_DEVICE_SCANNER | Public Open Unrestricted |
FILE_DEVICE_SERIAL_MOUSE_PORT | Public Open Unrestricted |
FILE_DEVICE_SERIAL_PORT | Public Open Unrestricted |
FILE_DEVICE_SCREEN | Public Open Unrestricted |
FILE_DEVICE_SOUND | Public Open Unrestricted |
FILE_DEVICE_STREAMS | Public Open Unrestricted |
FILE_DEVICE_TAPE | Public Open Unrestricted |
FILE_DEVICE_TAPE_FILE_SYSTEM | Public Default Unrestricted |
FILE_DEVICE_TRANSPORT | Public Open Unrestricted |
FILE_DEVICE_UNKNOWN | Public Open Unrestricted |
FILE_DEVICE_VIDEO | Public Open Unrestricted |
FILE_DEVICE_VIRTUAL_DISK | Modified Public Default Unrestricted |
FILE_DEVICE_WAVE_IN | Public Open Unrestricted |
FILE_DEVICE_WAVE_OUT | Public Open Unrestricted |
FILE_DEVICE_8042_PORT | Public Open Unrestricted |
FILE_DEVICE_NETWORK_REDIRECTOR | Public Open Unrestricted |
FILE_DEVICE_BATTERY | Public Open Unrestricted |
FILE_DEVICE_BUS_EXTENDER | Public Open Unrestricted |
FILE_DEVICE_MODEM | Public Open Unrestricted |
FILE_DEVICE_VDM | Public Open Unrestricted |
FILE_DEVICE_MASS_STORAGE | Modified Public Default Unrestricted |
FILE_DEVICE_SMB | Public Open Unrestricted |
FILE_DEVICE_KS | Public Open Unrestricted |
FILE_DEVICE_CHANGER | Public Open Unrestricted |
FILE_DEVICE_SMARTCARD | Public Open Unrestricted |
FILE_DEVICE_ACPI | Public Open Unrestricted |
FILE_DEVICE_DVD | Public Open Unrestricted |
FILE_DEVICE_FULLSCREEN_VIDEO | Public Open Unrestricted |
FILE_DEVICE_DFS_FILE_SYSTEM | Public Open Unrestricted |
FILE_DEVICE_DFS_VOLUME | Public Open Unrestricted |
FILE_DEVICE_SERENUM | Public Open Unrestricted |
FILE_DEVICE_TERMSRV | Public Open Unrestricted |
FILE_DEVICE_KSEC | Public Open Unrestricted |
StackSize(CCHAR)统计从该设备对象开始向下直到PDO之间的设备对象个数。该域的目的是告诉其它代码,如果把该设备对象的驱动程序作为其IRP的第一发送对象,那么应在这个IRP中创建多少个堆栈单元(stack location)。WDM驱动程序通常不需要修改该值,因为创建设备堆栈的支持函数会自动完成这个任务。