《Windows驱动开发技术详解》之派遣函数

驱动程序的主要功能是负责处理I/O请求,其中大部分I/O请求是在派遣函数中处理的。用户模式下所有对驱动程序的I/O请求,全部由操作系统转化为一个叫做IRP的数据结构,不同的IRP数据会被“派遣”到不同的派遣函数中。

  • IRP与派遣函数

IRP的全称是输入输出请求包。

其部分结构如下:

  1 typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _IRP {
  2     CSHORT Type;
  3     USHORT Size;
  4 
  5 
  6     //
  7     // Define the common fields used to control the IRP.
  8     //
  9 
 10     //
 11     // Define a pointer to the Memory Descriptor List (MDL) for this I/O
 12     // request.  This field is only used if the I/O is "direct I/O".
 13     //
 14 
 15     PMDL MdlAddress;
 16 
 17     //
 18     // Flags word - used to remember various flags.
 19     //
 20 
 21     ULONG Flags;
 22 
 23     //
 24     // The following union is used for one of three purposes:
 25     //
 26     //    1. This IRP is an associated IRP.  The field is a pointer to a master
 27     //       IRP.
 28     //
 29     //    2. This is the master IRP.  The field is the count of the number of
 30     //       IRPs which must complete (associated IRPs) before the master can
 31     //       complete.
 32     //
 33     //    3. This operation is being buffered and the field is the address of
 34     //       the system space buffer.
 35     //
 36 
 37     union {
 38         struct _IRP *MasterIrp;
 39         __volatile LONG IrpCount;
 40         PVOID SystemBuffer;
 41     } AssociatedIrp;
 42 
 43     //
 44     // Thread list entry - allows queueing the IRP to the thread pending I/O
 45     // request packet list.
 46     //
 47 
 48     LIST_ENTRY ThreadListEntry;
 49 
 50     //
 51     // I/O status - final status of operation.
 52     //
 53 
 54     IO_STATUS_BLOCK IoStatus;
 55 
 56     //
 57     // Requestor mode - mode of the original requestor of this operation.
 58     //
 59 
 60     KPROCESSOR_MODE RequestorMode;
 61 
 62     //
 63     // Pending returned - TRUE if pending was initially returned as the
 64     // status for this packet.
 65     //
 66 
 67     BOOLEAN PendingReturned;
 68 
 69     //
 70     // Stack state information.
 71     //
 72 
 73     CHAR StackCount;
 74     CHAR CurrentLocation;
 75 
 76     //
 77     // Cancel - packet has been canceled.
 78     //
 79 
 80     BOOLEAN Cancel;
 81 
 82     //
 83     // Cancel Irql - Irql at which the cancel spinlock was acquired.
 84     //
 85 
 86     KIRQL CancelIrql;
 87 
 88     //
 89     // ApcEnvironment - Used to save the APC environment at the time that the
 90     // packet was initialized.
 91     //
 92 
 93     CCHAR ApcEnvironment;
 94 
 95     //
 96     // Allocation control flags.
 97     //
 98 
 99     UCHAR AllocationFlags;
100 
101     //
102     // User parameters.
103     //
104 
105     PIO_STATUS_BLOCK UserIosb;
106     PKEVENT UserEvent;
107     union {
108         struct {
109             union {
110                 PIO_APC_ROUTINE UserApcRoutine;
111                 PVOID IssuingProcess;
112             };
113             PVOID UserApcContext;
114         } AsynchronousParameters;
115         LARGE_INTEGER AllocationSize;
116     } Overlay;
117 
118     //
119     // CancelRoutine - Used to contain the address of a cancel routine supplied
120     // by a device driver when the IRP is in a cancelable state.
121     //
122 
123     __volatile PDRIVER_CANCEL CancelRoutine;
124 
125     //
126     // Note that the UserBuffer parameter is outside of the stack so that I/O
127     // completion can copy data back into the user's address space without
128     // having to know exactly which service was being invoked.  The length
129     // of the copy is stored in the second half of the I/O status block. If
130     // the UserBuffer field is NULL, then no copy is performed.
131     //
132 
133     PVOID UserBuffer;
134 
135     //
136     // Kernel structures
137     //
138     // The following section contains kernel structures which the IRP needs
139     // in order to place various work information in kernel controller system
140     // queues.  Because the size and alignment cannot be controlled, they are
141     // placed here at the end so they just hang off and do not affect the
142     // alignment of other fields in the IRP.
143     //
144 
145     union {
146 
147         struct {
148 
149             union {
150 
151                 //
152                 // DeviceQueueEntry - The device queue entry field is used to
153                 // queue the IRP to the device driver device queue.
154                 //
155 
156                 KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
157 
158                 struct {
159 
160                     //
161                     // The following are available to the driver to use in
162                     // whatever manner is desired, while the driver owns the
163                     // packet.
164                     //
165 
166                     PVOID DriverContext[4];
167 
168                 } ;
169 
170             } ;
171 
172             //
173             // Thread - pointer to caller's Thread Control Block.
174             //
175 
176             PETHREAD Thread;
177 
178             //
179             // Auxiliary buffer - pointer to any auxiliary buffer that is
180             // required to pass information to a driver that is not contained
181             // in a normal buffer.
182             //
183 
184             PCHAR AuxiliaryBuffer;
185 
186             //
187             // The following unnamed structure must be exactly identical
188             // to the unnamed structure used in the minipacket header used
189             // for completion queue entries.
190             //
191 
192             struct {
193 
194                 //
195                 // List entry - used to queue the packet to completion queue, among
196                 // others.
197                 //
198 
199                 LIST_ENTRY ListEntry;
200 
201                 union {
202 
203                     //
204                     // Current stack location - contains a pointer to the current
205                     // IO_STACK_LOCATION structure in the IRP stack.  This field
206                     // should never be directly accessed by drivers.  They should
207                     // use the standard functions.
208                     //
209 
210                     struct _IO_STACK_LOCATION *CurrentStackLocation;
211 
212                     //
213                     // Minipacket type.
214                     //
215 
216                     ULONG PacketType;
217                 };
218             };
219 
220             //
221             // Original file object - pointer to the original file object
222             // that was used to open the file.  This field is owned by the
223             // I/O system and should not be used by any other drivers.
224             //
225 
226             PFILE_OBJECT OriginalFileObject;
227 
228         } Overlay;
229 
230         //
231         // APC - This APC control block is used for the special kernel APC as
232         // well as for the caller's APC, if one was specified in the original
233         // argument list.  If so, then the APC is reused for the normal APC for
234         // whatever mode the caller was in and the "special" routine that is
235         // invoked before the APC gets control simply deallocates the IRP.
236         //
237 
238         KAPC Apc;
239 
240         //
241         // CompletionKey - This is the key that is used to distinguish
242         // individual I/O operations initiated on a single file handle.
243         //
244 
245         PVOID CompletionKey;
246 
247     } Tail;
248 
249 } IRP;

