Freigeben über


Registrieren einer IoCompletion-Routine

Um eine IoCompletion-Routine zu registrieren, ruft eine Dispatchroutine IoSetCompletionRoutine auf und gibt die Adresse der IoCompletion-Routine und den IRP an, die sie anschließend mithilfe von IoCallDriver an niedrigere Treiber weitergibt.

Wenn IoSetCompletionRoutine aufgerufen wird, gibt die Dispatchroutine die Umstände an, unter denen der E/A-Manager die angegebene IoCompletion-Routine aufrufen soll. Sie können festlegen, dass die IoCompletion-Routine aufgerufen wird, wenn ein Treiber auf niedrigerer Ebene das IRP erfolgreich abschließt (InvokeOnSuccess), die IRP mit einem Fehler status Wert (InvokeOnError) abschließt oder das IRP (InvokeOnCancel) in einer beliebigen Kombination abbricht.

Der Zweck einer IoCompletion-Routine besteht darin, zu überwachen, was Treiber auf niedrigerer Ebene mit dem IRP gemacht haben, und bei Bedarf zusätzliche Vervollständigungsverarbeitung durchzuführen. Insbesondere sind die häufigsten Verwendungen für die IoCompletion-Routinen eines Treibers die folgenden:

  • So verwerfen Sie ein IRP, das der Treiber ioAllocateIrp oder IoBuildAsynchronousFsdRequest zugeordnet hat

    Jeder Treiber auf höherer Ebene, der ein IRP mithilfe einer dieser Supportroutinen zuordnet, muss eine IoCompletion-Routine für dieses IRP bereitstellen. Die IoCompletion-Routine muss IoFreeIrp aufrufen, um vom Treiber zugewiesene IRPs zu verwerfen.

  • So verwenden Sie einen eingehenden IRP wieder, um anzufordern, dass niedrigere Treiber eine bestimmte Anzahl von Vorgängen ausführen, z. B. Teilübertragungen, bis die ursprüngliche Anforderung erfüllt und von der IoCompletion-Routine abgeschlossen werden kann

  • So wiederholen Sie eine Anforderung, dass ein niedrigerer Treiber mit einem Fehler abgeschlossen wurde

    Treiber der höchsten Ebene, z. B. Dateisysteme, verfügen mit höherer Wahrscheinlichkeit über IoCompletion-Routinen , die versuchen, Anforderungen zu wiederholen, als Zwischentreiber, mit Ausnahme von Klassentreibern, die über einem eng gekoppelten Porttreiber liegen. Jeder Zwischentreiber verwendet jedoch IoCompletion-Routinen , um Anforderungen zu wiederholen.

Während die DispatchReadWrite-Routine eines Treibers auf oberster Ebene oder zwischengeschalteter Treiber höchstwahrscheinlich IRPs verarbeitet, die eine IoCompletion-Routine erfordern, kann jede Dispatchroutine in jedem Treiber, der IRPs an niedrigere Treiber übergibt, eine IoCompletion-Routine registrieren.

Für vom Treiber zugewiesene IRPs und wiederverwendete IRPs muss die Dispatchroutine IoSetCompletionRoutine mit den folgenden booleschen Parametern aufrufen:

  • InvokeOnSuccess auf TRUE festgelegt

  • InvokeOnError auf TRUE festgelegt

  • InvokeOnCancel auf TRUE festgelegt, wenn ein niedrigerer Treiber in der Kette abbrechbare IRPs verarbeiten kann

    In der Regel ist InvokeOnCancel auf TRUE festgelegt, unabhängig davon, ob ein IRP mit STATUS_CANCELLED zurückgegeben wird, um sicherzustellen, dass die IoCompletion-Routine jedes vom Treiber zugewiesenen IRP freigibt oder die Fertigstellung status jeder Wiederverwendung eines IRP überprüft.

Eine Dispatchroutine, die IRPs für niedrigere Treiber mithilfe von IoAllocateIrp oder IoBuildAsynchronousFsdRequestzuordnet, muss eine IoCompletion-Routine für jedes vom Treiber zugewiesene IRP festlegen.

  • Die Dispatchroutine muss den Zustand der ursprünglichen IRP und der zugeordneten IRP(s) für die IoCompletion-Routine einrichten. Die IoCompletion-Routine benötigt mindestens Zugriff auf das ursprüngliche IRP und die Anzahl der zusätzlichen Zuweisungen von IRPs.

  • Die Dispatchroutine sollte IoSetCompletionRoutine aufrufen, wobei alle InvokeOnXxx-Parameter für die zuzuordnenden IRP(s) auf TRUE festgelegt sind.

Eine Dispatchroutine, die IRPs für eine Abfolge von Vorgängen wiederverwendet oder die E/A-Vorgänge wiederholt, muss IoSetCompletionRoutine für jedes IRP aufrufen, das wiederverwendet oder wiederholt wird.

  • Die Dispatchroutine muss die Statusinformationen des ursprünglichen IRP zur späteren Verwendung durch die IoCompletion-Routine speichern.

    Beispielsweise muss eine DispatchReadWrite-Routine die relevanten Übertragungsparameter eines Eingabe-IRP für die IoCompletion-Routine speichern, bevor eine Teilübertragung für den nächstniedrigen Treiber in diesem IRP eingerichtet wird. Das Speichern der Parameter ist besonders wichtig, wenn die DispatchReadWrite-Routine Parameter ändert, die die IoCompletion-Routine bestimmen muss, wenn die ursprüngliche Anforderung erfüllt wurde.

  • Wenn die IoCompletion-Routine die Anforderung wiederholen kann, muss die Dispatchroutine eine vom Treiber festgelegte Obergrenze für die Anzahl von Wiederholungen einrichten, die die IoCompletion-Routine versuchen sollte, bevor sie den ursprünglichen IRP mit einem Fehler abschließt.

  • Wenn ein IRP wiederverwendet werden soll, sollte die Dispatchroutine IoSetCompletionRoutine aufrufen, wobei alle InvokeOnXxx-Parameter auf TRUE festgelegt sind.

  • Für eine asynchrone Anforderung muss die Dispatchroutine eines zwischengeschalteten Treibers IoMarkIrpPending für den ursprünglichen IRP aufrufen. Anschließend muss STATUS_PENDING zurückgegeben werden, nachdem der IRP an niedrigere Treiber gesendet wurde.