全部博文(584)
分类: WINDOWS
2012-01-11 15:04:08
时间 : 2008-03-26 作者:佚名 编辑:本站 点击: 688
KDevice类:
一、Overview
设备对象是系统驱动结构的核心。驱动程序创建符合物理和逻辑设备的设备对象,这些设备应该是I/O操作的目标。用户子系统通过文件系统控制设备对象。所有的I/O请求(IRPs)对应一个特殊的设备对象。
KDevice类的成员函数与处理设备对象的系统服务有映射关系。成员函数的集合包括:构造函数和析构函数、串行化和完成IRP的例程、底层设备的接口;其他系统对象的接口(比如中断和DMA通道);处理各种不同类型IRP的操作。
驱动程序只能把KDevice类作为一个基类使用。驱动编写人员定义一个多个KDevice类的派生类,并创建这些类的实例(一般是在驱动程序初始化期间创建)。
二、IRP Dispatching
类库框架已经自动将派遣函数和系统进行了绑定,因此当系统向驱动发送IRP时,一个适当的派遣函数就会被调用。
一个驱动程序支持的IRP类型集合必须在function.h文件里声明。这个文件由程序编写人员提供,包括了对系统驱动对象初始化的派遣函数的一个子集
的预处理声明(#define''s)。框架用相同的方式来声明宏定义DEVMEMBER_DISPATCHERS。驱动编写人员把这个宏定义插入到
KDevice派生类的定义部分。宏的使用确保KDevice派生类定义了所有会由系统发送到驱动程序来的IRP句柄。
如果一个驱动有两个以上的设备对象,那么function.h文件中声明的IRP集合必须包含驱动可能操纵的所有IRP类型的并集。在这种情况下不同设备
对象操纵的IRP集合可能是不同的,因而不要在设备类(device class)里用DEVMEMBER_DISPATCHERS
声明IRP句柄。相反,只需要要声明最主要的成员函数,因为你将在任何其他C++类里用到它们。如果你后来添加了额外的IRP类型的句柄,就必须在
function.h文件里添加相应的定义,并在KDevice派生类定义中把它们声明为成员函数。
三、Interface to Lower Devices
I/O子系统的一个主要特色是对设备进行分层。一个I/O请求可能要经受好几次转换因为它要经过连续几个驱动的处理。在这个体系结构的最高层是文件系统,
它负责从用户子系统接收请求。然后请求被向下传输到一层层更低的层上,直到它们到达可以直接面对硬件接口的设备对象。
系统允许一个设备对象附属于另外一个设备对象。这意味着一个I/O请求要到达被附属的主设备(the attached
device)就必须先经过附属设备(the attaching
device)。KDevice类的派生类从基类继承了对底层设备进行附属和解除附属的成员函数。
四、Interface to other System Objects
因为设备对象对于驱动程序的操作来说十分关键,所以驱动编写者经常要和设备对象打交道。最后,我们非常希望添加一些成员函数作为接口来直接处理其他对象,比如中断,DMA通道和控制器。这些是一些系统对象的例子,它们都定义了回调函数用来被系统调用。
类库提供了一些对定义了回调函数的系统对象进行封装的类,但是这些类不把回调函数作为自己的成员函数。相反,回调函数应该成为哪个类的成员函数,决定权在
驱动编写人员手里。通常,它们会是KDevice类的成员函数。为了方便定义这些成员函数,类库提供了一系列的宏来为各种系统对象声明回调函数。这些宏的
命名形式为DEVMEMBER_xxxx,其中xxxx是用于区分不同回调函数的助记符(例如EVMEMBER_DPCFORISR)
五、Constructors, Destructors, and operator new
为类的实例申请内存有两种选择。用通常的new操作符,是从非分页内存池(non-paged pool)中申请的。这种方法什么时候都是可以的,但是并非在任何情况下都是最好的。大量使用new会把内存堆搞得支离破碎,在某些WDM驱动程序中将会产生一些漏洞。
对于new操作符的更新的替换形式是从设备扩展区内(device extension)申请存储空间,比如直接临近系统设备对象的内存。尽管给new操作符传递参数,联系起传统的构造函数来说不够直观,但是可以肯定,这是上上之选。
驱动编写人员必须主义使用new操作符和其构造函数的合法组合。这要参看相关主题来获取更多细节。
DriverWorks负责在WDM驱动中调用析构函数。所有继承自KDevice的类都要在类的定义中声明 SAFE_DESTRUCTORS 这个宏。
六、Member Functions(60个)
1、KDevice – Constructor
【函数原型】(三种)
FORM 1: (PREFERRED)
KDevice( void);
FORM2: (LEGACY)
KDevice(
PCWSTR DeviceName,
DEVICE_TYPE Type,
PCWSTR LinkName,
ULONG Characteristics=0,
ULONG DeviceFlags=DO_BUFFERED_IO
);
FORM3: (RARE)
KDevice(
ULONG Context,
DEVICE_TYPE Type,
ULONG Characteristics=0,
ULONG DeviceFlags=DO_BUFFERED_IO
);
【Parameters】
DeviceName 用没有终结符的宽字符串(unicode string)来指定设备名。只需要名字(the tail)就行了,不包含任何路径前缀(比如 ""),这个参数为NULL说明是一个没有命名的设备。指定一个AUTOGENERATED_DEVICE_NAME (a #define)将使OS为设备命名。
Type 常量FILE_DEVICE_xxxx来区别设备类型。
LinkName 用没有终结符的宽字符串(unicode string)指定一个提供给应用程序打开设备用的名字。只要名字(the tail)就行了,不包含任何路径前缀(比如 ""),
如果这个设备不用被应用程序打开这个参数就为NULL。只要这个参数不是NULL,那么DeviceName就不能为NULL。一个设备可能有多个
link。要创建一个附加的(additional)或一个不受保护的(unprotected)link,需要参看CreateLink函数。
Characteristics
这个参数值是通过与0取"或"得到的一位掩码(mask),或者看情况采用以下常量:FILE_REMOVABLE_MEDIA,
FILE_READ_ONLY_DEVICE, FILE_FLOPPY_DISKETTE, FILE_WRITE_ONCE_MEDIA,
FILE_DEVICE_SECURE_OPEN, FILE_AUTOGENERATED_DEVICE_NAME, and
FILE_REMOTE_DEVICE,默认值是0。
DeviceFlags
这个参数值是通过与0取"或"得到的一位掩码(mask),或者看情况采用以下常量:DO_EXCLUSIVE, DO_BUFFERED_IO,
DO_DIRECT_IO, DO_MAP_IO_BUFFER, DO_SYSTEM_BOOT_PARTITION,
DO_LONG_TERM_REQUESTS, DO_NEVER_LAST_DEVICE,
DO_VERIFY_VOLUME。构造函数会先测试一下是不是DO_EXCLUSIVE,如果是就把它传给IoCreateDevice作参数。所有其
他值都在对象创建后被取"或"保存到设备对象中。其中,DO_BUFFERED_IO和DO_DIRECT_IO是最常用的。默认值是
DO_BUFFERED_IO。
Context 这个不严格要求类型的参数值是由GetNames()函数提供的,这个函数用来获得设备名和符号连接(symbolic link)。
【Comments】
Form1 是推荐的形式。用这一形式和new操作符相结合就可以在设备扩展区内为对象分配内存。
Form2 为了与已存在的驱动程序保持兼容而保留下来的。这一形式和new操作符相结合是在堆里为对象分配内存。
Form3 如果用这一形式,设备名和符号连接将由GetNames()函数提供,你提供的这个函数将被构造函数调用。
KDevice派生类的构造函数通过测试成员变量m_ConstructorStatus
来确定基类的构造函数是否成功执行了。如果没有成功,派生类的构造函数将终止构造。因此如果在KDevice构造时遇到错误,它应该给
m_ConstructorStatus 设置一个合适的值。
对于在DriverEntry里构造完了设备对象的驱动程序来说,它应该把系统设备对象的初始化标志(initializing flag)清除。用以下代码:
MyDevice* pDevice;
((PDEVICE_OBJECT) * pDevice)->Flags &= ~DO_DEVICE_INITIALIZING;
把自己附属到其他设备上的设备不需要一个名字来接收I/O请求,它们从被附属的设备中获得I/O请求。
如果构造函数创建了符号连接,析构函数一定要记得删除它。
支撑它的底层系统服务是IoCreateDevice 和 IoCreateSymoblicLink。
函数必须在PASSIVE_LEVEL级别上被调用。
2、ConstructorStatus – Retrieves status of constructor.
获取构造函数的状态
【函数原型】
NTSTATUS ConstructorStatus( void );
【Returns】
返回一个状态码,反映构造函数是成功了还是失败了。
【Comments】
KDevice派生类的构造函数应该做到:(1)在入口处确认成员变量m_ConstructorStatus ==
STATUS_SUCCESS,以确保基类对象被成功创建;如果基类构造没有成功,就应该立刻终止构造(2)如果在构造期间出现错误应该设置给
m_ConstructorStatus 设置合适的值,以确保创建者能测试构造这个设备对象是否成功了。
3、~KDevice – Destructor.
【函数原型】
~KDevice( void );
【Comments】
这个析构函数是虚函数。
这个析构函数删除底层系统设备对象,如果构造时创建了符号连接也要负责删除。它不能删除CreateLink函数创建的符号连接(symbolic link),要删除这个得用DestroyLink函数。
支撑它的底层系统服务是IoDeleteDevice 和 IoDeleteSymbolicLink
函数必须运行在PASSIVE_LEVEL级别上。
4、IsValid – Test for an underlying system device object.
测试这个实例是否有一个底层系统设备对象。
【函数原型】
BOOLEAN IsValid( void );
【Returns】
如果KDevice对象有一个底层系统设备对象就返回TRUE。
5、IsPnpDevice – Test if object is a Pnp device.
确定这个对象是否是一个即插即用设备。
【函数原型】
BOOLEAN IsPnpDevice( void );
【Returns】
如果是即插即用设备就返回TRUE。
【Comments】
任何KPnpDevice的派生类的对象都会返回TRUE,因为这个函数依赖构造函数。
6、DeviceName – Accessor to retrieve device name.
获取设备名
【函数原型】
PUNICODE_STRING DeviceName( void );
【Returns】
返回一个被缓冲的宽字符串(Unicode string )的指针,这个字符串内存储了设备对象名。
【Comments】
返回值指针指向的字符串包含设备路径前缀(例如"")。
即使设备创建的时候没有名字,返回值也不可能为NULL。然而,对于一个未命名的设备,相应字符串的长度为0。
这个函数可以在任何中断级别上被调用(any IRQL)。
7、SymbolicLinkName – Accessor to retrieve symbolic link name.
获取符号连接名
【函数原型】
PUNICODE_STRING SymbolicLinkName(void);
【Returns】
返回一个被缓冲的宽字符串(Unicode string )的指针,这个字符串内存储了符号连接名。
【Comments】
返回值指针指向的字符串包含设备路径前缀(例如"")。
即使设备创建的时候没有符号连接名,返回值也不可能为NULL。然而,对于一个没有符号连接名的设备,相应字符串的长度为0。
这个函数只返回一个存储着构造函数创建的符号连接名的合法字符串。函数CreateLink创建的连接对它没有任何影响。
这个函数可以在任何中断级别上被调用(any IRQL)。
8、NextDevice – Accessor to retrieve next device in driver''s device list.
获得驱动的设备列表上的下一个设备。
【函数原型】
KDevice* NextDevice( void );
【Returns】
返回一个指向当前驱动创建的下一个KDevice派生类实例的指针。
【Comments】
一个驱动程序创建的所有设备对象都被系统设备对象里的指针连成一个链表。这个函数存取同一系统设备对象里指向下一个设备的指针,并且从那里得到下一个类实例。要得到链表的头指针,用KDriver::GetDeviceListHead函数。
9、DeviceQueue – Accessor to retrieve device''s IRP queue.
获取设备的IRP队列。
【函数原型】
PKDEVICE_QUEUE DeviceQueue( void );
【Returns】
返回一个指向KDEVICE_QUEUE(系统对象)的指针,这个系统对象相当于设备的IRP队列。注意编译器可以自动将PKDEVICE_QUEUE类型转换成KDeviceQueue。
【Comments】
许多驱动程序通过在内部队列里排队来将IRP进行串行化处理,或者在设备对象的队列里,用StartIl函数实现。这个函数返回与设备对象关联的队列。要了解在一个设备队列上可以进行哪些操作,请查看KDeviceQueue类。
这个函数的调用者可以运行在任何中断级别上(any IRQL)。
10、operator PDEVICE_OBJECT – Overloaded cast to system device object pointer.
重载强制转换成PDEVICE_OBJECT类型的操作符
【函数原型】
PDEVICE_OBJECT operator PDEVICE_OBJECT( );
【Returns】
返回一个系统设备对象的指针,这个系统设备对象是KDevice类实例父对象。
【Comments】
这个函数允许编译器自动将一个KDevice类(或它的子类)的实例转化成PDEVICE_OBJECT类型。
[!警告!]:这个函数重载了一个对象的类型转换操作,而决不是把一个指针转换成一个对象。由于对象经常是动态申请内存的,所以我们必须在转换之前根据指
针找到对象本身。转换指针不产生任何代码,但是执行的是错误的动作。为了避免这种问题发生,最好的做法是在编码时忽略这种类型差异,让编译器自动转换去
吧。编译器只会在变量类型可以被正确转换的时候才去转换(否则报错),比如说,只有给定的变量是一个实例而不是指针。
这个函数的调用者可以运行在任何中断级别上(any IRQL)。
11、QueueIrp – Serializes IRP processing for a device.
给IRP排队等待StartIo例程的处理,如果队列是空的,就会同步调用StartIo。
【函数原型】
NTSTATUS QueueIrp(
KIrp I,
PDRIVER_CANCEL CancelRoutine,
PULONG Key
);
【Parameters】
I 将要被派度的IRP。
CancelRoutine 默认为NULL。如果不为NULL,将为IRP指定Cancel例程来取消它。
Key 允许调用者指定IRP被排队进去的位置。当这个参数不为NULL时,它的值指定了IRP排队位置,这个特点很少用。默认值是NULL。
【Returns】
总是返回STATUS_PENDING。
【Comments】
一个没有重写StartIo的KDevice派生类不能调用这个函数。
如果调用这个函数时设备不忙,IRP的处理将和StartIo同步执行。StartIo是在DISPATCH_LEVEL级别上运行的。
这个函数把IRP标记为挂起(pending)。
如果IRP被取消,参数CancelRoutine的值将指定一个例程给系统调用。这个回调函数的格式是:
VOID CancelRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp);
许多驱动编写人员发现把Cancel例程作为KDevice派生类的成员函数很方便。要实现这一点,需要在类的定义里声明DEVMEMBER_CANCELIRP这个宏。在一个驱动程序甚至一个设备类里都有可能出现多个Cancel例程。
这个函数返回 STATUS_PENDING 是因为一个负责在设备队列里对IRP进行排队的处理函数的返回给I/O管理器的值是STATUS_PENDING,这表示要完成IRP还要进行进一步的操作。所以,驱动程序可以这样编码排队操作:
return QueueIrp(I);
支撑它的底层系统服务是IoMarkIrpPending 和 IoStartPacket。
这个函数可以在IRQL <= DISPATCH_LEVEL的级别上被调用。
12、NextIrp – Completes an IRP and requests processing of next queued IRP.
完成指定的IRP,如果设备队列里存在下一个IRP的话就开始处理下一个IRP。
【函数原型】
VOID NextIrp(
KIrp I,
CCHAR PriorityBoost,
BOOLEAN Cancelable
);
【Parameters】
I 需要被完成IRP。
PriorityBoost 将被授予等待完成IRP操作的那个线程的优先权增量。默认是IO_NO_INCREMENT。
Cancelable 表明是否设备队列里有些IRP可以被取消(比如,存在一个Cancel例程)。这将告诉I/O管理器当要更新设备队列时是否要请求一个系统级的取消自旋锁。默认值是TRUE。
【Comments】
使用系统提供的IRP队列(StartIo)的设备经常有机会在处理IRP的最后一步用到这个函数。当一个驱动程序准备好要完成当前IRP并且使下一个
IRP出队时,这个函数刚好完成这两种操作。在调用NextIrp函数之前,调用者必须设置将要被完成的IRP的Status和Information两
个域。一段典型的代码应该是这样的:
I.Status() = STATUS_SUCCESS;
I.Information() = nBytesReturned;
NextIrp(I);
如果只是想开始处理下一个IRP,而不完成任何IRP,把第一个参数设为KIrp(0).
没有重写StartIo函数的KDevice派生类不能调用这个函数。
在进行这个函数调用的时候,调用者手里不能正在持有任何自旋锁。
支撑它的底层系统服务是IoCompleteRequest 和 IoStartNextPacket。
调用者必须运行在DISPATCH_LEVEL中断级别上。
13、CurrentIrp – Accessor to retrieve IRP currently being processed.
获得当前IRP。
【函数原型】
PIRP& CurrentIrp( void );
【Returns】
返回一个当前正在处理的IRP指针的参考(reference)。
【Comments】
这个函数假设了一个前提:设备是串行化处理IRP的,使用系统提供的设备队列并调用StartIo进行处理。当系统调用StartIo时,它设置系统设备对象的CurrenrIrp属性值为指向传给StartIo作参数的IRP的指针。
注意尽管返回值时一个PIRP的参考,驱动程序还是经常把它当作一个KIrp实例来用。所有一下的事例都是合法的:
(1) KIrp CurIrp = CurrentIrp();
(2) if ( (PIRP)I == CurrentIrp() ) {. . .}
(3) CurrentIrp() = NULL;
一个串行化处理IRP的设备可以在各种系统回调函数中调用这个CurrentIrp函数(比如DPC回调),来返回正在处理的IRP。
这个函数可以在任何中断级别上被调用(any IRQL)。驱动程序经常不改变这个属性,尤其在IRQL>DISPATCH_LEVEL级别上调用的时候。
14、Attach – Attaches a lower device ( 2 forms).
将一个设备附属于与类实例相对应的设备上。『即插即用设备不用这个函数,参见KPnpLowerDevice类』
【函数原型】
FORM 1:
NTSTATUS Attach(
PCWSTR name,
KLowerDevice * * ppLowerDevice,
BOOLEAN bInheritAlignmentReq
);
FORM 2:
NTSTATUS Attach(
KLowerDevice* pLowerDevice,
BOOLEAN bInheritAlignmentReq
);
【Parameters】
name 要附属的设备名。必须包含设备的全路径,例如:""。
ppLowerDevice 一个变量的地址,这个变量可以得到一个指向本函数(Form1)创建的KLowerDevice实例的指针。
pLowerDevice 指向要附属的设备的指针
bInheritAlignmentReq 表明调用设备是否应该继承要附属于的那个设备的对其要求(alignment requirement)。
【Returns】
返回值是一下值中的一个:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_OBJECT_TYPE_MISMATCH
STATUS_OBJECT_NAME_INVALID
STATUS_INSUFFICIENT_RESOURCES
STATUS_NO_SUCH_DEVICE
【Comments】
当一个设备把自己附属于一个底层设备(lower device)时,所有传给底层设备的IRP都要被重定向到当前设备。
一个给定的设备只能被另外一个其他的设备附属。加入设备A试图附属于设备B,而设备C已经提前附属于B了,那么A事实上附属了C。换句话说,一系列附属操
作将导致一个线性设备链,而不可能是其他复杂的拓扑结构。系统要求一个严格的设备分层结构来合理的处理IRP发送的目标次序。
Form1创建了一个KLowerDevice类(以提供的设备名为基类)的新实例。Form2需要一个已经存在的KLowerDevice类实例。
支撑它的底层系统服务是IoAttachDevice 和 IoAttachDeviceByPointer.
调用者必须运行在PASSIVE_LEVEL中断级别上。
15、Detach – Detaches a lower device.
解除以前被Attach建立的附属关系。
【函数原型】
VOID Detach( KLowerDevice* pLowerDevice );
【Parameters】
pLowerDevice 指向要解除附属的设备的指针
【Comments】
支撑它的底层系统服务是IoDetachDevice。
调用者必须运行在PASSIVE_LEVEL中断级别上。
16、CreateLink – Creates a symbolic link to the device.
创建一个到当前设备的符号连接,使应用程序能够访问它。
【函数原型】
NTSTATUS CreateLink(
PCWSTR LinkName,
BOOLEAN Protected=TRUE
);
【Parameters】
LinkName 无终结符的Unicode字符串,指定应用程序要打开设备所要使用的名字。不包括类似""的前缀。
Protected 如果是TRUE(默认值),一旦它创建了,应用程序就不能改变这个link。例如,应用程序不能使这个连接名链到另一个设备对象。
【Returns】
成功就返回STATUS_SUCCESS。
【Comments】
一个设备可以有多个符号连接向它。
连接到并口或串口(比如LPT1,COM1)应该设Protected为FALSE。
支撑它的底层系统服务为IoCreateSymbolicLink 和 IoCreateUnprotectedSymbolicLink。
调用者必须运行在PASSIVE_LEVEL中断级别上。
17、DestroyLink – Deletes a symbolic link to the device.
删除被CreateLink 创建的符号连接。如果参数为NULL,删除的是构造函数创建的符号连接。
【函数原型】
NTSTATUS DestroyLink( PCWST LinkName );
【Parameters】
LinkName 无终结符的Unicode字符串,指定应用程序要打开设备所要使用的名字。不包括类似""的前缀。为了删除构造函数创建的符号连接,把这个参数设为NULL。不要试图通过传递响应的字符创来产出构造函数创建的符号连接。
【Returns】
成功就返回STATUS_SUCCESS。
【Comments】
如果参数不为NULL,连接必须是CreateLink创建的。
支撑它的底层系统服务为IoDeleteSymbolicLink。
调用者必须运行在PASSIVE_LEVEL中断级别上。
18、CreateRegistryPath – Static member to create a canonical registry path for a given device class and unit number.
静态函数,根据指定的类名和单元号来产生一个注册表路径(以KUnitizedName的形式)。
【函数原型】
KUnitizedName* CreateRegistryPath(
PCWSTR className,
ULONG Unit
);
【Parameters】
className 没有终结符的Unicode字符串指定一个设备类。
Unit 想要获得注册表路径的设备的单元号。
【Returns】
返回指向特定设备注册表路径的指针。
【Comments】
系统给每个驱动程序分配一个注册表键,并且在初始化时传给驱动程序。按照约定,驱动应该为它的每个设备请求一个子键。这个函数提供了一种标准的机制给一个给定的设备分配注册表路径。
一个设备被它的类和单元号标定。这个类是为了驱动用来区分它管理之下的不同类型的设备。大多数驱动只有一个这样的类。这个类名必须和对应设备的
KDevice子类的C++类名相匹配,但是这并不是硬性要求。唯一的硬性要求是要使建立注册表的类名和驱动程序代码里使用的类名保持一致。
单元号(unit number)用于区分同一类设备的不同个体。细节参看 KUnitizedName 类。
对于类名 XXX 和单元号 N ,注册表路径通过给驱动程序的键里添加 "\Devices\XXX\N"
而形成。例如,如果类名是"AtoD_Controller"
,而单元号是3,那么返回的字符串应该是驱动的注册表路径后面接上"\Devices\AtoD_Controller\3"来构成。
这个函数通常从分页内存池(PagedPool)里给KUnitizedName对象分配内存,并且因此只能在IRQL < DISPATCH_LEVEL的级别上使用。
[!注意!]:调用者在不需要这个KUnitizedName 实例时有责任删除它
调用者必须运行在PASSIVE_LEVEL中断级别上。
19、ReleaseResources – Releases resources claimed for the device.
释放设备请求的资源
【函数原型】
NTSTATUS ReleaseResources( PUNICODE_STRING RegistryPath );
【Parameters】
RegistryPath 用来请求资源的路径
【Returns】
如果资源被释放就返回STATUS_SUCCESS。
【Comments】
这个函数可以释放任何先前通过调用KResourceRequest::Submit而给驱动申请的资源。注意:如果资源是分配给驱动而不是一个或几个设备,那么需要调用KDriver::ReleaseResources.
支撑它的底层系统调用是IoAssignResources。
函数可能在PASSIVE_LEVEL中断级别上被调用。
20、ReserveIrpStackLocation – Reserves IRP stack location based on requirements of a lower device.
为设备保存一个IRP栈的存储单元(location)。
【函数原型】
VOID ReserveIrpStackLocation( KLowerDevice* pLowerDevice );
【Parameters】
pLowerDevice 指向一个IRP传递的目标设备的指针
【Comments】
每个设备对象都有一个成员变量用来告诉I/O管理器,当前设备需要申请多少IRP栈空间。这些堆栈单元存储了一些参数,它们可以指明当前设备可以处理的IO请求。
假设一个接收到IRP的设备需要把它们传递给底层设备来处理。如果它只传递收到的相同IRP,那么它必须请求的堆栈单元的数量就要比底层被调用设备需要的
堆栈单元数量多1。如果有多个底层设备给当前设备来派遣分发IRP,那么它需要请求的数量应该比被调用设备中需要堆栈数量最多的那个设备多1。
假设调用设备当前需要的堆栈单元数量是S,并且底层设备通过参数pLowerDevice 指定的数量是T。那么如果S
21、InitializeDpcForIsr – Sets up a deferred callback from an interrupt service routine.
初始化一个延期回调函数(DPC)。
【函数原型】
VOID InitializeDpcForIsr( PIO_DPC_ROUTINE pRoutine );
【Parameters】
pRoutine 当延期调用被请求时需要调用的函数。
【Comments】
为了最小化中断服务程序花费的资源和执行时间,一个驱动要在较低的IRQL级别上请求一个回调函数来完成在中断处理中的非关键性的工作。系统设备对象包含一个延时调用结构体的存储空间,这个函数就初始化了这个结构体。
这是一个系统提供的延期调用(DPC)机制的特例。DPC的Context参数被初始化为系统设备对象的地址,回调函数原型为:
VOID DpcForIsrCallback(
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
为了使这个回调函数成为KDevice派生类的一个成员函数,需要使用DEVMEMBER_DPCFORISR宏。一个设备类拥有被这个宏声明的成员函数
的数量不能多于一个,因为在系统设备对象里只有一个DPC结构体。然而,设备类可以拥有任意数量的自定义DPC调用函数。具体细节参考类
KDeferredCall和宏MEMBER_DPC.
支撑它的底层系统调用是IoInitializeDpcRequest。
调用者必须运行在PASSIVE_LEVEL中断级别上。
22、RequestDpc – Queues a request for a deferred callback.
发送一个回调请求。
【函数原型】
VOID RequestDpc(
KIrp I,
PVOID Context
);
【Parameters】
I 设备的当前IRP。
Context 传给回调函数的参数。
【Comments】
这个函数的调用将为先前调用InitializeDpcForIsr()函数指定的回调函数进行排队。
支撑它的底层系统服务是IoRequestDpc。
调用者必须运行在IRQL > DISPATCH_LEVEL的中断级别上。它经常仅仅用来在中断服务程序里调用。
23、InitializeTimer – Initializes the 1 Hz timer owned by the device.
初始化一个设备的秒时钟。
【函数原型】
NTSTATUS InitializeTimer(
PIO_TIMER_ROUTINE TimerRoutine,
PVOID context
);
【Parameters】
TimerRoutine 每秒钟都要调用的函数
Context 一个无类型强制的参考数据,用来传递给时钟回调函数
【Returns】
成功就返回STATUS_SUCCESS。
【Comments】
每个系统设备对象都有一个指向时钟对象的成员变量。系统以大约1HZ的频率使这个时钟失效一次。设备有时就用这个原理测时间间隔。
这个函数指明了在1HZ时钟的每个时间片断里都要调用的函数,并且指明了这个函数的参数。只有当驱动调用了StartTimer()函数以后,时钟才会开始调用时钟回调函数。而StopTimer()函数则能命令系统停止调用时钟回调函数。
时钟回调函数的函数原型是:
VOID TimerRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
);
这个时钟函数不是KDevice的成员函数。通过插入一个宏DEVMEMBER_IOTIMER ,可以使这个函数成为KDevice派生类的一个成员函数。
这是系统提供的时钟机制的一个特例,一般的方法参看KTimer 和 KTimedCallback类.
支撑它的底层系统调用是IoInitializeTimer。
调用者必须运行在PASSIVE_LEVEL中断级别上。
24、StartTimer – Starts the 1 Hz timer.
开启设备的1HZ时钟。
【函数原型】
VOID StartTimer( void );
【Comments】
每个系统设备对象都有一个指向时钟对象的成员变量。系统以大约1HZ的频率使这个时钟失效一次。设备有时就用这个原理测时间间隔。
如果驱动已经用InitializeTimer()函数初始化了这个时钟,那么调用StartTimer将使设备开始调用由InitializeTimer的参数指定的时钟回调函数。
想要停止时钟,调用StopTimer()函数。
支撑它的底层系统调用是IoStartTimer。
调用者必须运行在IRQL <= DISPATCH_LEVEL的中断级别上。
25、StopTimer – Stops the 1 Hz timer.
停止设备的1HZ时钟。
【函数原型】
VOID StopTimer( void );
【Comments】
每个系统设备对象都有一个指向时钟对象的成员变量。系统以大约1HZ的频率使这个时钟失效一次。设备有时就用这个原理测时间间隔。
初始化时钟调用InitializeTimer函数,开启时钟调用StartTimer函数,终止时钟调用用StopTimer函数。
不要在时钟回调函数内部调用这个StopTimer函数。
支撑它的底层系统服务是IoStopTimer。
调用者必须运行在IRQL <= DISPATCH_LEVEL的中断级别上。
26、SynchronizeDmaAdapter – Queues a request to invoke a callback when exclusive access to a DMA channel is available.
当DMA通道可用时,发出一个函数回调请求
【函数原型】
NTSTATUS SynchronizeDmaAdapter(
KDmaAdapter* pAdapter,
PDRIVER_CONTROL SynchRoutine,
ULONG nMapRegisters,
PVOID Context
);
【Parameters】
pAdapter 一个指向被请求的KDmaAdapter类实例的指针。
nMapRegisters 待传输的过程需要的映射寄存器数量。
SynchRoutine 当通道可用时将调用的函数。
Context 传给回调函数的参数
【Returns】
如果参数nMapRegisters 的值超过了pAdapter->MaxRegisters()返回的可分配最大值,将返回STATUS_INSUFFICIENT_RESOURCES。否则将返回STATUS_SUCCESS。
【Comments】
如果在调用的时候,由pAdapter 指定的通道就是可用的,那么系统将立刻同步调用回调函数。如果通道暂时不可用,那么回调请求将被排队,而函数将立刻返回。
回调函数的函数原型是:
IO_ALLOCATION_ACTION (*PDRIVER_CONTROL) (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
);
系统在DISPATCH_LEVEL中断级别上调用回调函数。回调函数的返回值将告诉系统,驱动是否继续请求DMA控制器资源。如果要继续请求,就返回KeepObject,否则返回DeallocateObject。
这个回调函数不是KDevice的成员函数。要使他成为KDevice派生类的成员函数,要在派生类的定义中使用宏DEVMEMBER_CONTROLLER。
使用KDmaTransfer类来管理DMA传输的驱动不能直接调用这个函数,它通过这个类的句柄来调用。
支撑它的底层系统服务是IoAllocateAdapterChannel。
这个函数应该从IRQL <= DISPATCH_LEVEL的中断级别上调用。通常,它在KDevice派生类的StartIo例程里调用。如果不是在StartIo中调用,那么回调函数的参数Irp就不能被使用。
27、ReleaseDmaAdapter – Releases ownership of a DMA channel.
释放一个先前申请的DMA适配器(adapter)。
【函数原型】
VOID ReleaseDmaAdapter( KDmaAdapter* pDmaAdapter);
【Parameters】
pDmaAdapter 指向准备释放的适配器通道对象的指针
【Comments】
当驱动已经为它获得的通道请求服务完了时,就需要用这个函数。驱动必须先前已经通过调用SynchronizeDmaAdapter获取了通道控制权。
如果系统为SynchronizeDmaAdapter调用的回调函数返回了DeallocateObject,那说明它已经释放了通道,也就没必要调用这个函数了。
使用KDmaTransfer类来管理DMA传输的驱动不需要调用这个函数。
支撑它的底层系统调用是IoFreeAdapterChannel。
调用者必须运行在IRQL <= DISPATCH_LEVEL的中断级别上。
28、SynchronizeController – Queues a request to invoke a callback when exclusive access to a controller object is granted.
通过调用设备安排控制器资源的互斥使用。
【函数原型】
VOID SynchronizeController(
KController* pController,
PDRIVER_CONTROL SynchRoutine,
PVOID Context
);
【Parameters】
pController 指向将被获得的系统控制器对象。
SynchRoutine 当对控制器进行的使用被允许后,系统将要调用的函数。
Context 将传递给SynchRoutine的参数。
【Comments】
当被同一个驱动管理的多个社个共享一个公共资源时,一个控制器对象提供一种串行化访问这个资源的方法。驱动调用这个函数来安排一个在控制器对象可用时将被
调用的回调函数。如果控制器在调用此函数是就可用,那么系统将立刻同步调用回调函数。如果当前不可用,回调请求将被排队,且函数将返回。
回调例程(PDRIVER_CONTROL)的函数原型是:
IO_ALLOCATION_ACTION OnControllerSynched (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID SystemReserved,
IN PVOID Context
);
系统在DISPATCH_LEVEL中断级别上调用回调函数。回调函数的返回值将告诉系统,驱动是否继续请求控制器资源。如果要继续请求,就返回KeepObject,否则返回DeallocateObject。
这个回调函数不是KDevice的成员函数。要使他成为KDevice派生类的成员函数,要在派生类的定义中使用宏DEVMEMBER_CONTROLLER。
使用控制器的驱动程序通常通过设备的StartIo函数串行化处理IRP。除非驱动使用StartIo进行IRP串行化,或者在设备对象里明确的设定的CurrentIrp属性回调函数参数Irp,否则回调函数的Irp参数就是没有意义的。
支撑它的底层系统服务是IoAllocateController。
这个函数的调用者必须运行在IRQL <= DISPATCH_LEVEL的中断级别上。
29、ReleaseController – Releases ownership of a controller object.
释放一个先前申请的控制器。
【函数原型】
VOID ReleaseController( KController* pController );
【Parameters】
pController 指向要释放的系统控制器对象的指针。
【Comments】
当驱动已经为获得的控制器请求服务完毕时,就会调用这个哈数。驱动必须在先前已经调用SynchronizeController 获取了控制器的控制权。
如果系统为SynchronizeController调用的回调例程返回了DeallocateObject,那说明已经释放了控制器,也就没必要在调用这个函数了。
如果在函数被调用的时候,控制器上已经有这个设备的回调函数等待队列,那么设备将继续把持控制器。
支撑它的底层系统调用是IoFreeController。
调用者必须运行在IRQL <= DISPATCH_LEVEL的中断级别上。
30、SynchronizeInterrupt – Calls a specified routine while holding the spin lock associated with a specified interrupt object.
在提升IRQL等级后调用提供的同步化例程,并请求和中断相联系的自旋锁。
【函数原型】
BOOLEAN SynchronizeInterrupt(
KInterrupt* pInterrupt,
PKSYNCHRONIZE_ROUTINE SynchRoutine,
PVOID context
);
【Parameters】
pInterrupt 中断对象的地址
SynchRoutine 需要被调用的同步化例程的地址
SynchContext 将被传递给同步化例程的参数。
【Returns】
返回同步化例程返回的那个返回值。
【Comments】
这个函数使能一段能比中断服务例程(ISR)更安全访问数据的代码。如果驱动允许数据进入一个短暂的IRQL <=
DISPATCH_LEVEL中断状态,并且这些数据可能被中断服务例程访问,那么当中断发生使,数据将有可能被零星的截断。这个函数给驱动编写人员提供
了一种通过执行一段非中断服务代码(non-ISR),确保对共享数据实现原子访问的方法,这段代码不会被中断。
调用发生时,系统会把调用者的IRQL级别提升到和中断相联系的自旋锁的中断等级,然后获得自旋锁。这起到了封闭ISR(中断服务程序)的作用。然后系统
调用提供的SynchRoutine,将SynchContext作为一个参数传过去。这个同步化例程必须立即执行,因为它封锁了中断。
这个同步化例程的函数原型是:
BOOLEAN IntrSynchFunction(PVOID SynchronizeContext);
这个类需要驱动编写人员在KDevice派生类的定义内用一个宏DEVMEMBER_SYNCHCRITSECTION,来声明这个同步化例程的使用。
所提供的同步化函数和当前这个成员函数是完全同步执行的。比如,成员函数直到这个同步化函数完成后才返回。
当中断没有连接上的时候不能进行这种调用。
调用者必须运行在一个IRQL <= the IRQL(与中断相联系的中断级)
31、DefaultDispatch – Default successful IRP processing.
对一个IRP的默认处理。
【函数原型】
NTSTATUS DefaultDispatch( KIrp I );
【Parameters】
I 请求处理的IRP
【Returns】
返回STATUS_SUCCESS。
【Comments】
任何派遣函数(比如Create,Close,Read,Write等)都会调用这个函数来成功完成一个IRP,把IRP的信息设置为0并返回STATUS_SUCCESS。
这个函数对于不做任何事情但是需要成功完成请求的派遣者是很有用的。
32、StartIo – Serialized IRP processing function called at raised IRQL by system.
被系统调用来处理一个设备的串行化I/O请求。
【函数原型】
VOID StartIo( KIrp I );
【Parameters】
I I/O请求(IRP)
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_STARTIO。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS这个
宏自动插入到我们重写的类定义中。
系统通过提供一些函数为每个设备管理一个IRP队列的方式,来实现驱动对I/O请求的串行化处理。这个IRP队列被称为设备队列(device
queue).派遣者例程(Read,Write等)是以异步化方式接收请求的,在任意时间都可能接收到请求。如果对请求的处理需要互斥访问设备,派遣者
将用QueueIrp()函数对IRP进行排队。
QueueIrp()函数的行为依赖于设备队列的busy属性。每当QueueIrp被调用并且busy为FALSE时,系统都会立刻把IRP传给StartIo例程。如果busy属性为TRUE,系统将会把这个IRP暂时放在设备队列的尾端(tail)。
最初设备队列是空的,且busy属性为FALSE。调用QueueIrp将导致busy属性变为TRUE。在驱动程序要求出队一个IRP却发现没有可用的
IRP时,busy才变回FALSE,否则会一直保持TRUE。一个驱动程序使用NextIrp来出队下一个IRP。每当NextIrp被调用且设备队列
不为空时,设备就把下一个IRP传递给StartIo例程。
系统将在DISPATCH_LEVEL中断级别上调用这个函数。
33、SubscribeShutdownNotification – Called automatically to enable IRP_MJ_SHUTDOWN.
当系统关闭时,向设备发送一个IRP_MJ_SHUTDOWN的IRP。
【函数原型】
NTSTATUS SubscribeShutdownNotification (void );
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果function.h文件包含了#define DRIVER_FUNCTION_SHUTDOWN这个声明,KDevice的构造函数将自动调用这个函数。
支撑它的系统调用是IoRegisterShutdownNotification。
调用者必须运行在PASSIVE_LEVEL中断级上。
34、CancelShutdownNotification – Called automatically to disable IRP_MJ_SHUTDOWN.
为系统取消在关闭的时候发送IRP_MJ_SHUTDOWN这个IRP的请求。
【函数原型】
VOID CancelShutdownNotification ( void );
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果先前曾调用了SubscribeShutdownNotification函数,那么KDevice的析构函数将自动调用CancelShutdownNotification。
支撑它的系统调用是IoUnregisterShutdownNotification。
调用者必须运行在PASSIVE_LEVEL中断级上。
35、Create – Dispatcher.
主功能为IRP_MJ_CREATE的IRP的I/O请求处理函数。
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_CREATE。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS这个宏
自动插入到我们重写的类定义中。
系统调用这个函数作为对用户子系统调用(比如Win32 API CreateFile)的响应,并且是当另一个驱动附属于这个设备或包含一个指向这个设备的指针的时候。
这个函数的通常操作是设置Information为0,并完成请求,返回STATUS_SUCCESS。
驱动程序通过在没有设备打开时进行跟踪或者当驱动没有使用时开放驱动部分,可以优化它对锁定内存空间(locked memory)的使用。详细参看KImageSection类。
如果设备代表一个没有文件系统的物理或逻辑设备,这个函数可以采取检查文件系统对象的预防措施,如果文件名的长度不为0,就是这个函数调用返回失败。
36、CreateNamedPipe – Dispatcher.
主功能为IRP_MJ_CREATE_NAMED_PIPE的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS CreateNamedPipe(KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_CREATE_NAMED_PIPE。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
37、Close – Dispatcher.
主功能为IRP_MJ_CLOSE的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS Close (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define DRIVER_FUNCTION_CLOSE。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统调用这个函数作为对用户子系统调用(比如Win32 API CloseHandle)的响应,在clean-up过程中,且当另一个驱动与设备脱离的时候。
38、*** Read – Dispatcher.
主功能为IRP_MJ_READ的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS Read (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define DRIVER_FUNCTION_READ。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
高级别和一些中等级别的驱动程序在处理这个请求之前必须对参数作验证。需要读取的字节数由KIrp::ReadSize取得。对于使用buffered
I/O方式的设备,源数据缓存buffer的地址将通过调用KIrp::BufferedReadDest获得。
对于使用direct
I/O方式的设备,需要调用KIrp::Mdl函数来获取内存描述府列表。返回值可以直接转换成KMemory对象来描述客户端buffer的物理内存。
如果驱动程序需要访问这个内存,需要调用KMemory::MapToSystemSpace函数来得到一个虚拟地址。进行DMA传输的设备可以把
KMemory对象作为参数传给 KDmaTransfer::Initiate 函数。
高级别和中级别的驱动程序可以通过调用KLowerDevice::Call函数把这个请求传递给下层。如果当请求完成时驱动程序需要通知,那么它应该在
把这个请求向下层传递之前调用KIrp::SetCompletionRoutine
函数。为了把完成例程编程KDevice派生类的一个成员函数,需要使用宏MEMBER_COMPLETEIRP。
如果处理程序要串行化I/O请求给设备,那么它必须要么(1)返回QueueIrp(I,
CancelRoutine);要么(2)通过调用KIrp::MarkPending把IRP标记为未决的(pending),把它放在驱动管理的队列
上并返回STATUS_PENDING。
在完成请求前,一个驱动程序把KIrp::Information()的返回值作为要读取的字节数,这个数要小于或等于经过
KIrp::ReadSize()传输的数。使用成员函数QueueIrp()的驱动程序应该使用NextIrp()来完成请求。要不然,驱动就直接调用
KIrp::Complete()函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
39、Write – Dispatcher.
主功能为IRP_MJ_WRITE的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS Write (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define DRIVER_FUNCTION_WRITE。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
高级别和一些中等级别的驱动程序在处理这个请求之前必须对参数作验证。需要读取的字节数由KIrp::WriteSize取得。对于使用buffered
I/O方式的设备,源数据缓存buffer的地址将通过调用KIrp::BufferedWriteSource获得。
对于使用direct
I/O方式的设备,需要调用KIrp::Mdl函数来获取内存描述府列表。返回值可以直接转换成KMemory对象来描述客户端buffer的物理内存。
如果驱动程序需要访问这个内存,需要调用KMemory::MapToSystemSpace函数来得到一个虚拟地址。进行DMA传输的设备可以把
KMemory对象作为参数传给 KDmaTransfer::Initiate 函数。
高级别和中级别的驱动程序可以通过调用KLowerDevice::Call函数把这个请求传递给下层。如果当请求完成时驱动程序需要通知,那么它应该在
把这个请求向下层传递之前调用KIrp::SetCompletionRoutine
函数。为了把完成例程编程KDevice派生类的一个成员函数,需要使用宏MEMBER_COMPLETEIRP。
如果处理程序要串行化I/O请求给设备,那么它必须要么(1)返回QueueIrp(I,
CancelRoutine);要么(2)通过调用KIrp::MarkPending把IRP标记为未决的(pending),把它放在驱动管理的队列
上并返回STATUS_PENDING。
在完成请求前,一个驱动程序把KIrp::Information()的返回值作为要读取的字节数,这个数要小于或等于经过
KIrp::WriteSize()传输的数。使用成员函数QueueIrp()的驱动程序应该使用NextIrp()来完成请求。要不然,驱动就直接调
用KIrp::Complete()函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
40、QueryInformation – Dispatcher.
主功能为IRP_MJ_QUERY_INFORMATION的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS QueryInformation (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_QUERY_INFORMATION。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
41、SetInformation – Dispatcher.
主功能为IRP_MJ_SET_INFORMATION的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS SetInformation (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define DRIVER_FUNCTION_SET_INFORMATION
。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
42、QueryEa – Dispatcher.
主功能为IRP_MJ_QUERY_EA的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS QueryEa (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_QUERY_EA。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS这
个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
43、SetEa – Dispatcher.
主功能为IRP_MJ_SET_EA的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS SetEa (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_SET_EA。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS这个宏
自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
44、FlushBuffers – Dispatcher.
主功能为IRP_MJ_FLUSH_BUFFERS的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS FlushBuffers (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
这是一个KDevice类的纯虚函数。只有当function.h文件中定义了DRIVER_FUNCTION_ FLUSH_BUFFERS的时候,这个函数才会出现在KDevice类的定义里。类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
45、QueryVolumeInformation – Dispatcher.
主功能为IRP_MJ_QUERY_VOLUME_INFORMATION的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS QueryVolumeInformation (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_QUERY_VOLUME_INFORMATION。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
46、SetVolumeInformation – Dispatcher.
主功能为IRP_MJ_SET_VOLUME_INFORMATION的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS SetVolumeInformation (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_SET_VOLUME_INFORMATION。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
47、DirectoryControl – Dispatcher.
主功能为IRP_MJ_DIRECTORY_CONTROL的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS DirectoryControl (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_DIRECTORY_CONTROL。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
48、FileSystemControl – Dispatcher.
主功能为IRP_MJ_FILE_SYSTEM_CONTROL的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS FileSystemControl (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_FILE_SYSTEM_CONTROL。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
49、DeviceControl – Dispatcher.
主功能为IRP_MJ_DEVICE_CONTROL的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS DeviceControl (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_DEVICE_CONTROL。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
高级别和一些中等级别的驱动程序在处理这个请求之前必须对参数作验证。处理函数通过KIrp::IoctlCode取得I/O控制代码。如果操作使用
buffered
I/O方式(由控制码METHOD_BUFFERED指定),那么处理程序通过调用KIrp::IoctlBuffer获得一个指针作为输入和输出的共同
缓冲区。
输入缓冲区的大小由KIrp::IoctlInputBufferSize获得,输出缓冲区的大小由KIrp::IoctlOutputBufferSize获得。
如果I/O控制码由METHOD_IN_DIRECT(device to memory)或METHOD_OUT_DIRECT (memory
to
device)构成,处理函数需要调用KIrp::Mdl函数来获取内存描述府列表。返回值可以直接转换成KMemory对象来描述客户端buffer的
物理内存。如果驱动程序需要访问这个内存,需要调用KMemory::MapToSystemSpace函数来得到一个虚拟地址。进行DMA传输的设备可
以把KMemory对象作为参数传给 KDmaTransfer::Initiate 函数。
注意direct方式,DeviceIoControl的应用程序级参数的角色对于输入缓存和输出缓存来说的相反的。输入缓冲经常等价于IoctlBuffer(),而输出缓冲区经常等价与Mdl()。
高级别和中级别的驱动程序可以通过调用KLowerDevice::Call函数把这个请求传递给下层。如果当请求完成时驱动程序需要通知,那么它应该在
把这个请求向下层传递之前调用KIrp::SetCompletionRoutine
函数。为了把完成例程编程KDevice派生类的一个成员函数,需要使用宏MEMBER_COMPLETEIRP。
如果处理程序要串行化I/O请求给设备,那么它必须要么(1)返回QueueIrp(I,
CancelRoutine);要么(2)通过调用KIrp::MarkPending把IRP标记为未决的(pending),把它放在驱动管理的队列
上并返回STATUS_PENDING。
在完成请求前,一个驱动程序把KIrp::Information()的返回值作为要读取的字节数,这个数要小于或等于经过
KIrp::WriteSize()传输的数。使用成员函数QueueIrp()的驱动程序应该使用NextIrp()来完成请求。要不然,驱动就直接调
用KIrp::Complete()函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
50、InternalDeviceControl – Dispatcher.
主功能为IRP_MJ_INTERNAL_DEVICE_CONTROL的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS InternalDeviceControl (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_INTERNAL_DEVICE_CONTROL。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
请求对象的结构和DeviceControl()函数相同。内部控制请求是在驱动内部发起,和普通的在用户子系统发起的设备控制请求是相反的。
如果I/O请求代码是METHOD_NEITHER,那么输出缓存区将作为IRP的UserBuffer属性(比如,I->UserBuffer)。
系统在PASSIVE_LEVEL中断级上调用这个函数。
51、Shutdown – Dispatcher.
主功能为IRP_MJ_SHUTDOWN的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS Shutdown (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_SHUTDOWN。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS这
个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
52、LockControl – Dispatcher.
主功能为IRP_MJ_LOCK_CONTROL的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS LockControl (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_LOCK_CONTROL。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
53、CleanUp – Dispatcher.
指示驱动把所有文件对象(file object)等于所给的IRP,且未决的IRP都取消掉。
【函数原型】
NTSTATUS CleanUp (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_CLEANUP。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS这个
宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
这个函数负责把所有文件对象(file
object)等于所给的IRP,且未决的IRP都取消掉。对于每个这样的IRP,这个函数必须获得取消自旋锁(the cancel spin
lock)并设置IRP的取消例程(cancel
routine)为NULL。IRP状态必须设置为STATUS_CANCELLED,并且Information字段设置为0。许多这样的功能可以得到
实现,通过:调用成员函数DeviceQueue()获取IRP队列,调用KIrp::FileObject获取IRP文件对象,然后调用
KDeviceQueue::CleanUp函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
54、CreateMailSlot – Dispatcher.
主功能为IRP_MJ_CREATE_MAILSLOT的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS CreateMailSlot (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_CREATE_MAILSLOT。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
55、QuerySecurity – Dispatcher.
主功能为IRP_MJ_QUERY_SECURITY的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS QuerySecurity (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含#define
DRIVER_FUNCTION_QUERY_SECURITY。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
56、SetSecurity – Dispatcher.
主功能为IRP_MJ_SET_SECURITY的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS SetSecurity (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含 #define
DRIVER_FUNCTION_SET_SECURITY。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
系统在PASSIVE_LEVEL中断级上调用这个函数。
57、SetPower – Dispatcher. [DEPRECATED]
主功能为IRP_MJ_SET_POWER的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS SetPower (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
这是一个废弃过时的IRP,这个函数保留在文档里只是作为遗产来保存(向后兼容)。
58、QueryPower – Dispatcher. [DEPRECATED]
主功能为IRP_MJ_QUERY_POWER的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS QueryPower (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
这是一个废弃过时的IRP,这个函数保留在文档里只是作为遗产来保存(向后兼容)。
59、ReadWrite – Dispatcher.
主功能为IRP_MJ_READ 或 IRP_MJ_WRITE 的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS ReadWrite (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含 #define
DRIVER_FUNCTION_READWRITE。这样做对于正确建立系统驱动对象是必须的,而且可以使DEVMEMBER_DISPATCHERS
这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
因为有在处理read和write请求时有一种普通的逻辑,一些驱动编写人员选择在一个例程里同时处理这两种请求。参看Read和Write函数可以分别
了解这两种请求分开处理的方法。一个驱动如果在function.h文件里定义了DRIVER_FUNCTION_READWRITE,就不能再定义
DRIVER_FUNCTION_READ和DRIVER_FUNCTION_WRITE,否则会造成冲突。
系统在PASSIVE_LEVEL中断级上调用这个函数。
60、CreateClose – Dispatcher.
主功能为IRP_MJ_CREATE或 IRP_MJ_CLOSE的IRP的I/O请求处理函数。
【函数原型】
NTSTATUS CreateClose (KIrp I);
【Parameters】
I I/O请求(IRP)
【Returns】
成功就返回STATUS_SUCCESS,否则返回一个指明错误条件的代码。
【Comments】
如果重写这个虚函数,一定记着在function.h文件中包含 #define
DRIVER_FUNCTION_CREATECLOSE。这样做对于正确建立系统驱动对象是必须的,而且可以使
DEVMEMBER_DISPATCHERS这个宏自动插入到我们重写的类定义中。
类库自动将这个IRP派遣到这个处理函数。
一个驱动如果在function.h文件里定义了DRIVER_FUNCTION_CREATECLOSE,就不能再定义DRIVER_FUNCTION_CREATE和DRIVER_FUNCTION_CLOSE,否则会造成冲突。
这个函数允许驱动程序在一个例程里联合处理IRP_MJ_CREATE和IRP_MJ_CLOSE两种类型的IRP。如果驱动需要在处理函数里区分这两种情况,它可以调用KIrp::MajorFunction 来获取功能码(function code)。