用图表示数据结构如下:

对里边的部分成员进行说明。

MdlAddress(PMDL)域指向一个内存描述符表(MDL),该表描述了一个
与该请求关联的用户模式缓冲区。如果顶级设备对象的Flags域为DO_DIRECT_IO,则I/O管理器为IRP_MJ_READ或
IRP_MJ_WRITE请求创建这个MDL。如果一个IRP_MJ_DEVICE_CONTROL请求的控制代码指定METHOD_IN_DIRECT
或METHOD_OUT_DIRECT操作方式,则I/O管理器为该请求使用的输出缓冲区创建一个MDL。MDL本身用于描述用户模式虚拟缓冲区,但它同
时也含有该缓冲区锁定内存页的物理地址。为了访问用户模式缓冲区,驱动程序必须做一点额外工作。

Flags(ULONG)域包含一些对驱动程序只读的标志。但这些标志与WDM驱动程序无关。

AssociatedIrp(union)域是一个三指针联合。其中,与WDM驱动程序相关的指针是AssociatedIrp.SystemBuffer
SystemBuffer指针指向一个数据缓冲区,该缓冲区位于内核模式的非分页内存中。对于IRP_MJ_READ和IRP_MJ_WRITE操作,如
果顶级设备指定DO_BUFFERED_IO标志,则I/O管理器就创建这个数据缓冲区。对于IRP_MJ_DEVICE_CONTROL操作,如果
I/O控制功能代码指出需要缓冲区(见第九章),则I/O管理器就创建这个数据缓冲区。I/O管理器把用户模式程序发送给驱动程序的数据复制到这个缓冲
区,这也是创建IRP过程的一部分。这些数据可以是与WriteFile调用有关的数据,或者是DeviceIoControl
用中所谓的输入数据。对于读请求,设备驱动程序把读出的数据填到这个缓冲区,然后I/O管理器再把缓冲区的内容复制到用户模式缓冲区。对于指定了
METHOD_BUFFERED的I/O控制操作,驱动程序把所谓的输出数据放到这个缓冲区,然后I/O管理器再把数据复制到用户模式的输出缓冲区。

