Inscription d’une routine IoCompletion
Pour inscrire une routine IoCompletion, une routine de répartition appelle IoSetCompletionRoutine, en fournissant l’adresse de la routine IoCompletion et l’IRP qu’elle transmettra par la suite aux pilotes inférieurs à l’aide d’IoCallDriver.
Lorsqu’il appelle IoSetCompletionRoutine, la routine de répartition spécifie les circonstances dans lesquelles le gestionnaire d’E/S doit appeler la routine IoCompletion spécifiée. Vous pouvez choisir d’appeler la routine IoCompletion si un pilote de niveau inférieur termine correctement l’IRP (InvokeOnSuccess), termine l’IRP avec une erreur status valeur (InvokeOnError) ou annule l’IRP (InvokeOnCancel), dans n’importe quelle combinaison.
L’objectif d’une routine IoCompletion est de surveiller ce que les pilotes de niveau inférieur ont fait avec l’IRP et d’effectuer un traitement d’achèvement supplémentaire, si nécessaire. Plus précisément, les utilisations les plus courantes pour les routines IoCompletion d’un pilote sont les suivantes :
Pour supprimer un IRP alloué par le pilote avec IoAllocateIrp ou IoBuildAsynchronousFsdRequest
Tout pilote de niveau supérieur qui alloue une IRP à l’aide de l’une de ces routines de support doit fournir une routine IoCompletion pour cette IRP. La routine IoCompletion doit appeler IoFreeIrp pour supprimer les irps alloués par le pilote.
Pour réutiliser un IRP entrant pour demander que les pilotes inférieurs effectuent un certain nombre d’opérations, telles que des transferts partiels, jusqu’à ce que la demande d’origine puisse être satisfaite et terminée par la routine IoCompletion
Pour réessayer une demande indiquant qu’un pilote inférieur s’est terminé avec une erreur
Les pilotes de niveau supérieur, tels que les systèmes de fichiers, sont plus susceptibles d’avoir des routines IoCompletion qui tentent de réessayer des demandes que les pilotes intermédiaires, à l’exception éventuellement des pilotes de classe superposés au-dessus d’un pilote de port étroitement couplé. Toutefois, tout pilote intermédiaire utilise des routines IoCompletion pour réessayer les demandes.
Alors que la routine DispatchReadWrite d’un pilote de niveau supérieur ou intermédiaire est la plus susceptible de traiter les IRP qui nécessitent une routine IoCompletion , toute routine de répartition dans n’importe quel pilote qui transmet des IRP aux pilotes inférieurs peut inscrire une routine IoCompletion .
Pour les IRP alloués par le pilote et les IRP réutilisés, la routine de répartition doit appeler IoSetCompletionRoutine avec les paramètres booléens suivants :
InvokeOnSuccess défini sur TRUE
InvokeOnError défini sur TRUE
InvokeOnCancel défini sur TRUE si un pilote inférieur de la chaîne peut gérer les irps annulables
En règle générale, InvokeOnCancel est défini sur TRUE, qu’un IRP puisse être retourné avec STATUS_CANCELLED, pour s’assurer que la routine IoCompletion libère chaque IRP allouée par le pilote ou vérifie l’achèvement status de chaque réutilisation d’un IRP.
Une routine de répartition qui alloue des irps pour les pilotes inférieurs à l’aide de IoAllocateIrp ou IoBuildAsynchronousFsdRequestdoit définir une routine IoCompletion pour chaque IRP allouée par le pilote.
La routine de répartition doit configurer l’état à la fois sur l’IRP d’origine et ses IRP alloués pour que la routine IoCompletion utilise. Au minimum, la routine IoCompletion doit accéder à l’IRP d’origine et le nombre d’irps supplémentaires qui ont été alloués.
La routine de dispatch doit appeler IoSetCompletionRoutine avec tous les paramètres InvokeOnXxx définis sur TRUE pour les IRP qu’elle alloue.
Une routine de répartition qui réutilise des irps pour une séquence d’opérations, ou qui retente une opération d’E/S, doit appeler IoSetCompletionRoutine pour chaque IRP qui sera réutilisé ou retenté.
La routine de répartition doit enregistrer les informations d’état de l’IRP d’origine pour une utilisation ultérieure par la routine IoCompletion .
Par exemple, une routine DispatchReadWrite doit enregistrer les paramètres de transfert appropriés d’un IRP d’entrée pour la routine IoCompletion avant de configurer un transfert partiel pour le pilote inférieur suivant dans cette IRP. L’enregistrement des paramètres est particulièrement important si la routine DispatchReadWrite modifie tous les paramètres dont la routine IoCompletion a besoin pour déterminer quand la requête d’origine a été satisfaite.
Si la routine IoCompletion peut réessayer la demande, la routine de répartition doit configurer une limite supérieure déterminée par le pilote pour le nombre de nouvelles tentatives que sa routine IoCompletion doit tenter avant de terminer l’IRP d’origine avec une erreur.
Si un IRP doit être réutilisé, la routine de dispatch doit appeler IoSetCompletionRoutine avec tous les paramètres InvokeOnXxx définis sur TRUE.
Pour une requête asynchrone, la routine de répartition de tout pilote intermédiaire doit appeler IoMarkIrpPending pour l’IRP d’origine. Il doit ensuite retourner STATUS_PENDING après avoir envoyé l’IRP aux pilotes inférieurs.