Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Ein WDM-Treiber (Windows Driver Model) sendet in der Regel Eingabe-/Ausgabeanforderungspakete (IRPs) an andere Treiber. Ein Treiber erstellt entweder einen eigenen IRP und sendet ihn an einen niedrigeren Treiber, oder der Treiber leitet die IRPs weiter, die er von einem anderen Treiber empfängt, der oben angefügt ist.
In diesem Artikel werden verschiedene Möglichkeiten erläutert, wie ein Treiber IRPs an einen niedrigeren Treiber senden kann, und enthält kommentierten Beispielcode.
- In den Szenarien 1 bis 5 geht es darum, wie ein IRP von einer Dispatchroutine an einen niedrigeren Treiber weitergeleitet wird.
- In den Szenarien 6-12 werden verschiedene Möglichkeiten zum Erstellen eines IRP und zum Senden an einen anderen Treiber erläutert.
Bevor Sie die verschiedenen Szenarien untersuchen, beachten Sie, dass eine IRP-Vervollständigungsroutine entweder STATUS_MORE_PROCESSING_REQUIRED oder STATUS_SUCCESS zurückgeben kann.
Der E/A-Manager verwendet die folgenden Regeln, wenn er die status untersucht:
- Wenn die status STATUS_MORE_PROCESSING_REQUIRED ist, beenden Sie das Abschließen des IRP, lassen Sie den Stapelspeicherort unverändert, und kehren Sie zurück.
- Wenn der status etwas anderes als STATUS_MORE_PROCESSING_REQUIRED ist, fahren Sie mit dem Abschluss des IRP nach oben fort.
Da der E/A-Manager nicht wissen muss, welcher Nicht-STATUS_MORE_PROCESSING_REQUIRED-Wert verwendet wird, verwenden Sie STATUS_SUCCESS (da der Wert 0 in den meisten Prozessorarchitekturen effizient geladen wird).
Beachten Sie beim Lesen des folgenden Codes, dass STATUS_CONTINUE_COMPLETION im WDK als Alias für STATUS_SUCCESS verwendet wird.
// 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;
Weiterleiten eines IRP an einen anderen Treiber
Szenario 1: Weiterleiten und Vergessen
Verwenden Sie den folgenden Code, wenn ein Treiber den IRP nur nach unten weiterleiten und keine zusätzlichen Aktionen ausführen möchte. Der Treiber muss in diesem Fall keine Vervollständigungsroutine festlegen. Wenn der Treiber ein Treiber der obersten Ebene ist, kann der IRP synchron oder asynchron abgeschlossen werden, abhängig von der status, die vom niedrigeren Treiber zurückgegeben wird.
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);
}
Szenario 2: Weiterleiten und Warten
Verwenden Sie den folgenden Code, wenn ein Treiber den IRP an einen niedrigeren Treiber weiterleiten und warten möchte, bis er zurückgibt, damit er das IRP verarbeiten kann. Dies geschieht häufig bei der Behandlung von PNP-IRPs. Wenn Sie beispielsweise eine IRP_MN_START_DEVICE IRP erhalten, müssen Sie das IRP an den Bustreiber weiterleiten und warten, bis sie abgeschlossen ist, bevor Sie Ihr Gerät starten können. Sie können IoForwardIrpSynchronously aufrufen, um diesen Vorgang einfach auszuführen.
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;
}
Szenario 3: Weiterleiten mit einer Abschlussroutine
In diesem Fall legt der Treiber eine Vervollständigungsroutine fest, leitet den IRP nach unten und gibt dann den status des niedrigeren Treibers zurück. Der Zweck des Festlegens der Vervollständigungsroutine besteht darin, den Inhalt des IRP auf dem Rückweg zu ändern.
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);
}
Wenn Sie die status des niedrigeren Treibers aus Ihrer Dispatchroutine zurückgeben:
- Sie dürfen die status des IRP in der Vervollständigungsroutine nicht ändern. Dadurch wird sichergestellt, dass die im IoStatus-Block (Irp-IoStatus.Status>) des IRP festgelegten status Werte mit den rückgabebasierten status der niedrigeren Treiber übereinstimmen.
- Sie müssen die ausstehende status des IRP weitergeben, wie durch Irp-PendingReturned> angegeben.
- Sie dürfen die Synchronisierung des IRP nicht ändern.
Daher gibt es in diesem Szenario nur zwei gültige Versionen der Vervollständigungsroutine (31 und 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;
}
Szenario 4: Warteschlange für später oder Weiterleiten und Wiederverwenden
Verwenden Sie den folgenden Codeausschnitt in einer Situation, in der der Treiber entweder ein IRP in die Warteschlange stellen und später verarbeiten oder den IRP an den niedrigeren Treiber weiterleiten und für eine bestimmte Anzahl von Fällen wiederverwenden möchte, bevor das IRP abgeschlossen wird. Die Dispatchroutine markiert das ausstehende IRP und gibt STATUS_PENDING zurück, da der IRP später in einem anderen Thread abgeschlossen wird. Hier kann die Vervollständigungsroutine bei Bedarf die status des IRP ändern (im Gegensatz zum vorherigen Szenario).
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 ;
}
Die Vervollständigungsroutine kann entweder STATUS_CONTINUE_COMPLETION oder STATUS_MORE_PROCESSING_REQUIRED zurückgeben. Sie geben STATUS_MORE_PROCESSING_REQUIRED nur zurück, wenn Sie beabsichtigen, den IRP aus einem anderen Thread wiederzuverwenden und später abzuschließen.
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 ;
}
Szenario 5: Abschließen des IRP in der Dispatchroutine
In diesem Szenario wird gezeigt, wie ein IRP in der Dispatchroutine abgeschlossen wird.
Wichtig Wenn Sie eine IRP in der Dispatchroutine abschließen, sollte die rückgabebasierte status der Dispatchroutine mit dem status des Werts übereinstimmen, der im IoStatus-Block des IRP (Irp-IoStatus.Status>) festgelegt ist.
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;
}
Erstellen von IRPs und Senden an einen anderen Treiber
Einführung
Bevor Sie die Szenarien untersuchen, müssen Sie die Unterschiede zwischen einem vom Treiber erstellten synchronen Eingabe-/Ausgabeanforderungspaket (IRP) und einer asynchronen Anforderung verstehen.
Synchrone IRP (Threaded) | Asynchrone (nicht threaded) IRP |
---|---|
Erstellt mit IoBuildSynchronousFsdRequest oder IoBuildDeviceIoControlRequest . | Erstellt mit IoBuildAsynchronousFsdRequest oder IoAllocateIrp . Dies ist für die Kommunikation zwischen Fahrern und Fahrern vorgesehen. |
Der Thread muss auf den Abschluss des IRP warten. | Der Thread muss nicht auf den Abschluss des IRP warten. |
Dem Thread zugeordnet, von dem es erstellt wurde, daher der Name der IRPs im Thread. Wenn der Thread beendet wird, bricht der E/A-Manager daher die IRP ab. | Nicht dem Thread zugeordnet, der ihn erstellt hat. |
Kann nicht in einem beliebigen Threadkontext erstellt werden. | Kann in einem beliebigen Threadkontext erstellt werden, da der Thread nicht auf den Abschluss des IRP wartet. |
Der E/A-Manager führt die Nachabschlüsse durch, um den Puffer frei zu geben, der dem IRP zugeordnet ist. | Der E/A-Manager kann die Bereinigung nicht durchführen. Der Treiber muss eine Vervollständigungsroutine bereitstellen und die Puffer freigeben, die dem IRP zugeordnet sind. |
Muss auf IRQL-Ebene gleich PASSIVE_LEVEL gesendet werden. | Kann an IRQL gesendet werden, der kleiner oder gleich DISPATCH_LEVEL, wenn die Dispatchroutine des Zieltreibers die Anforderung bei DISPATCH_LEVEL verarbeiten kann. |
Szenario 6: Senden einer synchronen Gerätesteuerungsanforderung (IRP_MJ_INTERNAL_DEVICE_CONTROL/IRP_MJ_DEVICE_CONTROL) mithilfe von IoBuildDeviceIoControlRequest
Der folgende Code zeigt, wie Sie die IoBuildDeviceIoControlRequest-Anforderung aufrufen, um eine synchrone IOCTL-Anforderung auszuführen. Weitere Informationen finden Sie unter IRP_MJ_INTERNAL_DEVICE_CONTROL und 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;
}
Szenario 7: Senden einer IOCTL-Anforderung (Synchrone Gerätesteuerung) und Abbrechen der Anforderung, wenn sie nicht innerhalb eines bestimmten Zeitraums abgeschlossen ist
Dieses Szenario ähnelt dem vorherigen Szenario, mit dem Unterschied, dass es nicht unbegrenzt auf den Abschluss der Anforderung wartet, sondern eine vom Benutzer angegebene Zeit wartet und die IOCTL-Anforderung sicher abbricht, wenn die Wartezeit ein Timeout auftritt.
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 ;
}
Szenario 8: Senden einer synchronen Nicht-IOCTL-Anforderung mithilfe von IoBuildSynchronousFsdRequest – Abschlussroutine gibt STATUS_CONTINUE_COMPLETION
Der folgende Code zeigt, wie Eine synchrone Nicht-IOCTL-Anforderung durch Aufrufen von IoBuildSynchronousFsdRequest ausgeführt wird. Die hier gezeigte Technik ähnelt Szenario 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 ;
}
Szenario 9: Senden einer synchronen Nicht-IOCTL-Anforderung mithilfe von IoBuildSynchronousFsdRequest – Abschlussroutine gibt STATUS_MORE_PROCESSING_REQUIRED
Der einzige Unterschied zwischen diesem Szenario und Szenario 8 besteht darin, dass die Abschlussroutine STATUS_MORE_PROCESSING_REQUIRED zurückgibt.
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;
}
Szenario 10: Senden einer asynchronen Anforderung mithilfe von IoBuildAsynchronousFsdRequest
In diesem Szenario wird gezeigt, wie Sie eine asynchrone Anforderung ausführen, indem Sie IoBuildAsynchronousFsdRequest aufrufen.
In einer asynchronen Anforderung muss der Thread, der die Anforderung gestellt hat, nicht auf den Abschluss des IRP warten. Das IRP kann in einem beliebigen Threadkontext erstellt werden, da das IRP nicht dem Thread zugeordnet ist. Sie müssen eine Vervollständigungsroutine bereitstellen und die Puffer und IRP in der Vervollständigungsroutine freigeben, wenn Sie nicht beabsichtigen, das IRP wiederzuverwenden. Dies liegt daran, dass der E/A-Manager keine Bereinigung von vom Treiber erstellten asynchronen IRPs (erstellt mit IoBuildAsynchronousFsdRequest und IoAllocateIrp) durchführen kann.
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;
}
Szenario 11: Senden einer asynchronen Anforderung mithilfe von IoAllocateIrp
Dieses Szenario ähnelt dem vorherigen Szenario, mit der Ausnahme, dass dieses Szenario anstelle des Aufrufs von IoBuildAsynchronousFsdRequest die IoAllocateIrp-Funktion verwendet, um das IRP zu erstellen.
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;
}
Szenario 12: Senden einer asynchronen Anforderung und Abbrechen in einem anderen Thread
In diesem Szenario wird gezeigt, wie Sie jeweils eine Anforderung an einen niedrigeren Treiber senden können, ohne auf den Abschluss der Anforderung zu warten, und Sie können die Anforderung auch jederzeit über einen anderen Thread abbrechen.
Sie können sich die IRP und andere Variablen merken, um diese Arbeit in einer Geräteerweiterung oder in einer Kontextstruktur global für das Gerät zu erledigen, wie unten gezeigt. Der Status des IRP wird mit einer IRPLOCK-Variable in der Geräteerweiterung nachverfolgt. Das IrpEvent wird verwendet, um sicherzustellen, dass das IRP vollständig abgeschlossen (oder freigegeben) ist, bevor die nächste Anforderung gestellt wird.
Dieses Ereignis ist auch nützlich, wenn Sie IRP_MN_REMOVE_DEVICE und IRP_MN_STOP_DEVICE PNP-Anforderungen verarbeiten, bei denen Sie sicherstellen müssen, dass keine IRPs ausstehen, bevor Sie diese Anforderungen abschließen. Dieses Ereignis funktioniert am besten, wenn Sie es als Synchronisierungsereignis in AddDevice oder in einer anderen Initialisierungsroutine initialisieren.
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 ;
}
Referenzen
- Walter Oney. Programmieren des Windows-Treibermodells, Zweite Edition, Kapitel 5.