IoStatus(IO_STATUS_BLOCK)是一个仅包含两个域的结构,驱动程序在最终完成请求时设置这个结构。IoStatus.Status域将收到一个NTSTATUS代码,而IoStatus.Information
类型为ULONG_PTR,它将收到一个信息值,该信息值的确切含义要取决于具体的IRP类型和请求完成的状态。Information域的一个公认用法
是用于保存数据传输操作,如IRP_MJ_READ,的流量总计。某些PnP请求把这个域作为指向另外一个结构的指针,这个结构通常包含查询请求的结果。

RequestorMode将等于一个枚举常量UserModeKernelMode,指定原始I/O请求的来源。驱动程序有时需要查看这个值来决定是否要信任某些参数。

PendingReturned(BOOLEAN)如果为TRUE,则表明处理该IRP的最低级派遣例程返回了STATUS_PENDING。完成例程通过参考该域来避免自己与派遣例程间的潜在竞争。

Cancel(BOOLEAN)如果为TRUE,则表明IoCancelIrp已被调用,该函数用于取消这个请求。如果为FALSE,则表明没有调用IoCancelIrp函数。取消IRP是一个相对复杂的主题,我将在本章的最后详细描述它。

CancelIrql(KIRQL)是一个IRQL值,表明那个专用的取消自旋锁是在这个IRQL上获取的。当你在取消例程中释放自旋锁时应参考这个域。

CancelRoutine(PDRIVER_CANCEL)是驱动程序取消例程的地址。你应该使用IoSetCancelRoutine函数设置这个域而不是直接修改该域。

UserBuffer(PVOID)
对于METHOD_NEITHER方式的IRP_MJ_DEVICE_CONTROL请求,该域包含输出缓冲区的用户模式虚拟地址。该域还用于保存读写请
求缓冲区的用户模式虚拟地址,但指定了DO_BUFFERED_IO或DO_DIRECT_IO标志的驱动程序,其读写例程通常不需要访问这个域。当处理
一个METHOD_NEITHER控制操作时,驱动程序能用这个地址创建自己的MDL。

Tail.OverlayTail联合中的一种结构,它含有几个对WDM驱动程序有潜在用途的成员。图5-2是Tail联合的组成图。在这个图中,以水平方向从左到右是这个联合的三个可选成员,在垂直方向是每个结构的成员描述。Tail.Overlay.DeviceQueueEntry(KDEVICE_QUEUE_ENTRY)和Tail.Overlay.DriverContext(PVOID[4])是Tail.Overlayare内一个未命名联合的两个可选成员(只能出现一个)。I/O管理器把DeviceQueueEntry作为设备标准请求队列中的连接域。当IRP还没有进入某个队列时,如果你拥有这个IRP你可以使用这个域,你可以任意使用DriverContext中的四个指针。Tail.Overlay.ListEntry(LIST_ENTRY)仅能作为你自己实现的私有队列的连接域。

