IoCompletion ルーチンの登録
IoCompletion ルーチンを登録するために、ディスパッチ ルーチンは IoSetCompletionRoutine を呼び出し、IoCallDriver使用して、その後下位ドライバーに渡す IoCompletion ルーチンのアドレスと IRP を提供します。
IoSetCompletionRoutine を呼び出すとき、ディスパッチ ルーチンは、I/O マネージャーが指定された IoCompletion ルーチンを呼び出す必要がある状況を指定します。 IoCompletionルーチンを、下位レベルのドライバーが IRP を正常に完了した場合 (InvokeOnSuccess)、IRP がエラー ステータス値で完了した場合 (InvokeOnError)、または IRPを取り消した場合 InvokeOnCancel) の任意の組み合わせで呼び出すことができます。
IoCompletion ルーチンの目的は、下位レベルのドライバーが IRP で行ったことを監視し、必要に応じて追加の完了処理を実行することです。 具体的には、ドライバーの IoCompletion ルーチンの最も一般的な用途は次のとおりです。
ドライバーが IoAllocateIrp または IoBuildAsynchronousFsdRequest で割り当てた IRP を破棄するには
これらのサポート ルーチンのいずれかを使用して IRP を割り当てる上位レベルのドライバーは、その IRP の IoCompletion ルーチンを提供する必要があります。 IoCompletion ルーチンは、ドライバーによって割り当てられた IRP を破棄するために IoFreeIrp を呼び出す必要があります。
元の要求が IoCompletion ルーチンによって満たされて完了するまでに下位ドライバーが一部の操作 (部分転送など) を完了するように要求する受信 IRP を再利用するには
下位ドライバーがエラーで完了した要求を再試行するには
ファイル システムなどの最上位レベルのドライバーには、要求の再試行を試みる IoCompletion ルーチンがある可能性が中間ドライバーよりも高くなります。ただし、クラス ドライバーは、密接に結合されたポート ドライバーの上に重なっている可能性があります。 ただし、中間ドライバーは、IoCompletion ルーチンを使用して要求を再試行します。
最上位レベルまたは中間ドライバーの DispatchReadWrite ルーチンは、IoCompletion ルーチンを必要とする IRP を処理する可能性が最も高くなりますが、下位ドライバーに IRP を渡す任意のドライバーのディスパッチ ルーチンは、IoCompletion ルーチンを登録できます。
ドライバー割り当て IRP と再利用される IRP の場合、ディスパッチ ルーチンは、次のブール値パラメーターで IoSetCompletionRoutine を呼び出す必要があります。
InvokeOnSuccess を TRUE に設定
InvokeOnError を TRUE に設定
チェーン内の下位のドライバーが取り消し可能な IRP を処理する可能性がある場合、InvokeOnCancel を TRUE に設定する
通常、InvokeOnCancel は、IRP がSTATUS_CANCELLEDで返される可能性があるかどうかに関係なく TRUE に設定され、IoCompletion ルーチンで各ドライバーに割り当てられた IRP を解放させるか、IRP の各再利用の完了ステータスをチェックします。
IoAllocateIrp または IoBuildAsynchronousFsdRequest を使用して下位ドライバーに IRP を割り当てるディスパッチ ルーチンは、ドライバーに割り当てられた IRP ごとに IoCompletion ルーチンを設定する必要があります。
ディスパッチ ルーチンは、使用する IoCompletion ルーチンの元の IRP とその割り当てられた IRP の両方に関する状態を設定する必要があります。 少なくとも、IoCompletion ルーチンには、元の IRP へのアクセス権と、割り当てられた追加 IRP の数が必要です。
ディスパッチ・ルーチンは IoSetCompletionRoutine を呼び出す際、割り当てた IRP に対してすべての InvokeOnXxx パラメータを TRUE に設定する必要があります。
一連の操作に IRP を再利用するディスパッチ ルーチン、または I/O 操作を再試行するディスパッチ ルーチンは、再利用や再試行される IRP ごとに IoSetCompletionRoutine を呼び出す必要があります。
ディスパッチ ルーチンは、IoCompletion ルーチンで後で使用するために、元の IRP の状態情報を保存する必要があります。
たとえば、DispatchReadWrite ルーチンは、その IRP の次の下位ドライバーの部分転送を設定する前に、IoCompletion ルーチンの入力 IRP の関連転送パラメーターを保存する必要があります。 DispatchReadWrite ルーチンが、元の要求がいつ満たされたかを判断するために IoCompletion ルーチンが必要とするパラメーターを変更する場合、パラメーターの保存は特に重要です。
IoCompletion ルーチンが要求を再試行できる場合、ディスパッチ ルーチンは、エラーで元の IRP を完了する前に、その IoCompletion ルーチンが試行する再試行回数のドライバーで決定された上限を設定する必要があります。
IRP を再利用する場合、ディスパッチ・ルーチンは、 IoSetCompletionRoutine を呼び出す際に、 InvokeOnXxx パラメーターを TRUE に設定する必要があります。
非同期要求の場合、中間ドライバーのディスパッチ ルーチンは、元の IRP の IoMarkIrpPending を呼び出す必要があります。 その後、下位ドライバーに IRP を送信した後、STATUS_PENDING を返す必要があります。