IRP_MN_QUERY_STOP_DEVICE 要求の処理 (Windows 2000 以降)
IRP_MN_QUERY_STOP_DEVICE 要求は、まずデバイス スタックの最上位ドライバーによって処理され、その後、次の下位ドライバーによって処理されます。 ドライバーは、DispatchPnP ルーチンの IRP の停止を処理します。
ドライバーは、IRP_MN_QUERY_STOP_DEVICE に応答して、以下の操作を行う必要があります。
悪影響を及ぼすことなく、デバイスを停止できるかどうか、およびそのハードウェア リソースを解放できるかどうかを判断します。
次のいずれかに該当する場合、ドライバーは、クエリ停止 IRP に失敗する必要があります。
デバイスがページング、休止状態、またはクラッシュ ダンプ ファイルのパスにあることが (IRP_MN_DEVICE_USAGE_NOTIFICATION を通じて) ドライバーに通知されました。
デバイスのハードウェア リソースを解放できません。
以下の条件に当てはまる場合、ドライバーはクエリ停止 IRP に失敗する可能性があります。
ドライバーは I/O 要求を削除すべきではありません。また、IRP をキューに入れるためのメカニズムがありません。
デバイスが停止状態の間、ドライバーはデバイスへのアクセスを必要とする IRP を保持する必要があります。 ドライバーが IRP をキューに入れない場合、デバイスの停止してはならないため、クエリ停止 IRP に失敗する必要があります。
この規則の例外は、I/O の削除が許可されたデバイスです。 このようなデバイスのドライバーは、IRP をキューに入れることなく、クエリ停止および要求停止に成功できます。
デバイスを停止できない場合、クエリ停止 IRP に失敗します。
Irp->IoStatus.Status を適切なエラー ステータスに設定し、IO_NO_INCREMENT を使用して IoCompleteRequest を呼び出した後、ドライバーの DispatchPnP ルーチンから戻ります。 IRP を次の下位ドライバーに渡さないでください。
デバイスを停止でき、ドライバーが IRP をキューに入れる場合、デバイス拡張機能で HOLD_NEW_REQUESTS フラグを設定して、後続の IRP がキューに入れられるようにします (「デバイスが一時停止したときの着信 IRP の保持」を参照)。
または、デバイスのドライバーは、ドライバーが後続の IRP_MN_STOP_DEVICE 要求を受け取るまで、デバイスの完全な一時停止を延期できます。 ただし、そのようなドライバーは、停止 IRP の到着直後に成功することを妨げている要求をすべてキューに入れる必要があります。 デバイスが再起動されるまで、このようなドライバーは、次のような要求をキューに入れる必要があります。
IRP_MN_DEVICE_USAGE_NOTIFICATION 要求 (たとえば、デバイスにページング ファイルを配置するため)。
アイソクロナス転送の要求。
ドライバーが IRP 停止に成功するのを防ぐ要求を作成します。
進行中の IRP をデバイスが失敗させることができない場合、他のドライバー ルーチンおよび下位ドライバーに渡された未処理の要求が完了したことを確認します。
ドライバーがこれを実現する 1 つの方法は、参照カウントとイベントを使って、すべての要求が完了したことを確認する方法です。
その AddDevice ルーチンでは、ドライバーは、デバイス拡張機能の I/O 参照カウントを定義し、カウントを 1 に初期化します。
さらに、AddDevice ルーチンでは、ドライバーは KeInitializeEvent を使用してイベントを作成し、KeClearEvent を使用して Not-Signaled 状態にイベントを初期化します。
IRP を処理するたびに、ドライバーは InterlockedIncrement を使用して参照カウントをインクリメントします。
要求が完了するたびに、ドライバーは InterlockedDecrement を使用して参照カウントをデクリメントします。
要求に IoCompletion ルーチンがある場合、またはドライバーが要求に IoCompletion ルーチンを使用しない場合は IoCallDriver の呼び出しの直後、ドライバーは IoCompletion ルーチンの参照カウントをデクリメントします。
ドライバーは、IRP_MN_QUERY_STOP_DEVICE を受け取ると、InterlockedDecrement を使用して参照カウントをデクリメントします。 未処理の要求がない場合、参照カウントが 0 に減少します。
参照カウントが 0 に達すると、ドライバーは、クエリ停止コードを続行できることを通知する KeSetEvent を使ってイベントを設定します。
上記の手順の代わりに、ドライバーは、進行中の IRP の 背後にある IRP_MN_QUERY_STOP_DEVICE IRP をシリアル化することもできます。
デバイスを停止保留中状態にするために必要なその他の手順を実行します。
ドライバーは、クエリ停止 IRP に成功した後、IRP_MN_STOP_DEVICE に成功する準備ができている必要があります。
IRP を完了します。
関数またはフィルター ドライバーの場合:
Irp->IoStatus.Status を STATUS_SUCCESS に設定します。
IoSkipCurrentIrpStackLocation を使用して次のスタックの場所をセットアップし、IoCallDriver で次の下位ドライバーに IRP を渡します。
DispatchPnP ルーチンからの戻りステータスとして IoCallDriver からステータスを伝達します。
IRP は完了させません。
バス ドライバーの場合:
Irp->IoStatus.Status を STATUS_SUCCESS に設定します。
ただし、バス上のデバイスがハードウェア リソースを使用している場合、バスと子デバイスのリソース要件を再評価します。 いずれかの要件が変更された場合は、STATUS_SUCCESS の代わりに STATUS_RESOURCE_REQUIREMENTS_CHANGED を返します。 このステータスは成功を示していますが、PnP マネージャーが停止 IRP を送信する前にリソースを再クエリすることを要求します。
IO_NO_INCREMENT を使用して IRP (IoCompleteRequest) を完了します。
DispatchPnP ルーチンから返します。
デバイス スタック内のドライバーが IRP_MN_QUERY_STOP_DEVICE に失敗した場合、PnP マネージャーは IRP_MN_CANCEL_STOP_DEVICE をデバイス スタックに送信します。 これにより、下位のドライバーが IRP に失敗したかどうかを検出するために、ドライバーが、クエリ停止 IRP の IoCompletion ルーチンを必要とすることがなくなります。