CurrentLocation(CHAR)和Tail.Overlay.CurrentStackLocation(PIO_STACK_LOCATION)没有公开为驱动程序使用,因为你完全可以使用象IoGetCurrentIrpStackLocation这样的函数获取这些信息。但意识到CurrentLocation就是当前I/O堆栈单元的索引以及CurrentStackLocation就是指向它的指针,会对驱动程序调试有一些帮助。

 

在DriverEntry中注册派遣函数的代码如下:

对派遣函数做简单的处理

这个派遣函数仅仅是设置了IRP的完成状态,并通过IoCompleteReques结束请求。

以ReadFile为例,其内部操作大概是这样的:

(1)ReadFile调用ntdll中的NtReadFile。其中ReadFile是Win32 API,而NtReadFile是Native API。

(2)ntdll中的NtReadFile进入到内核模式,调用系统服务中的NtReadFile函数。

(3)系统服务函数NtReadFile创建IRP_MJ_READ类型的IRP,然后将这个IRP发送到某个驱动程序的派遣函数中。NtReadFile去等待一个事件,进入“睡眠”(阻塞/挂起)状态。

(4)在派遣函数中一般会将IRP结束,这是通过IoCompleteReques函数实现的。IoCompleteRequest会设置刚才等待的时间,“睡眠”的线程被恢复运行。

通过设备链接打开设备:

在R0层编写驱动程序的设备所对应的符号链接是:

而当你在R3层编写程序要打开某个设备对象时,设备链接就要改写为:

假设我们已经加载了这个驱动程序,然后运行一个R3应用程序,代码如下:

运行结果如下:

 要编写一个更通用的派遣函数,这里不得不提及一个更重要的数据结构——IO_STACK_LOCATION,即I/O堆栈

驱动对象会创建一个个的设备对象,并将这些设备对象“叠”成一个垂直的结构。这种垂直的结构很像栈,因此被称为“设备栈”。IRP会被操作系统发送到设备栈的顶层,如果顶层的设备对象的派遣函数结束了IRP的请求,这次I/O请求就结束。如果没有将IRP的请求结束,那么操作系统将IRP转发到设备栈的下一层设备处理。如果这个设备的派遣函数依然不能结束IRP请求,则会继续向下一层设备转发。因此,一个IRP可能会被转发多次,为了记录IRP在每层设备中的操作,IRP会有一个IO_STACK_LOCATION数组。对于本层设备对应的IO_STACK_LOCATION可以通过IoGetCurrentIrpStackLocation函数得到。

