But du cours : maîtriser le trajet normal d’un IRP dans une pile WDM, comprendre exactement ce que contient un IRP, comment lire sa IO_STACK_LOCATION, comment transmettre une requête au driver inférieur, quand utiliser IoSkipCurrentIrpStackLocation ou IoCopyCurrentIrpStackLocationToNext, et comment fonctionnent IoCompleteRequest, les completion routines et le mécanisme STATUS_PENDING.
Ce cours ne réexplique pas DriverEntry, les IRQL, les spinlocks ou les bases d’un DEVICE_OBJECT.
1. Modèle mental d’un IRP
Un IRP représente une opération d’I/O en cours. Il est créé soit par l’I/O Manager, soit par un driver, puis il traverse une pile de drivers.
Code: Select all
Application
|
v
I/O Manager
|
v
IRP
|
v
Driver A
|
v
Driver B
|
v
Driver C
|
v
Périphérique / bus
2. Ce qu’il faut vraiment retenir de la structure IRP
Les champs les plus utiles en pratique sont :
- : résultat NTSTATUS final.
Code: Select all
Irp->IoStatus.Status - : information supplémentaire, souvent un nombre d’octets.
Code: Select all
Irp->IoStatus.Information - : indique qu’un driver inférieur a traité la requête de manière pending.
Code: Select all
Irp->PendingReturned - : indique qu’une annulation a été demandée.
Code: Select all
Irp->Cancel - : routine d’annulation installée.
Code: Select all
Irp->CancelRoutine - : MDL utilisé avec direct I/O.
Code: Select all
Irp->MdlAddress - : buffer système utilisé avec buffered I/O.
Code: Select all
Irp->AssociatedIrp.SystemBuffer - : buffer utilisateur dans certains modes.
Code: Select all
Irp->UserBuffer - : peut être utilisé pour placer l’IRP dans une liste.
Code: Select all
Irp->Tail.Overlay.ListEntry
3. IO_STACK_LOCATION : vue propre à chaque driver
Chaque driver de la pile reçoit une stack location contenant notamment :
Code: Select all
MajorFunctionCode: Select all
MinorFunctionCode: Select all
ParametersCode: Select all
DeviceObjectCode: Select all
FileObjectCode: Select all
CompletionRoutineCode: Select all
Context
Code: Select all
IoGetCurrentIrpStackLocationL’idée est la suivante :
Code: Select all
IRP
|
+-- Stack location Driver A
|
+-- Stack location Driver B
|
+-- Stack location Driver C
4. MajorFunction et MinorFunction
Le champ
Code: Select all
MajorFunctionExemples :
Code: Select all
IRP_MJ_CREATE
IRP_MJ_CLOSE
IRP_MJ_CLEANUP
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_DEVICE_CONTROL
IRP_MJ_INTERNAL_DEVICE_CONTROL
IRP_MJ_PNP
IRP_MJ_POWER
Code: Select all
MinorFunction5. Le champ Parameters
Code: Select all
stack->ParametersExemples :
Code: Select all
ULONG ioctl =
stack->Parameters.DeviceIoControl.IoControlCode;
Code: Select all
ULONG readLength =
stack->Parameters.Read.Length;
Code: Select all
ULONG writeLength =
stack->Parameters.Write.Length;
IoGetCurrentIrpStackLocation
À quoi sert cette API ?
Retourne la IO_STACK_LOCATION correspondant au driver qui traite actuellement l’IRP. C’est généralement la première fonction appelée dans une dispatch routine lorsque le driver a besoin de connaître le type exact de requête ou ses paramètres.
Quand l’utiliser ?
À utiliser dans presque toute dispatch routine qui doit lire MajorFunction, MinorFunction, un IOCTL, la taille d’une lecture, un FileObject ou un autre paramètre porté par la stack location.
Paramètres importants :
- : IRP actuellement traité.
Code: Select all
Irp
Retourne un
Code: Select all
PIO_STACK_LOCATIONIRQL / contexte :
Utilisable aux IRQL compatibles avec la routine appelante. La fonction elle-même ne bloque pas.
Propriété de l’IRP / durée de vie :
Le pointeur retourné reste lié à l’IRP. Il ne doit pas être utilisé après que l’IRP a été complété ou transmis d’une manière qui rend la stack courante invalide pour le code appelant.
Pièges classiques :
- Conserver le pointeur puis utiliser l’IRP après complétion.
- Lire la mauvaise sous-structure de Parameters.
- Supposer que FileObject est toujours non NULL.
Code: Select all
PIO_STACK_LOCATION stack =
IoGetCurrentIrpStackLocation(Irp);
if (stack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
ULONG code =
stack->Parameters.DeviceIoControl.IoControlCode;
}
À quoi sert cette API ?
Retourne la stack location qui sera utilisée par le driver inférieur. Contrairement à IoGetCurrentIrpStackLocation, cette API sert à préparer la prochaine étape de l’IRP.
Quand l’utiliser ?
À utiliser lorsqu’un driver construit lui-même un IRP ou lorsqu’il doit remplir explicitement les paramètres destinés au driver inférieur.
Paramètres importants :
- : IRP à préparer.
Code: Select all
Irp
Retourne un
Code: Select all
PIO_STACK_LOCATIONIRQL / contexte :
Ne bloque pas.
Propriété de l’IRP / durée de vie :
Le pointeur retourné appartient à la structure de l’IRP. Il ne doit pas être conservé au-delà de la durée de vie de l’IRP.
Pièges classiques :
- Modifier la next stack alors qu’un helper comme IoCopyCurrentIrpStackLocationToNext suffit.
- Oublier d’initialiser MajorFunction ou les paramètres requis lorsqu’on crée soi-même un IRP.
Code: Select all
PIO_STACK_LOCATION next =
IoGetNextIrpStackLocation(Irp);
next->MajorFunction = IRP_MJ_DEVICE_CONTROL;
next->Parameters.DeviceIoControl.IoControlCode = IOCTL_MY_DEVICE;
À quoi sert cette API ?
Termine le traitement de l’IRP au niveau courant et lance le mécanisme de complétion de l’I/O Manager. Cela peut appeler les completion routines installées dans les drivers supérieurs, puis rendre les ressources de l’IRP à l’I/O Manager lorsque la complétion arrive en haut de la pile.
Quand l’utiliser ?
À utiliser quand le driver est le dernier propriétaire de l’IRP et que l’opération est terminée, en succès, erreur ou annulation.
Paramètres importants :
- : IRP à compléter.
Code: Select all
Irp - : boost de priorité à appliquer au thread ayant initié l’I/O, souvent
Code: Select all
PriorityBoostpour un IOCTL simple.Code: Select all
IO_NO_INCREMENT
Pas de valeur de retour.
IRQL / contexte :
Peut être appelée à différents IRQL selon le type d’IRP et le chemin de complétion, mais tout le code exécuté autour doit respecter les contraintes du chemin courant.
Propriété de l’IRP / durée de vie :
Après l’appel, le driver doit considérer l’IRP comme n’étant plus sa propriété. Le pointeur ne doit plus être déréférencé.
Pièges classiques :
- Double completion.
- Accès à Irp après IoCompleteRequest.
- Compléter avec STATUS_PENDING.
- Oublier de remplir IoStatus.Information lorsque l’appelant attend une taille.
Code: Select all
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = bytesTransferred;
IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);
return STATUS_SUCCESS;
La paire :
Code: Select all
Irp->IoStatus.Status
Irp->IoStatus.Information
Exemples de Status :
Code: Select all
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_INVALID_DEVICE_REQUEST
STATUS_INSUFFICIENT_RESOURCES
STATUS_CANCELLED
STATUS_DELETE_PENDING
Code: Select all
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 512;
Code: Select all
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
7. Priority boost de IoCompleteRequest
Le deuxième argument de IoCompleteRequest sert à favoriser le thread qui attend l’I/O.
Exemples :
Code: Select all
IO_NO_INCREMENT
IO_DISK_INCREMENT
IO_KEYBOARD_INCREMENT
IO_MOUSE_INCREMENT
IO_NETWORK_INCREMENT
IO_SERIAL_INCREMENT
IO_SOUND_INCREMENT
Code: Select all
IO_NO_INCREMENT8. Transmission d’un IRP dans une pile
Un driver empilé a généralement trois choix :
- traiter et compléter l’IRP lui-même ;
- le transmettre sans traitement postérieur ;
- le transmettre et installer une completion routine.
Code: Select all
IoSkipCurrentIrpStackLocationCode: Select all
IoCopyCurrentIrpStackLocationToNextIoSkipCurrentIrpStackLocation
À quoi sert cette API ?
Avance logiquement l’IRP vers la stack location suivante sans copier la location courante. Le driver indique en pratique qu’il ne veut pas conserver sa propre stack location pour un travail de complétion.
Quand l’utiliser ?
À utiliser dans un pass-through simple : le driver ne modifie pas les paramètres, n’installe pas de completion routine et veut uniquement transmettre l’IRP au driver inférieur.
Paramètres importants :
- : IRP à transmettre.
Code: Select all
Irp
Macro / helper sans valeur de retour utile.
IRQL / contexte :
Ne bloque pas.
Propriété de l’IRP / durée de vie :
Après le futur IoCallDriver, le driver courant ne doit plus supposer qu’il possède l’IRP.
Pièges classiques :
- Utiliser IoSkipCurrentIrpStackLocation puis installer une completion routine comme si une stack dédiée avait été conservée.
- Toucher l’IRP après IoCallDriver sans garantie de propriété.
Code: Select all
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(
devExt->LowerDeviceObject,
Irp
);
À quoi sert cette API ?
Copie les paramètres pertinents de la stack location courante vers la suivante, afin que le driver inférieur reçoive la même requête tout en laissant au driver courant la possibilité d’utiliser sa propre stack location pour une completion routine.
Quand l’utiliser ?
À utiliser lorsqu’un driver transmet l’IRP mais doit exécuter du code lors de la remontée, ou lorsqu’il souhaite modifier certains paramètres dans la next stack après copie.
Paramètres importants :
- : IRP dont la stack courante doit être copiée vers la suivante.
Code: Select all
Irp
Pas de valeur de retour.
IRQL / contexte :
Ne bloque pas.
Propriété de l’IRP / durée de vie :
La stack courante reste associée au driver courant et la suivante sera utilisée par le driver inférieur.
Pièges classiques :
- Copier puis oublier d’installer la completion routine alors qu’on avait besoin d’un post-traitement.
- Modifier la mauvaise stack après la copie.
Code: Select all
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(
Irp,
CompletionRoutine,
context,
TRUE,
TRUE,
TRUE
);
return IoCallDriver(
devExt->LowerDeviceObject,
Irp
);
À quoi sert cette API ?
Envoie l’IRP au driver représenté par le DEVICE_OBJECT cible. En pratique, c’est l’équivalent du passage de relais vers le niveau inférieur de la pile.
Quand l’utiliser ?
À utiliser après avoir préparé correctement la prochaine stack location.
Paramètres importants :
- : device cible, souvent le lower device conservé dans la device extension.
Code: Select all
DeviceObject - : IRP à transmettre.
Code: Select all
Irp
Retourne un NTSTATUS. Il peut s’agir de STATUS_PENDING ou d’un autre statut. Cette valeur n’implique pas que l’IRP est encore valide pour le code appelant.
IRQL / contexte :
Peut être appelée à des IRQL élevés selon le type de requête, sous réserve que la pile appelée supporte ce contexte.
Propriété de l’IRP / durée de vie :
Le point critique : le driver inférieur peut compléter l’IRP avant que IoCallDriver ne retourne. Il faut donc considérer l’IRP comme potentiellement déjà parti dès l’appel.
Pièges classiques :
- Use-after-completion après IoCallDriver.
- Supposer que STATUS_PENDING signifie forcément que l’IRP existe encore sous la même forme.
- Appeler IoCallDriver sans préparer la next stack.
Code: Select all
IoCopyCurrentIrpStackLocationToNext(Irp);
return IoCallDriver(
devExt->LowerDeviceObject,
Irp
);
Cas 1 : le driver ne fait absolument rien après le driver inférieur.
Code: Select all
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(lower, Irp);
Code: Select all
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(
Irp,
MyCompletion,
context,
TRUE,
TRUE,
TRUE
);
return IoCallDriver(lower, Irp);
IoSetCompletionRoutine
À quoi sert cette API ?
Installe une fonction appelée lorsque l’IRP remonte après traitement par le driver inférieur. Cette routine permet au driver courant d’observer ou de modifier le résultat, libérer un contexte, signaler un événement, ou interrompre temporairement la complétion.
Quand l’utiliser ?
À utiliser lorsque le driver a besoin d’un post-traitement après le driver inférieur.
Paramètres importants :
- : IRP concerné.
Code: Select all
Irp - : callback.
Code: Select all
CompletionRoutine - : contexte privé transmis au callback.
Code: Select all
Context - Trois booléens : appel sur succès, erreur et annulation.
Pas de NTSTATUS de retour significatif pour l’installation elle-même.
IRQL / contexte :
Le callback de completion peut s’exécuter à un IRQL allant jusqu’à DISPATCH_LEVEL selon le chemin de l’IRP. Il ne faut donc pas supposer PASSIVE_LEVEL.
Propriété de l’IRP / durée de vie :
Le contexte doit rester valide jusqu’à l’exécution de la completion routine. Si le contexte pointe vers de la mémoire libérée trop tôt, le callback provoquera une corruption.
Pièges classiques :
- Utiliser un contexte alloué sur une stack qui n’existera plus.
- Bloquer dans la completion routine sans vérifier l’IRQL.
- Oublier que le DeviceObject passé au callback peut être NULL dans certains cas de création manuelle d’IRP.
Code: Select all
IoSetCompletionRoutine(
Irp,
MyCompletion,
context,
TRUE,
TRUE,
TRUE
);
À quoi sert cette API ?
Ce n’est pas une fonction mais une valeur de retour spéciale d’une completion routine. Elle indique à l’I/O Manager : « arrête la remontée ici, je prends la responsabilité de la suite ».
Quand l’utiliser ?
À utiliser lorsqu’une completion routine doit stopper temporairement la complétion normale, par exemple pour réveiller un thread qui attend et reprendre ensuite la gestion de l’IRP.
Paramètres importants :
- Aucun paramètre : c’est une valeur NTSTATUS retournée par la completion routine.
Empêche la continuation normale de la complétion à ce moment.
IRQL / contexte :
La completion routine elle-même peut s’exécuter jusqu’à DISPATCH_LEVEL.
Propriété de l’IRP / durée de vie :
Le driver qui retourne STATUS_MORE_PROCESSING_REQUIRED devient responsable de la suite de la vie de l’IRP : reprise, nettoyage et éventuellement complétion/libération.
Pièges classiques :
- Retourner STATUS_MORE_PROCESSING_REQUIRED puis oublier l’IRP.
- Le retourner sans savoir qui libérera les ressources.
Code: Select all
NTSTATUS MyCompletion(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
{
UNREFERENCED_PARAMETER(DeviceObject);
PKEVENT event = (PKEVENT)Context;
KeSetEvent(event, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
Une requête est pending lorsqu’elle ne sera pas terminée avant le retour de la routine courante.
Exemple :
Code: Select all
Dispatch
|
+--> IoMarkIrpPending
|
+--> mise en queue
|
+--> return STATUS_PENDING
... plus tard ...
DPC / thread / matériel
|
+--> IoCompleteRequest
IoMarkIrpPending
À quoi sert cette API ?
Marque dans l’IRP que la requête a été rendue asynchrone et qu’elle sera complétée plus tard. Ce marquage permet aussi la propagation correcte de l’état pending à travers les completion routines.
Quand l’utiliser ?
À utiliser lorsqu’un driver garde l’IRP au-delà du retour de sa dispatch routine, ou dans une completion routine lorsque PendingReturned doit être propagé.
Paramètres importants :
- : IRP à marquer.
Code: Select all
Irp
Pas de valeur de retour.
IRQL / contexte :
Ne bloque pas.
Propriété de l’IRP / durée de vie :
Marquer l’IRP pending ne donne pas magiquement la propriété à vie : le driver doit toujours savoir quel composant possède l’IRP après chaque transfert.
Pièges classiques :
- Oublier de le marquer avant de conserver l’IRP.
- Le faire systématiquement sans comprendre PendingReturned.
- Marquer pending un IRP déjà engagé dans une complétion qui ne doit plus être modifiée.
Code: Select all
IoMarkIrpPending(Irp);
QueueIrp(Irp);
return STATUS_PENDING;
Le champ :
Code: Select all
Irp->PendingReturned
Pattern classique dans une completion routine :
Code: Select all
if (Irp->PendingReturned)
{
IoMarkIrpPending(Irp);
}
Parce que le pending doit parfois être propagé vers le niveau supérieur afin que le thread ou le driver à l’origine de la requête ne considère pas qu’elle s’est terminée de manière synchrone.
Le couple PendingReturned + IoMarkIrpPending est un mécanisme de propagation, pas une simple convention de style.
12. IoCallDriver peut compléter immédiatement
Le cas suivant est dangereux :
Code: Select all
status = IoCallDriver(lower, Irp);
// ERREUR POTENTIELLE :
Irp->IoStatus.Status = ...
Donc :
- ne jamais supposer que l’IRP est toujours vivant après l’appel ;
- si un post-traitement est nécessaire, utiliser une completion routine ;
- si le driver doit attendre, employer un pattern synchronisé avec événement et STATUS_MORE_PROCESSING_REQUIRED.
À quoi sert cette API ?
Construit un IRP de type lecture, écriture, flush ou shutdown destiné à un autre driver, avec support d’un événement et d’un IO_STATUS_BLOCK pour une utilisation synchrone.
Quand l’utiliser ?
À utiliser lorsqu’un driver a besoin de demander une opération standard à un autre device et veut pouvoir attendre sa fin.
Paramètres importants :
- MajorFunction.
- DeviceObject cible.
- Buffer.
- Length.
- StartingOffset.
- Event.
- IoStatusBlock.
Retourne un PIRP ou NULL en cas d’échec d’allocation.
IRQL / contexte :
Le thread qui attend doit être dans un contexte permettant l’attente, généralement PASSIVE_LEVEL.
Propriété de l’IRP / durée de vie :
L’I/O Manager gère une grande partie de la vie de l’IRP construit par ce helper. Le driver doit respecter le modèle de synchronisation et ne pas libérer arbitrairement l’IRP.
Pièges classiques :
- Attendre à DISPATCH_LEVEL.
- Libérer l’IRP comme s’il venait d’IoAllocateIrp.
- Oublier de vérifier NULL.
Code: Select all
KEVENT event;
IO_STATUS_BLOCK iosb;
KeInitializeEvent(
&event,
NotificationEvent,
FALSE
);
PIRP irp = IoBuildSynchronousFsdRequest(
IRP_MJ_READ,
DeviceObject,
Buffer,
Length,
&Offset,
&event,
&iosb
);
À quoi sert cette API ?
Construit un IRP IRP_MJ_DEVICE_CONTROL ou IRP_MJ_INTERNAL_DEVICE_CONTROL destiné à un autre driver. C’est le helper classique pour envoyer un IOCTL depuis le noyau.
Quand l’utiliser ?
À utiliser lorsqu’un driver veut invoquer un IOCTL d’un autre device et éventuellement attendre le résultat.
Paramètres importants :
- IoControlCode.
- DeviceObject.
- InputBuffer / InputBufferLength.
- OutputBuffer / OutputBufferLength.
- InternalDeviceIoControl.
- Event.
- IoStatusBlock.
Retourne un PIRP ou NULL.
IRQL / contexte :
Le chemin d’attente doit respecter PASSIVE_LEVEL.
Propriété de l’IRP / durée de vie :
Le helper construit l’IRP selon un modèle géré avec l’I/O Manager ; ne pas le traiter comme un IRP librement alloué avec IoAllocateIrp.
Pièges classiques :
- Mauvaise valeur InternalDeviceIoControl.
- Attendre dans un contexte arbitraire.
- Supposer que les buffers sont toujours copiés de la même manière indépendamment de la méthode IOCTL.
Code: Select all
PIRP irp = IoBuildDeviceIoControlRequest(
IOCTL_MY_DEVICE,
DeviceObject,
inBuffer,
inLength,
outBuffer,
outLength,
FALSE,
&event,
&iosb
);
À quoi sert cette API ?
Alloue un IRP brut avec un nombre de stack locations choisi par le driver. C’est l’option la plus flexible et aussi celle qui donne le plus de responsabilités.
Quand l’utiliser ?
À utiliser lorsque les helpers IoBuild... ne conviennent pas et que le driver doit contrôler explicitement la structure de l’IRP.
Paramètres importants :
- : nombre de stack locations.
Code: Select all
StackSize - : comportement historique de quota, généralement FALSE dans les drivers modernes.
Code: Select all
ChargeQuota
Retourne un PIRP ou NULL.
IRQL / contexte :
Allocation noyau non bloquante au sens classique, mais respecter les contraintes documentées du chemin courant.
Propriété de l’IRP / durée de vie :
L’IRP appartient au driver qui l’alloue jusqu’à ce que son modèle de transfert/complétion en décide autrement. Il doit être libéré avec IoFreeIrp lorsque le driver reste responsable de sa destruction.
Pièges classiques :
- Mauvais StackSize.
- Oublier d’initialiser la prochaine stack.
- Oublier IoFreeIrp.
- Libérer deux fois.
Code: Select all
PIRP irp = IoAllocateIrp(
DeviceObject->StackSize,
FALSE
);
if (irp == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Code: Select all
IoFreeIrp(Irp);
Ne pas appeler IoFreeIrp sur un IRP appartenant à l’I/O Manager simplement parce qu’on a reçu son pointeur dans une dispatch routine.
14. Trois modèles de dispatch fondamentaux
A. Complétion locale
Code: Select all
NTSTATUS DispatchLocal(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
UNREFERENCED_PARAMETER(DeviceObject);
NTSTATUS status = STATUS_SUCCESS;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
Code: Select all
NTSTATUS DispatchPass(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PDEVICE_EXTENSION devExt =
(PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(
devExt->LowerDeviceObject,
Irp
);
}
Code: Select all
NTSTATUS DispatchObserve(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PDEVICE_EXTENSION devExt =
(PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(
Irp,
CompletionRoutine,
devExt,
TRUE,
TRUE,
TRUE
);
return IoCallDriver(
devExt->LowerDeviceObject,
Irp
);
}
- Double completion : IoCompleteRequest appelé deux fois.
- Use-after-completion : lecture/écriture dans l’IRP après complétion.
- Mauvaise propriété : deux composants pensent posséder le même IRP.
- Mauvais forwarding : next stack mal préparée.
- Mauvais pending : requête gardée sans IoMarkIrpPending.
- Mauvais contexte : attente bloquante à un IRQL incompatible.
- Mauvais contexte de completion : pointer vers une variable locale qui a disparu.
Code: Select all
IoGetCurrentIrpStackLocationCode: Select all
IoGetNextIrpStackLocationCode: Select all
IoSkipCurrentIrpStackLocationCode: Select all
IoCopyCurrentIrpStackLocationToNextCode: Select all
IoCallDriverCode: Select all
IoCompleteRequestCode: Select all
IoSetCompletionRoutineCode: Select all
IoSetCompletionRoutineExCode: Select all
IoMarkIrpPendingCode: Select all
IoBuildSynchronousFsdRequestCode: Select all
IoBuildDeviceIoControlRequestCode: Select all
IoBuildAsynchronousFsdRequestCode: Select all
IoAllocateIrpCode: Select all
IoFreeIrp
Toujours se poser ces questions :
Code: Select all
1. Qui a créé l'IRP ?
2. Qui le possède maintenant ?
3. Quelle stack location est active ?
4. Le driver traite-t-il ou forward-t-il ?
5. Une completion routine est-elle nécessaire ?
6. L'IRP peut-il survivre après le retour ?
7. Qui le complétera ?
8. Qui le libérera ?
