處理 IRP 的不同方式 - 速查表
Windows 驅動程式模型 (WDM) 驅動程式通常會將輸入/輸出要求封包傳送至其他驅動程式 (IRP) 。 驅動程式會建立自己的 IRP,並將其傳送至較低的驅動程式,或驅動程式轉送它從上面附加的另一個驅動程式接收的 IRP。
本文討論驅動程式可以將 IRP 傳送至較低驅動程式的不同方式,並包含批注範例程式代碼。
- 案例 1-5 是關於如何從分派例程將 IRP 轉送到較低的驅動程式。
- 案例 6-12 討論建立 IRP 並將它傳送至另一個驅動程式的不同方式。
檢查各種案例之前,請注意 IRP 完成例程可以傳回STATUS_MORE_PROCESSING_REQUIRED或STATUS_SUCCESS。
I/O 管理員在檢查狀態時會使用下列規則:
- 如果狀態STATUS_MORE_PROCESSING_REQUIRED,請停止完成 IRP,讓堆疊位置保持不變並傳回。
- 如果狀態不是STATUS_MORE_PROCESSING_REQUIRED,請繼續向上完成 IRP。
因為 I/O 管理員不需要知道使用哪一個非STATUS_MORE_PROCESSING_REQUIRED值,請使用 STATUS_SUCCESS (,因為值 0 在大部分的處理器架構上都會有效率地載入) 。
當您閱讀下列程式代碼時,請注意,STATUS_CONTINUE_COMPLETION別名為 WDK 中的STATUS_SUCCESS。
// This value should be returned from completion routines to continue
// completing the IRP upwards. Otherwise, STATUS_MORE_PROCESSING_REQUIRED
// should be returned.
//
#define STATUS_CONTINUE_COMPLETION STATUS_SUCCESS
//
// Completion routines can also use this enumeration instead of status codes.
//
typedef enum _IO_COMPLETION_ROUTINE_RESULT {
ContinueCompletion = STATUS_CONTINUE_COMPLETION,
StopCompletion = STATUS_MORE_PROCESSING_REQUIRED
} IO_COMPLETION_ROUTINE_RESULT, *PIO_COMPLETION_ROUTINE_RESULT;
將 IRP 轉送至另一個驅動程式
案例 1:轉寄和忘記
如果驅動程式只想將 IRP 向下轉送,且不採取其他動作,請使用下列程式代碼。 在此情況下,驅動程式不需要設定完成例程。 如果驅動程式是最上層驅動程式,則根據較低驅動程式傳回的狀態,IRP 可以同步或異步完成。
NTSTATUS
DispatchRoutine_1(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
//
// You are not setting a completion routine, so just skip the stack
// location because it provides better performance.
//
IoSkipCurrentIrpStackLocation (Irp);
return IoCallDriver(TopOfDeviceStack, Irp);
}
案例 2:轉寄和等候
如果驅動程式想要將 IRP 轉送至較低的驅動程式,並等候它傳回,以便處理 IRP,請使用下列程式代碼。 處理 PNP IRP 時,通常會這麼做。 例如,當您收到 IRP_MN_START_DEVICE IRP 時,必須將 IRP 向下轉送到總線驅動程式,並等候它完成,才能啟動您的裝置。 您可以輕鬆地呼叫 IoForwardIrpSynchronous 來執行這項作業。
NTSTATUS
DispatchRoutine_2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
KEVENT event;
NTSTATUS status;
KeInitializeEvent(&event, NotificationEvent, FALSE);
//
// You are setting completion routine, so you must copy
// current stack location to the next. You cannot skip a location
// here.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
CompletionRoutine_2,
&event,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver(TopOfDeviceStack, Irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event,
Executive, // WaitReason
KernelMode, // must be Kernelmode to prevent the stack getting paged out
FALSE,
NULL // indefinite wait
);
status = Irp->IoStatus.Status;
}
// <---- Do your own work here.
//
// Because you stopped the completion of the IRP in the CompletionRoutine
// by returning STATUS_MORE_PROCESSING_REQUIRED, you must call
// IoCompleteRequest here.
//
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS
CompletionRoutine_2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
if (Irp->PendingReturned == TRUE) {
//
// You will set the event only if the lower driver has returned
// STATUS_PENDING earlier. This optimization removes the need to
// call KeSetEvent unnecessarily and improves performance because the
// system does not have to acquire an internal lock.
//
KeSetEvent ((PKEVENT) Context, IO_NO_INCREMENT, FALSE);
}
// This is the only status you can return.
return STATUS_MORE_PROCESSING_REQUIRED;
}
案例 3:轉送完成例程
在此情況下,驅動程式會設定完成例程、將 IRP 往下轉送,然後依原樣傳回較低驅動程式的狀態。 設定完成例程的目的是要在其返回時修改 IRP 的內容。
NTSTATUS
DispatchRoutine_3(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
NTSTATUS status;
//
// Because you are setting completion routine, you must copy the
// current stack location to the next. You cannot skip a location
// here.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
CompletionRoutine_31,// or CompletionRoutine_32
NULL,
TRUE,
TRUE,
TRUE
);
return IoCallDriver(TopOfDeviceStack, Irp);
}
如果您從分派例程傳回較低驅動程式的狀態:
- 您不得在完成例程中變更 IRP 的狀態。 這是為了確保 IRP 的 IoStatus 區塊中設定的狀態值 (Irp-IoStatus.Status>) 與較低驅動程式的傳回狀態相同。
- 您必須傳播 IRP 的擱置狀態,如 Irp-PendingReturned> 所指出。
- 您不得變更 IRP 的同步處理。
因此,此案例中只有 2 個有效版本的完成例程, (31 和 32) :
NTSTATUS
CompletionRoutine_31 (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
//
// Because the dispatch routine is returning the status of lower driver
// as is, you must do the following:
//
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
return STATUS_CONTINUE_COMPLETION ; // Make sure of same synchronicity
}
NTSTATUS
CompletionRoutine_32 (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
//
// Because the dispatch routine is returning the status of lower driver
// as is, you must do the following:
//
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
//
// To make sure of the same synchronicity, complete the IRP here.
// You cannot complete the IRP later in another thread because the
// the dispatch routine is returning the status returned by the lower
// driver as is.
//
IoCompleteRequest( Irp, IO_NO_INCREMENT);
//
// Although this is an unusual completion routine that you rarely see,
// it is discussed here to address all possible ways to handle IRPs.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
案例 4:佇列供稍後使用,或轉送和重複使用
在驅動程式想要將 IRP 排入佇列,並在稍後處理 IRP 或將 IRP 轉送至較低驅動程式,並在完成 IRP 之前重複使用該代碼段的特定次數,請使用下列代碼段。 分派例程會標示 IRP 擱置中,並傳回STATUS_PENDING,因為 IRP 將在稍後在不同的線程中完成。 在此,完成例程可以視需要變更 IRP 的狀態, (與先前的案例) 相反。
NTSTATUS
DispathRoutine_4(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
NTSTATUS status;
//
// You mark the IRP pending if you are intending to queue the IRP
// and process it later. If you are intending to forward the IRP
// directly, use one of the methods discussed earlier in this article.
//
IoMarkIrpPending( Irp );
//
// For demonstration purposes: this IRP is forwarded to the lower driver.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
CompletionRoutine_41, // or CompletionRoutine_42
NULL,
TRUE,
TRUE,
TRUE
);
IoCallDriver(TopOfDeviceStack, Irp);
//
// Because you marked the IRP pending, you must return pending,
// regardless of the status of returned by IoCallDriver.
//
return STATUS_PENDING ;
}
完成例程可以傳回STATUS_CONTINUE_COMPLETION或STATUS_MORE_PROCESSING_REQUIRED。 只有在您想要從另一個線程重複使用 IRP 並稍後完成時,才會傳回STATUS_MORE_PROCESSING_REQUIRED。
NTSTATUS
CompletionRoutine_41(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
//
// By returning STATUS_CONTINUE_COMPLETION , you are relinquishing the
// ownership of the IRP. You cannot touch the IRP after this.
//
return STATUS_CONTINUE_COMPLETION ;
}
NTSTATUS
CompletionRoutine_42 (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
//
// Because you are stopping the completion of the IRP by returning the
// following status, you must complete the IRP later.
//
return STATUS_MORE_PROCESSING_REQUIRED ;
}
案例 5:完成分派例程中的 IRP
此案例示範如何在分派例程中完成 IRP。
重要 當您在分派例程中完成 IRP 時,分派例程的傳回狀態應該符合 IRP (Irp-IoStatus.Status>) IoStatus 區塊中設定的值狀態。
NTSTATUS
DispatchRoutine_5(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
//
// <-- Process the IRP here.
//
Irp->IoStatus.Status = STATUS_XXX;
Irp->IoStatus.Information = YYY;
IoCompletRequest(Irp, IO_NO_INCREMENT);
return STATUS_XXX;
}
建立 IRP 並將其傳送至另一個驅動程式
簡介
在檢查案例之前,您必須了解驅動程式建立的同步輸入/輸出要求封包之間的差異, (IRP) 和異步要求。
同步 (線程) IRP | 異步 (非線程) IRP |
---|---|
使用 IoBuildSynchronousFsdRequest 或 IoBuildDeviceIoControlRequest 建立。 | 使用 IoBuildAsynchronousFsdRequest 或 IoAllocateIrp 建立。 這適用於驅動程式對驅動程序通訊。 |
線程必須等候 IRP 完成。 | 線程不需要等待 IRP 完成。 |
與建立它的線程相關聯,因此名稱為線程的 IRP。 因此,如果線程結束時,I/O 管理員會取消 IRP。 | 未與建立它的線程相關聯。 |
無法在任意線程內容中建立。 | 可以在任意線程內容中建立,因為線程不會等待 IRP 完成。 |
I/O 管理員會執行後續完成,以釋放與 IRP 相關聯的緩衝區。 | I/O 管理員無法進行清除。 驅動程式必須提供完成例程,並釋放與 IRP 相關聯的緩衝區。 |
必須在 IRQL 層級傳送等於 PASSIVE_LEVEL。 | 如果目標驅動程式的 Dispatch 例程可以在DISPATCH_LEVEL處理要求,則可以在 IRQL 中傳送小於或等於DISPATCH_LEVEL。 |
案例 6:使用 IoBuildDeviceIoControlRequest 傳送同步裝置控制要求 (IRP_MJ_INTERNAL_DEVICE_CONTROL/IRP_MJ_DEVICE_CONTROL)
下列程式代碼示範如何呼叫 IoBuildDeviceIoControlRequest 要求,以提出同步IOCTL要求。 如需詳細資訊,請參閱 IRP_MJ_INTERNAL_DEVICE_CONTROL 和 IRP_MJ_DEVICE_CONTROL。
NTSTATUS
MakeSynchronousIoctl(
IN PDEVICE_OBJECT TopOfDeviceStack,
IN ULONG IoctlControlCode,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength
)
/*++
Arguments:
TopOfDeviceStack-
IoctlControlCode - Value of the IOCTL request
InputBuffer - Buffer to be sent to the TopOfDeviceStack
InputBufferLength - Size of buffer to be sent to the TopOfDeviceStack
OutputBuffer - Buffer for received data from the TopOfDeviceStack
OutputBufferLength - Size of receive buffer from the TopOfDeviceStack
Return Value:
NT status code
--*/
{
KEVENT event;
PIRP irp;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
//
// Creating Device control IRP and send it to the another
// driver without setting a completion routine.
//
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest (
IoctlControlCode,
TopOfDeviceStack,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength,
FALSE, // External
&event,
&ioStatus);
if (NULL == irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(TopOfDeviceStack, irp);
if (status == STATUS_PENDING) {
//
// You must wait here for the IRP to be completed because:
// 1) The IoBuildDeviceIoControlRequest associates the IRP with the
// thread and if the thread exits for any reason, it would cause the IRP
// to be canceled.
// 2) The Event and IoStatus block memory is from the stack and we
// cannot go out of scope.
// This event will be signaled by the I/O manager when the
// IRP is completed.
//
status = KeWaitForSingleObject(
&event,
Executive, // wait reason
KernelMode, // To prevent stack from being paged out.
FALSE, // You are not alertable
NULL); // No time out !!!!
status = ioStatus.Status;
}
return status;
}
案例 7:傳送同步裝置控制 (IOCTL) 要求,並在特定期間內未完成時取消它
此案例與先前的案例類似,不同之處在於,如果等候逾時,它不會無限期地等候要求完成,而是等候某些使用者指定的時間,並安全地取消IOCTL要求。
typedef enum {
IRPLOCK_CANCELABLE,
IRPLOCK_CANCEL_STARTED,
IRPLOCK_CANCEL_COMPLETE,
IRPLOCK_COMPLETED
} IRPLOCK;
//
// An IRPLOCK allows for safe cancellation. The idea is to protect the IRP
// while the canceller is calling IoCancelIrp. This is done by wrapping the
// call in InterlockedExchange(s). The roles are as follows:
//
// Initiator/completion: Cancelable --> IoCallDriver() --> Completed
// Canceller: CancelStarted --> IoCancelIrp() --> CancelCompleted
//
// No cancellation:
// Cancelable-->Completed
//
// Cancellation, IoCancelIrp returns before completion:
// Cancelable --> CancelStarted --> CancelCompleted --> Completed
//
// Canceled after completion:
// Cancelable --> Completed -> CancelStarted
//
// Cancellation, IRP completed during call to IoCancelIrp():
// Cancelable --> CancelStarted -> Completed --> CancelCompleted
//
// The transition from CancelStarted to Completed tells the completer to block
// postprocessing (IRP ownership is transferred to the canceller). Similarly,
// the canceller learns it owns IRP postprocessing (free, completion, etc)
// during a Completed->CancelCompleted transition.
//
NTSTATUS
MakeSynchronousIoctlWithTimeOut(
IN PDEVICE_OBJECT TopOfDeviceStack,
IN ULONG IoctlControlCode,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength,
IN ULONG Milliseconds
)
/*++
Arguments:
TopOfDeviceStack -
IoctlControlCode - Value of the IOCTL request.
InputBuffer - Buffer to be sent to the TopOfDeviceStack.
InputBufferLength - Size of buffer to be sent to the TopOfDeviceStack.
OutputBuffer - Buffer for received data from the TopOfDeviceStack.
OutputBufferLength - Size of receive buffer from the TopOfDeviceStack.
Milliseconds - Timeout value in Milliseconds
Return Value:
NT status code
--*/
{
NTSTATUS status;
PIRP irp;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
LARGE_INTEGER dueTime;
IRPLOCK lock;
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest (
IoctlControlCode,
TopOfDeviceStack,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength,
FALSE, // External ioctl
&event,
&ioStatus);
if (irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
lock = IRPLOCK_CANCELABLE;
IoSetCompletionRoutine(
irp,
MakeSynchronousIoctlWithTimeOutCompletion,
&lock,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver(TopOfDeviceStack, irp);
if (status == STATUS_PENDING) {
dueTime.QuadPart = -10000 * Milliseconds;
status = KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE,
&dueTime
);
if (status == STATUS_TIMEOUT) {
if (InterlockedExchange((PVOID)&lock, IRPLOCK_CANCEL_STARTED) == IRPLOCK_CANCELABLE) {
//
// You got it to the IRP before it was completed. You can cancel
// the IRP without fear of losing it, because the completion routine
// does not let go of the IRP until you allow it.
//
IoCancelIrp(irp);
//
// Release the completion routine. If it already got there,
// then you need to complete it yourself. Otherwise, you got
// through IoCancelIrp before the IRP completed entirely.
//
if (InterlockedExchange(&lock, IRPLOCK_CANCEL_COMPLETE) == IRPLOCK_COMPLETED) {
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
}
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
ioStatus.Status = status; // Return STATUS_TIMEOUT
} else {
status = ioStatus.Status;
}
}
return status;
}
NTSTATUS
MakeSynchronousIoctlWithTimeOutCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PLONG lock;
lock = (PLONG) Context;
if (InterlockedExchange(lock, IRPLOCK_COMPLETED) == IRPLOCK_CANCEL_STARTED) {
//
// Main line code has got the control of the IRP. It will
// now take the responsibility of completing the IRP.
// Therefore...
return STATUS_MORE_PROCESSING_REQUIRED;
}
return STATUS_CONTINUE_COMPLETION ;
}
案例 8:使用 IoBuildSynchronousFsdRequest 傳送同步非 IOCTL 要求 - 完成例程會傳回STATUS_CONTINUE_COMPLETION
下列程式代碼示範如何藉由呼叫 IoBuildSynchronousFsdRequest 來提出同步非 IOCTL 要求。 此處所示的技術類似於案例 6。
NTSTATUS
MakeSynchronousNonIoctlRequest (
PDEVICE_OBJECT TopOfDeviceStack,
PVOID WriteBuffer,
ULONG NumBytes
)
/*++
Arguments:
TopOfDeviceStack -
WriteBuffer - Buffer to be sent to the TopOfDeviceStack.
NumBytes - Size of buffer to be sent to the TopOfDeviceStack.
Return Value:
NT status code
--*/
{
NTSTATUS status;
PIRP irp;
LARGE_INTEGER startingOffset;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PVOID context;
startingOffset.QuadPart = (LONGLONG) 0;
//
// Allocate memory for any context information to be passed
// to the completion routine.
//
context = ExAllocatePoolWithTag(NonPagedPool, sizeof(ULONG), 'ITag');
if(!context) {
return STATUS_INSUFFICIENT_RESOURCES;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildSynchronousFsdRequest(
IRP_MJ_WRITE,
TopOfDeviceStack,
WriteBuffer,
NumBytes,
&startingOffset, // Optional
&event,
&ioStatus
);
if (NULL == irp) {
ExFreePool(context);
return STATUS_INSUFFICIENT_RESOURCES;
}
IoSetCompletionRoutine(irp,
MakeSynchronousNonIoctlRequestCompletion,
context,
TRUE,
TRUE,
TRUE);
status = IoCallDriver(TopOfDeviceStack, irp);
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE, // Not alertable
NULL);
status = ioStatus.Status;
}
return status;
}
NTSTATUS
MakeSynchronousNonIoctlRequestCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
if (Context) {
ExFreePool(Context);
}
return STATUS_CONTINUE_COMPLETION ;
}
案例 9:使用 IoBuildSynchronousFsdRequest 傳送同步非 IOCTL 要求 - 完成例程會傳回STATUS_MORE_PROCESSING_REQUIRED
此案例與案例 8 的唯一差異在於完成例程會傳回STATUS_MORE_PROCESSING_REQUIRED。
NTSTATUS MakeSynchronousNonIoctlRequest2(
PDEVICE_OBJECT TopOfDeviceStack,
PVOID WriteBuffer,
ULONG NumBytes
)
/*++ Arguments:
TopOfDeviceStack
WriteBuffer - Buffer to be sent to the TopOfDeviceStack.
NumBytes - Size of buffer to be sent to the TopOfDeviceStack.
Return Value:
NT status code
--*/
{
NTSTATUS status;
PIRP irp;
LARGE_INTEGER startingOffset;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
BOOLEAN isSynchronous = TRUE;
startingOffset.QuadPart = (LONGLONG) 0;
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildSynchronousFsdRequest(
IRP_MJ_WRITE,
TopOfDeviceStack,
WriteBuffer,
NumBytes,
&startingOffset, // Optional
&event,
&ioStatus
);
if (NULL == irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
IoSetCompletionRoutine(irp,
MakeSynchronousNonIoctlRequestCompletion2,
(PVOID)&event,
TRUE,
TRUE,
TRUE);
status = IoCallDriver(TopOfDeviceStack, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event,
Executive,
KernelMode,
FALSE, // Not alertable
NULL);
status = irp->IoStatus.Status;
isSynchronous = FALSE;
}
//
// Because you have stopped the completion of the IRP, you must
// complete here and wait for it to be completed by waiting
// on the same event again, which will be signaled by the I/O
// manager.
// NOTE: you cannot queue the IRP for
// reuse by calling IoReuseIrp because it does not break the
// association of this IRP with the current thread.
//
KeClearEvent(&event);
IoCompleteRequest(irp, IO_NO_INCREMENT);
//
// We must wait here to prevent the event from going out of scope.
// I/O manager will signal the event and copy the status to our
// IoStatus block for synchronous IRPs only if the return status is not
// an error. For asynchronous IRPs, the above mentioned copy operation
// takes place regardless of the status value.
//
if (!(NT_ERROR(status) && isSynchronous)) {
KeWaitForSingleObject(&event,
Executive,
KernelMode,
FALSE, // Not alertable
NULL);
}
return status;
}
NTSTATUS MakeSynchronousNonIoctlRequestCompletion2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context )
{
if (Irp->PendingReturned) {
KeSetEvent ((PKEVENT) Context, IO_NO_INCREMENT, FALSE);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
案例 10:使用 IoBuildAsynchronousFsdRequest 傳送異步要求
此案例示範如何呼叫 IoBuildAsynchronousFsdRequest 來提出異步要求。
在異步要求中,提出要求的線程不需要等待 IRP 完成。 IRP 可以在任意線程內容中建立,因為 IRP 與線程無關。 如果您不想重複使用 IRP,您必須提供完成例程,並在完成例程中釋放緩衝區和 IRP。 這是因為 I/O 管理員無法完成以 IoBuildAsynchronousFsdRequest 和 IoAllocateIrp) 建立的驅動程式建立異步 IRP (清除。
NTSTATUS
MakeAsynchronousRequest (
PDEVICE_OBJECT TopOfDeviceStack,
PVOID WriteBuffer,
ULONG NumBytes
)
/*++
Arguments:
TopOfDeviceStack -
WriteBuffer - Buffer to be sent to the TopOfDeviceStack.
NumBytes - Size of buffer to be sent to the TopOfDeviceStack.
--*/
{
NTSTATUS status;
PIRP irp;
LARGE_INTEGER startingOffset;
PIO_STACK_LOCATION nextStack;
PVOID context;
startingOffset.QuadPart = (LONGLONG) 0;
irp = IoBuildAsynchronousFsdRequest(
IRP_MJ_WRITE,
TopOfDeviceStack,
WriteBuffer,
NumBytes,
&startingOffset, // Optional
NULL
);
if (NULL == irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Allocate memory for context structure to be passed to the completion routine.
//
context = ExAllocatePoolWithTag(NonPagedPool, sizeof(ULONG_PTR), 'ITag');
if (NULL == context) {
IoFreeIrp(irp);
return STATUS_INSUFFICIENT_RESOURCES;
}
IoSetCompletionRoutine(irp,
MakeAsynchronousRequestCompletion,
context,
TRUE,
TRUE,
TRUE);
//
// If you want to change any value in the IRP stack, you must
// first obtain the stack location by calling IoGetNextIrpStackLocation.
// This is the location that is initialized by the IoBuildxxx requests and
// is the one that the target device driver is going to view.
//
nextStack = IoGetNextIrpStackLocation(irp);
//
// Change the MajorFunction code to something appropriate.
//
nextStack->MajorFunction = IRP_MJ_SCSI;
(void) IoCallDriver(TopOfDeviceStack, irp);
return STATUS_SUCCESS;
}
NTSTATUS
MakeAsynchronousRequestCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PMDL mdl, nextMdl;
//
// If the target device object is set up to do buffered i/o
// (TopOfDeviceStack->Flags and DO_BUFFERED_IO), then
// IoBuildAsynchronousFsdRequest request allocates a system buffer
// for read and write operation. If you stop the completion of the IRP
// here, you must free that buffer.
//
if(Irp->AssociatedIrp.SystemBuffer && (Irp->Flags & IRP_DEALLOCATE_BUFFER) ) {
ExFreePool(Irp->AssociatedIrp.SystemBuffer);
}
//
// If the target device object is set up do direct i/o (DO_DIRECT_IO), then
// IoBuildAsynchronousFsdRequest creates an MDL to describe the buffer
// and locks the pages. If you stop the completion of the IRP, you must unlock
// the pages and free the MDL.
//
else if (Irp->MdlAddress != NULL) {
for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) {
nextMdl = mdl->Next;
MmUnlockPages( mdl ); IoFreeMdl( mdl ); // This function will also unmap pages.
}
Irp->MdlAddress = NULL;
}
if(Context) {
ExFreePool(Context);
}
//
// If you intend to queue the IRP and reuse it for another request,
// make sure you call IoReuseIrp(Irp, STATUS_SUCCESS) before you reuse.
//
IoFreeIrp(Irp);
//
// NOTE: this is the only status that you can return for driver-created asynchronous IRPs.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
案例 11:使用IoAllocateIrp傳送異步要求
此案例類似於先前的案例,不同之處在於,此案例會使用IoAllocateIrp函式來建立IRP,而不是呼叫IoBuildAsynchronousFsdRequest。
NTSTATUS
MakeAsynchronousRequest2(
PDEVICE_OBJECT TopOfDeviceStack,
PVOID WriteBuffer,
ULONG NumBytes
)
/*++
Arguments:
TopOfDeviceStack -
WriteBuffer - Buffer to be sent to the TopOfDeviceStack.
NumBytes - Size of buffer to be sent to the TopOfDeviceStack.
--*/
{
NTSTATUS status;
PIRP irp;
LARGE_INTEGER startingOffset;
KEVENT event;
PIO_STACK_LOCATION nextStack;
startingOffset.QuadPart = (LONGLONG) 0;
//
// Start by allocating the IRP for this request. Do not charge quota
// to the current process for this IRP.
//
irp = IoAllocateIrp( TopOfDeviceStack->StackSize, FALSE );
if (NULL == irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Obtain a pointer to the stack location of the first driver that will be
// invoked. This is where the function codes and the parameters are set.
//
nextStack = IoGetNextIrpStackLocation( irp );
nextStack->MajorFunction = IRP_MJ_WRITE;
nextStack->Parameters.Write.Length = NumBytes;
nextStack->Parameters.Write.ByteOffset= startingOffset;
if(TopOfDeviceStack->Flags & DO_BUFFERED_IO) {
irp->AssociatedIrp.SystemBuffer = WriteBuffer;
irp->MdlAddress = NULL;
} else if (TopOfDeviceStack->Flags & DO_DIRECT_IO) {
//
// The target device supports direct I/O operations. Allocate
// an MDL large enough to map the buffer and lock the pages into
// memory.
//
irp->MdlAddress = IoAllocateMdl( WriteBuffer,
NumBytes,
FALSE,
FALSE,
(PIRP) NULL );
if (irp->MdlAddress == NULL) {
IoFreeIrp( irp );
return STATUS_INSUFFICIENT_RESOURCES;
}
try {
MmProbeAndLockPages( irp->MdlAddress,
KernelMode,
(LOCK_OPERATION) (nextStack->MajorFunction == IRP_MJ_WRITE ? IoReadAccess : IoWriteAccess) );
} except(EXCEPTION_EXECUTE_HANDLER) {
if (irp->MdlAddress != NULL) {
IoFreeMdl( irp->MdlAddress );
}
IoFreeIrp( irp );
return GetExceptionCode();
}
}
IoSetCompletionRoutine(irp,
MakeAsynchronousRequestCompletion2,
NULL,
TRUE,
TRUE,
TRUE);
(void) IoCallDriver(TargetDeviceObject, irp);
return STATUS_SUCCESS;
}
NTSTATUS
MakeAsynchronousRequestCompletion2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PMDL mdl, nextMdl;
//
// Free any associated MDL.
//
if (Irp->MdlAddress != NULL) {
for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) {
nextMdl = mdl->Next;
MmUnlockPages( mdl ); IoFreeMdl( mdl ); // This function will also unmap pages.
}
Irp->MdlAddress = NULL;
}
//
// If you intend to queue the IRP and reuse it for another request,
// make sure you call IoReuseIrp(Irp, STATUS_SUCCESS) before you reuse.
//
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
案例 12:傳送異步要求,並在不同的線程中取消它
此案例示範如何一次傳送一個要求給較低的驅動程式,而不需要等待要求完成,您也可以隨時從另一個線程取消要求。
您可以記住 IRP 和其他變數,以在裝置擴充功能或裝置的全域內容結構中執行此工作,如下所示。 IRP 的狀態會在裝置擴充功能中使用 IRPLOCK 變數進行追蹤。 IrpEvent 可用來確定 IRP 已完成 (或釋放) ,再提出下一個要求。
當您處理 IRP_MN_REMOVE_DEVICE 和 IRP_MN_STOP_DEVICE PNP 要求時,這個事件也很有用,您必須在完成這些要求之前,確定沒有任何擱置的 IRP。 當您在 AddDevice 或其他初始化例程中將其初始化為同步處理事件時,此事件最適合使用。
typedef struct _DEVICE_EXTENSION{
..
PDEVICE_OBJECT TopOfDeviceStack;
PIRP PendingIrp;
IRPLOCK IrpLock; // You need this to track the state of the IRP.
KEVENT IrpEvent; // You need this to synchronize various threads.
..
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
</pre><pre class="code">
InitializeDeviceExtension( PDEVICE_EXTENSION DeviceExtension)
{
KeInitializeEvent(&DeviceExtension->IrpEvent, SynchronizationEvent, TRUE);
}
NTSTATUS
MakeASynchronousRequest3(
PDEVICE_EXTENSION DeviceExtension,
PVOID WriteBuffer,
ULONG NumBytes
)
/*++
Arguments:
DeviceExtension -
WriteBuffer - Buffer to be sent to the TargetDeviceObject.
NumBytes - Size of buffer to be sent to the TargetDeviceObject.
--*/
{
NTSTATUS status;
PIRP irp;
LARGE_INTEGER startingOffset;
PIO_STACK_LOCATION nextStack;
//
// Wait on the event to make sure that PendingIrp
// field is free to be used for the next request. If you do
// call this function in the context of the user thread,
// make sure to call KeEnterCriticialRegion before the wait to protect
// the thread from getting suspended while holding a lock.
//
KeWaitForSingleObject( &DeviceExtension->IrpEvent,
Executive,
KernelMode,
FALSE,
NULL );
startingOffset.QuadPart = (LONGLONG) 0;
//
// If the IRP is used for the same purpose every time, you can just create the IRP in the
// Initialization routine one time and reuse it by calling IoReuseIrp.
// The only thing that you have to do in the routines in this article
// is remove the lines that call IoFreeIrp and set the PendingIrp
// field to NULL. If you do so, make sure that you free the IRP
// in the PNP remove handler.
//
irp = IoBuildAsynchronousFsdRequest(
IRP_MJ_WRITE,
DeviceExtension->TopOfDeviceStack,
WriteBuffer,
NumBytes,
&startingOffset, // Optional
NULL
);
if (NULL == irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the fields relevant fields in the DeviceExtension
//
DeviceExtension->PendingIrp = irp;
DeviceExtension->IrpLock = IRPLOCK_CANCELABLE;
IoSetCompletionRoutine(irp,
MakeASynchronousRequestCompletion3,
DeviceExtension,
TRUE,
TRUE,
TRUE);
//
// If you want to change any value in the IRP stack, you must
// first obtain the stack location by calling IoGetNextIrpStackLocation.
// This is the location that is initialized by the IoBuildxxx requests and
// is the one that the target device driver is going to view.
//
nextStack = IoGetNextIrpStackLocation(irp);
//
// You could change the MajorFunction code to something appropriate.
//
nextStack->MajorFunction = IRP_MJ_SCSI;
(void) IoCallDriver(DeviceExtension->TopOfDeviceStack, irp);
return STATUS_SUCCESS;
}
NTSTATUS
MakeASynchronousRequestCompletion3(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PMDL mdl, nextMdl;
PDEVICE_EXTENSION deviceExtension = Context;
//
// If the target device object is set up to do buffered i/o
// (TargetDeviceObject->Flags & DO_BUFFERED_IO), then
// IoBuildAsynchronousFsdRequest request allocates a system buffer
// for read and write operation. If you stop the completion of the IRP
// here, you must free that buffer.
//
if(Irp->AssociatedIrp.SystemBuffer && (Irp->Flags & IRP_DEALLOCATE_BUFFER) ) {
ExFreePool(Irp->AssociatedIrp.SystemBuffer);
}
//
// If the target device object is set up to do direct i/o (DO_DIRECT_IO), then
// IoBuildAsynchronousFsdRequest creates an MDL to describe the buffer
// and locks the pages. If you stop the completion of the IRP, you must unlock
// the pages and free the MDL.
//
if (Irp->MdlAddress != NULL) {
for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) {
nextMdl = mdl->Next;
MmUnlockPages( mdl ); IoFreeMdl( mdl ); // This function will also unmap pages.
}
Irp->MdlAddress = NULL;
}
if (InterlockedExchange((PVOID)&deviceExtension->IrpLock, IRPLOCK_COMPLETED)
== IRPLOCK_CANCEL_STARTED) {
//
// Main line code has got the control of the IRP. It will
// now take the responsibility of freeing the IRP.
// Therefore...
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// If you intend to queue the IRP and reuse it for another request, make
// sure you call IoReuseIrp(Irp, STATUS_SUCCESS) before you reuse.
//
IoFreeIrp(Irp);
deviceExtension->PendingIrp = NULL; // if freed
//
// Signal the event so that the next thread in the waiting list
// can send the next request.
//
KeSetEvent (&deviceExtension->IrpEvent, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
CancelPendingIrp(
PDEVICE_EXTENSION DeviceExtension
)
/*++
This function tries to cancel the PendingIrp if it is not already completed.
Note that the IRP may not be completed and freed when the
function returns. Therefore, if you are calling this from your PNP Remove device handle,
you must wait on the IrpEvent to make sure the IRP is indeed completed
before successfully completing the remove request and allowing the driver to unload.
--*/
{
if (InterlockedExchange((PVOID)&DeviceExtension->IrpLock, IRPLOCK_CANCEL_STARTED) == IRPLOCK_CANCELABLE) {
//
// You got it to the IRP before it was completed. You can cancel
// the IRP without fear of losing it, as the completion routine
// will not let go of the IRP until you say so.
//
IoCancelIrp(DeviceExtension->PendingIrp);
//
// Release the completion routine. If it already got there,
// then you need to free it yourself. Otherwise, you got
// through IoCancelIrp before the IRP completed entirely.
//
if (InterlockedExchange((PVOID)&DeviceExtension->IrpLock, IRPLOCK_CANCEL_COMPLETE) == IRPLOCK_COMPLETED) {
IoFreeIrp(DeviceExtension->PendingIrp);
DeviceExtension->PendingIrp = NULL;
KeSetEvent(&DeviceExtension->IrpEvent, IO_NO_INCREMENT, FALSE);
}
}
return ;
}
參考資料
- 個選項 Oney。 程序設計 Windows 驅動程式模型,第二版,第 5 章。