IO_STACK_LOCATION的结构如下:

  1 typedef struct _IO_STACK_LOCATION {
  2     UCHAR MajorFunction;
  3     UCHAR MinorFunction;
  4     UCHAR Flags;
  5     UCHAR Control;
  6     //
  7     // The following user parameters are based on the service that is being
  8     // invoked.  Drivers and file systems can determine which set to use based
  9     // on the above major and minor function codes.
 10     //
 11     union {
 12         //
 13         // System service parameters for:  NtCreateFile
 14         //
 15         struct {
 16             PIO_SECURITY_CONTEXT SecurityContext;
 17             ULONG Options;
 18             USHORT POINTER_ALIGNMENT FileAttributes;
 19             USHORT ShareAccess;
 20             ULONG POINTER_ALIGNMENT EaLength;
 21         } Create;
 22 
 23         //
 24         // System service parameters for:  NtReadFile
 25         //
 26         struct {
 27             ULONG Length;
 28             ULONG POINTER_ALIGNMENT Key;
 29             LARGE_INTEGER ByteOffset;
 30         } Read;
 31         //
 32         // System service parameters for:  NtWriteFile
 33         //
 34         struct {
 35             ULONG Length;
 36             ULONG POINTER_ALIGNMENT Key;
 37             LARGE_INTEGER ByteOffset;
 38         } Write;
 39 
 40         //
 41         // System service parameters for:  NtQueryInformationFile
 42         //
 43         struct {
 44             ULONG Length;
 45             FILE_INFORMATION_CLASS POINTER_ALIGNMENT FileInformationClass;
 46         } QueryFile;
 47         //
 48         // System service parameters for:  NtSetInformationFile
 49         //
 50         struct {
 51             ULONG Length;
 52             FILE_INFORMATION_CLASS POINTER_ALIGNMENT FileInformationClass;
 53             PFILE_OBJECT FileObject;
 54             union {
 55                 struct {
 56                     BOOLEAN ReplaceIfExists;
 57                     BOOLEAN AdvanceOnly;
 58                 };
 59                 ULONG ClusterCount;
 60                 HANDLE DeleteHandle;
 61             };
 62         } SetFile;
 63 
 64         //
 65         // System service parameters for:  NtQueryVolumeInformationFile
 66         //
 67         struct {
 68             ULONG Length;
 69             FS_INFORMATION_CLASS POINTER_ALIGNMENT FsInformationClass;
 70         } QueryVolume;
 71 
 72         //
 73         // System service parameters for:  NtFlushBuffersFile
 74         //
 75         // No extra user-supplied parameters.
 76         //
 77 
 78         //
 79         // System service parameters for:  NtDeviceIoControlFile
 80         //
 81         // Note that the user's output buffer is stored in the UserBuffer field
 82         // and the user's input buffer is stored in the SystemBuffer field.
 83         //
 84         struct {
 85             ULONG OutputBufferLength;
 86             ULONG POINTER_ALIGNMENT InputBufferLength;
 87             ULONG POINTER_ALIGNMENT IoControlCode;
 88             PVOID Type3InputBuffer;
 89         } DeviceIoControl;
 90 // end_wdm
 91         //
 92         // System service parameters for:  NtQuerySecurityObject
 93         //
 94         struct {
 95             SECURITY_INFORMATION SecurityInformation;
 96             ULONG POINTER_ALIGNMENT Length;
 97         } QuerySecurity;
 98         //
 99         // System service parameters for:  NtSetSecurityObject
100         //
101         struct {
102             SECURITY_INFORMATION SecurityInformation;
103             PSECURITY_DESCRIPTOR SecurityDescriptor;
104         } SetSecurity;
105 // begin_wdm
106         //
107         // Non-system service parameters.
108         //
109         // Parameters for MountVolume
110         //
111         struct {
112             PVPB Vpb;
113             PDEVICE_OBJECT DeviceObject;
114         } MountVolume;
115         //
116         // Parameters for VerifyVolume
117         //
118         struct {
119             PVPB Vpb;
120             PDEVICE_OBJECT DeviceObject;
121         } VerifyVolume;
122         //
123         // Parameters for Scsi with internal device contorl.
124         //
125         struct {
126             struct _SCSI_REQUEST_BLOCK *Srb;
127         } Scsi;
128 
129         //
130         // Parameters for IRP_MN_QUERY_DEVICE_RELATIONS
131         //
132         struct {
133             DEVICE_RELATION_TYPE Type;
134         } QueryDeviceRelations;
135         //
136         // Parameters for IRP_MN_QUERY_INTERFACE
137         //
138         struct {
139             CONST GUID *InterfaceType;
140             USHORT Size;
141             USHORT Version;
142             PINTERFACE Interface;
143             PVOID InterfaceSpecificData;
144         } QueryInterface;
145 // end_ntifs
146         //
147         // Parameters for IRP_MN_QUERY_CAPABILITIES
148         //
149         struct {
150             PDEVICE_CAPABILITIES Capabilities;
151         } DeviceCapabilities;
152         //
153         // Parameters for IRP_MN_FILTER_RESOURCE_REQUIREMENTS
154         //
155         struct {
156             PIO_RESOURCE_REQUIREMENTS_LIST IoResourceRequirementList;
157         } FilterResourceRequirements;
158         //
159         // Parameters for IRP_MN_READ_CONFIG and IRP_MN_WRITE_CONFIG
160         //
161         struct {
162             ULONG WhichSpace;
163             PVOID Buffer;
164             ULONG Offset;
165             ULONG POINTER_ALIGNMENT Length;
166         } ReadWriteConfig;
167         //
168         // Parameters for IRP_MN_SET_LOCK
169         //
170         struct {
171             BOOLEAN Lock;
172         } SetLock;
173         //
174         // Parameters for IRP_MN_QUERY_ID
175         //
176         struct {
177             BUS_QUERY_ID_TYPE IdType;
178         } QueryId;
179         //
180         // Parameters for IRP_MN_QUERY_DEVICE_TEXT
181         //
182         struct {
183             DEVICE_TEXT_TYPE DeviceTextType;
184             LCID POINTER_ALIGNMENT LocaleId;
185         } QueryDeviceText;
186         //
187         // Parameters for IRP_MN_DEVICE_USAGE_NOTIFICATION
188         //
189         struct {
190             BOOLEAN InPath;
191             BOOLEAN Reserved[3];
192             DEVICE_USAGE_NOTIFICATION_TYPE POINTER_ALIGNMENT Type;
193         } UsageNotification;
194         //
195         // Parameters for IRP_MN_WAIT_WAKE
196         //
197         struct {
198             SYSTEM_POWER_STATE PowerState;
199         } WaitWake;
200         //
201         // Parameter for IRP_MN_POWER_SEQUENCE
202         //
203         struct {
204             PPOWER_SEQUENCE PowerSequence;
205         } PowerSequence;
206         //
207         // Parameters for IRP_MN_SET_POWER and IRP_MN_QUERY_POWER
208         //
209         struct {
210             ULONG SystemContext;
211             POWER_STATE_TYPE POINTER_ALIGNMENT Type;
212             POWER_STATE POINTER_ALIGNMENT State;
213             POWER_ACTION POINTER_ALIGNMENT ShutdownType;
214         } Power;
215         //
216         // Parameters for StartDevice
217         //
218         struct {
219             PCM_RESOURCE_LIST AllocatedResources;
220             PCM_RESOURCE_LIST AllocatedResourcesTranslated;
221         } StartDevice;
222 // begin_ntifs
223         //
224         // Parameters for Cleanup
225         //
226         // No extra parameters supplied
227         //
228         //
229         // WMI Irps
230         //
231         struct {
232             ULONG_PTR ProviderId;
233             PVOID DataPath;
234             ULONG BufferSize;
235             PVOID Buffer;
236         } WMI;
237         //
238         // Others - driver-specific
239         //
240         struct {
241             PVOID Argument1;
242             PVOID Argument2;
243             PVOID Argument3;
244             PVOID Argument4;
245         } Others;
246     } Parameters;
247     //
248     // Save a pointer to this device driver's device object for this request
249     // so it can be passed to the completion routine if needed.
250     //
251     PDEVICE_OBJECT DeviceObject;
252     //
253     // The following location contains a pointer to the file object for this
254     //
255     PFILE_OBJECT FileObject;
256     //
257     // The following routine is invoked depending on the flags in the above
258     // flags field.
259     //
260     PIO_COMPLETION_ROUTINE CompletionRoutine;
261     //
262     // The following is used to store the address of the context parameter
263     // that should be passed to the CompletionRoutine.
264     //
265     PVOID Context;
266 } IO_STACK_LOCATION, *PIO_STACK_LOCATION;

