入力時に、 IoCompletion ルーチンは コンテキスト ポインターを受け取ります。 ディスパッチ ルーチンが IoSetCompletionRoutine を呼び出すと、 コンテキスト ポインターを指定できます。 このポインターは、 IoCompletion ルーチンが IRP を処理するために必要なドライバーによって決定されたコンテキスト情報を参照できます。 IoCompletion ルーチンは IRQL = DISPATCH_LEVEL で呼び出すことができるため、コンテキスト領域をページングできません。
IoCompletion ルーチンの実装ガイドラインを次に示します。
IoCompletion ルーチンは、IRP の I/O 状態ブロックを確認して、I/O 操作の結果を判断できます。
ディスパッチ ルーチンが IoAllocateIrp または IoBuildAsynchronousFsdRequest を使用して入力 IRP を割り当てた場合、 IoCompletion ルーチンは、元の IRP を完了する前に、その IRP を解放するために IoFreeIrp を呼び出す必要があります。
IoCompletion ルーチンは、できれば対応する IRP を解放する前に、ディスパッチ ルーチンがドライバーに割り当てた IRP のために割り当てた各 IRP ごとのリソースを解放する必要があります。
たとえば、ディスパッチ ルーチンが IoAllocateMdl で MDL を割り当て、割り当てる部分転送 IRP に 対して IoBuildPartialMdl を呼び出す場合、 IoCompletion ルーチンは IoFreeMdl で MDL を解放する必要があります。 元の IRP に関する状態を維持するためにリソースを割り当てる場合は、それらのリソースを解放する必要があります。可能であれば、元の IRP で IoCompleteRequest を呼び出す前に、コントロールを返す前に間違いなく解放する必要があります。
一般に、IRP を解放または完了する前に、 IoCompletion ルーチンはディスパッチ ルーチンによって割り当てられた IRP ごとのリソースを解放する必要があります。 それ以外の場合、ドライバーは、 IoCompletion ルーチンが元の要求を完了する制御を返す前に、解放されるリソースに関する状態を維持する必要があります。
IoCompletion ルーチンは、STATUS_SUCCESSを使用して元の IRP を完了できない場合、元の IRP の I/O 状態ブロックを、元の要求に失敗する IoCompletion ルーチンの原因となったドライバー割り当て IRP で返された値に設定する必要があります。
IoCompletion ルーチンは、STATUS_PENDINGを使用して元の要求を完了する場合は、IoCompleteRequest を呼び出す前に、元の IRP で IoMarkIrpPending を呼び出す必要があります。
IoCompletion ルーチンがエラー STATUS_XXX で元の IRP を失敗させる必要がある場合は、エラーをログに記録できます。 ただし、発生したデバイス I/O エラーをログに記録するのは、基になるデバイス ドライバーの責任です。そのため、 IoCompletion ルーチンでは通常、エラーは記録されません。
IoCompletion ルーチンが処理を行い、ドライバーによって割り当てられた IRP を解放する際には、STATUS_MORE_PROCESSING_REQUIRED の制御を返す必要があります。
IoCompletion ルーチンから STATUS_MORE_PROCESSING_REQUIRED を返すと、ドライバーが割り当てて解放した IRP に対する I/O マネージャの完了処理が抑制されます。 IoCompleteRequest への 2 回目の呼び出しにより、I/O マネージャーは IRP の完了ルーチンの呼び出しを再開します。これは、STATUS_MORE_PROCESSING_REQUIREDを返したルーチンのすぐ上の完了ルーチンから始まります。
IoCompletion ルーチンが受信 IRP を再利用して 1 つ以上の要求を下位ドライバーに送信する場合、またはルーチンが失敗した操作を再試行する場合は、IRP の再利用または再試行ごとに IoCompletion ルーチンが保持するコンテキストを更新する必要があります。 次に、次の下位ドライバーの I/O スタックの場所をもう一度設定し、独自のエントリ ポイント で IoSetCompletionRoutine を呼び出し、IRP の IoCallDriver を呼び出すことができます。
IoCompletion ルーチンは、IRP の再利用または再試行のたびに IoMarkIrpPending を呼び出さないでください。
ディスパッチ ルーチンは、既に保留中として元の IRP をマークします。 チェーン内のすべてのドライバーが IoCompleteRequest で元の IRP を完了するまで、保留中のままになります。
要求を再試行する前に、IoCompletion ルーチンは、返されたエラー情報を保存する可能性がある後に、STATUS_SUCCESS と 情報 が 0 であるように I/O 状態ブロックをリセットする必要があります。
通常、 IoCompletion ルーチンは、再試行ごとにディスパッチ ルーチンによって設定された再試行回数をデクリメントします。 通常、 IoCompletion ルーチンは IoCompleteRequest を呼び出して、制限された回数の再試行が失敗した場合に IRP を失敗させる必要があります。
IoCompletion ルーチンは、再利用または再試行中の IRP で IoSetCompletionRoutine と IoCallDriver を呼び出した後、STATUS_MORE_PROCESSING_REQUIREDを返す必要があります。
IoCompletion ルーチン内からSTATUS_MORE_PROCESSING_REQUIREDを返すと、I/O マネージャーが再利用または再試行したIRPの完了処理を遅らせます。
IoCompletionルーチンが元のIRPをSTATUS_SUCCESSで完了できない場合、IoCompletionルーチンがIRPの失敗を引き起こす再利用または再試行操作のために、下位ドライバーによって返されたI/O状態ブロックをそのまま残しておかなければなりません。
IoCompletion ルーチンは、STATUS_PENDINGを使用して元の要求を完了する場合は、IoCompleteRequest を呼び出す前に、元の IRP で IoMarkIrpPending を呼び出す必要があります。
IoCompletion ルーチンがエラー STATUS_XXX で元の IRP を失敗させる必要がある場合は、エラーをログに記録できます。 ただし、発生したデバイス I/O エラーをログに記録するのは、基になるデバイス ドライバーの責任です。そのため、 IoCompletion ルーチンでは通常、エラーは記録されません。
IRP で IoCompletion ルーチンを設定し、下位のドライバーに IRP を渡すドライバーは、> ルーチンで IRP PendingReturned フラグを確認する必要があります。 フラグが設定されている場合、 IoCompletion ルーチンは、IRP で IoMarkIrpPending を呼び出す必要があります。 ただし、IRP を渡した後にイベントを待機するドライバーは、IRP を保留中としてマークするべきではありません。 代わりに、 その IoCompletion ルーチンはイベントを通知し、STATUS_MORE_PROCESSING_REQUIREDを返す必要があります。
IoCompletion ルーチンは、元の IRP を処理するために割り当てられたディスパッチ ルーチンのすべてのリソースを解放する必要があります。できれば、IoCompletion ルーチンが元の IRP で IoCompleteRequest を呼び出す前、および IoCompletion ルーチンが元の IRP を完了する制御を返す前に、間違いなく。
上位レベルのドライバーが元の IRP で IoCompletion ルーチンを設定した場合、そのドライバーの IoCompletion ルーチンは、下位レベルのすべてのドライバーの IoCompletion ルーチンが呼び出されるまで呼び出されません。
IoCompleteRequest への呼び出しで優先度ブーストを提供する
最下位レベルのデバイス ドライバーがディスパッチ ルーチンで IRP を完了できる場合は、IO_NO_INCREMENT の PriorityBoost を使用して IoCompleteRequest を呼び出します。 ドライバーは、元の要求元が I/O 操作の完了を待たなかったと想定できるため、実行時の優先度の引き上げは必要ありません。
それ以外の場合、最下位レベルのドライバーは、要求者がデバイス I/O 要求を待機した時間を補うために要求者の実行時優先度を高めるシステム定義およびデバイスの種類固有の値を提供します。 ブースト値については、Wdm.h または Ntddk.h を参照してください。
上位レベルのドライバーは、IoCompleteRequest を呼び出すときに、それぞれの基になるデバイス ドライバーと同じ PriorityBoost を適用します。
IoCompleteRequest の呼び出しの効果
ドライバーが IoCompleteRequest を呼び出すと、I/O マネージャーは、IRP に対して呼び出される IoCompletion ルーチンを設定する次の上位レベルのドライバー (ある場合) を呼び出す前に、そのドライバーの I/O スタックの場所をゼロで埋めます。
上位レベルのドライバーの IoCompletion ルーチンでは、IRP の I/O 状態ブロックのみを確認して、下位のすべてのドライバーが要求をどのように処理したかを判断できます。
IoCompleteRequest の呼び出し元は、完了した IRP へのアクセスを試みてはなりません。 このような試行は、システム クラッシュの原因となるプログラミング エラーです。