任何内核模式程序在创建一个IRP时,同时还创建了一个与之关联的 IO_STACK_LOCATION结构数组数组中的每个堆栈单元都对应一个将处理该IRP的驱动程序,而每一个I/O堆栈单元(它是一个数组)对应着这个驱动所关联的多个设备对象(数组中的每个元素对应着一个设备对象)

IO_STACK_LOCATION结构简化表示如下:

其成员介绍如下:

MajorFunction(UCHAR)是该IRP的主功能码。这个代码应该为类似 IRP_MJ_READ一样的值,并与驱动程序对象中MajorFunction表的某个派遣函数指针相对应。如果该代码存在于某个特殊驱动程序的I/O堆栈单元中,它有可能一开始是,例如IRP_MJ_READ,而后被驱动程序转换成其它代码,并沿着驱动程序堆栈发送到低层驱动程序。我将在第十一章(USB总线)中举一个这样的例子,USB驱动程序把标准的读或写请求转换成内部控制操作,以便向USB总线驱动程序提交请求。 
  MinorFunction(UCHAR)是该IRP的副功能码。它进一步指出该IRP属于哪个主功能类。例如,IRP_MJ_PNP请求就有约一打的副功能码,如IRP_MN_START_DEVICE、IRP_MN_REMOVE_DEVICE,等等。 
  Parameters(union)是几个子结构的联合,每个请求类型都有自己专用的参数,而每个子结构就是一种参数。这些子结构包括Create(IRP_MJ_CREATE请求)、Read(IRP_MJ_READ请求)、StartDevice(IRP_MJ_PNP的IRP_MN_START_DEVICE子类型),等等。 
  DeviceObject(PDEVICE_OBJECT)是与该堆栈单元对应的设备对象的地址。该域由IoCallDriver函数负责填写。 
  FileObject(PFILE_OBJECT)是内核文件对象的地址,IRP的目标就是这个文件对象。驱动程序通常在处理清除请求(IRP_MJ_CLEANUP)时使用FileObject指针,以区分队列中与该文件对象无关的IRP。 
  CompletionRoutine(PIO_COMPLETION_ROUTINE)是一个I/O完成例程的地址,该地址是由与这个堆栈单元对应的驱动程序的更上一层驱动程序设置的。你绝对不要直接设置这个域,应该调用IoSetCompletionRoutine函数,该函数知道如何参考下一层驱动程序的堆栈单元。设备堆栈的最低一级驱动程序并不需要完成例程,因为它们必须直接完成请求。然而,请求的发起者有时确实需要一个完成例程,但通常没有自己的堆栈单元。这就是为什么每一级驱动程序都使用下一级驱动程序的堆栈单元保存自己完成例程指针的原因。 
  Context(PVOID)是一个任意的与上下文相关的值,将作为参数传递给完成例程。你绝对不要直接设置该域;它由IoSetCompletionRoutine函数自动设置,其值来自该函数的某个参数。

IRP结合IO_STACK_LOCATION的结构如下:

举个演示派遣函数获取IRP的类型,代码如下:

 1 NTSTATUS HelloDDKDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp){
 2     UNREFERENCED_PARAMETER(pDevObj);
 3     DbgPrint("HelloDDKDispatchRoutine!\n");
 4     PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
 5     static char* irpname[] = {
 6         "IRP_MJ_CREATE",
 7         "IRP_MJ_CREATE_NAMED_PIPE",
 8         "IRP_MJ_CLOSE",
 9         "IRP_MJ_READ",
10         "IRP_MJ_WRITE",
11         "IRP_MJ_QUERY_INFORMATION"
12     };
13     UCHAR type = stack->MajorFunction;
14     if (type >= ARRAYSIZE(irpname)){
15         DbgPrint("Unknown IRP!");
16     }
17     else{
18         DbgPrint("IRP:%s", irpname[type]);
19     }
20     NTSTATUS status = STATUS_SUCCESS;
21     pIrp->IoStatus.Status = status;
22     pIrp->IoStatus.Information = 0;
23     IoCompleteRequest(pIrp, IO_NO_INCREMENT);
24     return status;
25 }

创建了设备对象之后,再用之前的R3层应用去打开这个设备对象,就会输出

 

posted @ 2016-05-26 11:26  _No.47  阅读(1686)  评论(0编辑  收藏  举报