IRP_MN_SURPRISE_REMOVAL 要求の処理

Windows 2000 以降の PnP マネージャーは、この IRP を送信し、デバイスが I/O 操作で使用できなくなり、予期せずコンピューターから取り出されたことをドライバーに通知します ("予期しない取り出し")。

PnP マネージャーは、以下の理由で IRP_MN_SURPRISE_REMOVAL 要求を送信します。

  • バスにホット プラグ通知がある場合、デバイスが消えたことをデバイスの親バス ドライバーに通知します。 バス ドライバーは、IoInvalidateDeviceRelations を呼び出します。 これに対して、PnP マネージャーは、その子 (BusRelationsIRP_MN_QUERY_DEVICE_RELATIONS) についてバス ドライバーにクエリを実行します。 PnP マネージャーは、デバイスが子の新しいリストに含まれていないと判断し、デバイスの予期しない取り出し操作を開始します。

  • バスは別の理由で列挙され、予期せず取り出されたデバイスは子のリストに含まれません。 PnP マネージャーは、予期しない取り出し操作を開始します。

  • デバイスのファンクション ドライバーは、デバイスが存在しなくなったと判断します (たとえば、要求が繰り返しタイムアウトしたため)。 バスは列挙可能な可能性がありますが、ホット プラグ通知はありません。 この場合、ファンクション ドライバーは IoInvalidateDeviceState を呼び出します。 これに対して、PnP マネージャーはデバイス スタックに IRP_MN_QUERY_PNP_DEVICE_STATE 要求を送信します。 ファンクション ドライバーは、デバイスが失敗したことを示す PNP_DEVICE_STATE ビットマスクで PNP_DEVICE_FAILED フラグを設定します。

  • ドライバー スタックは、IRP_MN_STOP_DEVICE 要求を正常に完了しますが、その後 IRP_MN_START_DEVICE 要求に失敗します。 このような場合、デバイスが引き続き接続されている可能性があります。

すべての PnP ドライバーは、この IRP を処理する必要があり、Irp->IoStatus.Status を STATUS_SUCCESS に設定する必要があります。 PnP デバイスのドライバーは、ドライバーの AddDevice ルーチンが呼び出された後、いつでも IRP_MN_SURPRISE_REMOVAL を処理するように準備する必要があります。 IRP を適切に処理すると、ドライバーと PnP マネージャーで次のことが可能になります。

  1. まだ接続されている場合はデバイスを無効にします。

    ドライバー スタックが IRP_MN_STOP_DEVICE 要求を正常に完了したが、何らかの理由で後続の IRP_MN_START_DEVICE 要求に失敗した場合、デバイスを無効にする必要があります。

  2. デバイスに割り当てられているハードウェア リソースを解放し、別のデバイスで使用できるようにします。

    デバイスが使用できなくなったらすぐに、そのハードウェア リソースを解放する必要があります。 PnP マネージャーは、リソースを別のデバイス (同じデバイスを含む) に再割り当てすることができます。そのデバイスは、ユーザーがコンピューターにもう一度ホット プラグする可能性があります。

  3. データ損失とシステム中断のリスクを最小限に抑えます。

    ホット プラグとそのドライバーをサポートするデバイスは、予期しない取り出しを処理するよう設計する必要があります。 ユーザーは、ホット プラグをサポートするデバイスをいつでも取り出させることを期待しています。

PnP マネージャーは、システム スレッドのコンテキストの IRQL = PASSIVE_LEVEL で IRP_MN_SURPRISE_REMOVAL を送信します。

PnP マネージャーは、ユーザー モード アプリケーションと他のカーネル モード コンポーネントに通知する前に、この IRP をドライバーに送信します。 IRP が完了すると、PnP マネージャーは、GUID_TARGET_DEVICE_REMOVE_COMPLETE と共に EventCategoryTargetDeviceChange 通知を、デバイスでこのような通知に登録されているカーネル モード コンポーネントに送信します。

IRP_MN_SURPRISE_REMOVAL IRP は、最初にデバイス スタックの最上位ドライバーによって処理され、その後、次の下位ドライバーごとに処理されます。

IRP_MN_SURPRISE_REMOVAL に応答して、ドライバーは、リストされた順序で以下の操作を行う必要があります。

  1. デバイスが削除されたかどうかを判断します。

    ドライバーは常に、デバイスがまだ接続されているかどうかを確認する必要があります。 接続されている場合、ドライバーはデバイスを停止して無効にする必要があります。

  2. デバイスのハードウェア リソース (割り込み、I/O ポート、メモリ レジスタ、DMA チャネル) を解放します。

  3. 親バス ドライバーで、バス スロットの電源をオフにします (ドライバーがこの操作を実行可能な場合)。 PoSetPowerState を呼び出して、電源マネージャーに通知します。 詳細については、「電源管理」を参照してください。

  4. デバイスで新しい I/O 操作を禁止します。

    ドライバーは、後続の IRP_MJ_CLEANUPIRP_MJ_CLOSEIRP_MJ_POWER、および IRP_MJ_PNP 要求を処理する必要がありますが、ドライバーは新しい I/O 操作を禁止する必要があります。 ドライバーは、デバイスが存在しているだけでなく、クローズ、クリーンアップ、および PnP IRP であるとしたらドライバーが処理していたであろう後続の IRP に失敗する必要があります。

    ドライバーは、デバイスが予期せず取り出されたことを示すためにデバイス拡張機能に少し設定を加えることができます。 ドライバーのディスパッチ ルーチンは、このビットをチェックする必要があります。

  5. デバイスで未処理の I/O 要求に失敗します。

  6. ドライバーがデバイスで処理しない IRP を引き続き渡します。

  7. IoSetDeviceInterfaceState を使用してデバイス インターフェイスを無効にします。

  8. デバイス固有の割り当て、メモリ、イベント、または他のシステム リソースをクリーンアップします。

    ドライバーは、後続の IRP_MN_REMOVE_DEVICE 要求を受け取るまで、このようなクリーンアップを延期できますが、閉じることができない開いているハンドルがレガシ コンポーネントにある場合、削除 IRP は送信されません。

  9. デバイス オブジェクトをデバイス スタックにアタッチしたままにします。

    後続の IRP_MN_REMOVE_DEVICE 要求まで、デバイス オブジェクトをデタッチおよび削除しないでください。

  10. IRP を完了します。

    関数またはフィルター ドライバーの場合:

    • Irp->IoStatus.Status を STATUS_SUCCESS に設定します。

    • IoSkipCurrentIrpStackLocation を使用して次のスタックの場所をセットアップし、IoCallDriver で次の下位ドライバーに IRP を渡します。

    • DispatchPnP ルーチンからの戻りステータスとして IoCallDriver からステータスを伝達します。

    • IRP は完了させません。

    バス ドライバー内 (子 PDO のこの IRP を処理する):

    • Irp->IoStatus.Status を STATUS_SUCCESS に設定します。

    • IO_NO_INCREMENT を使用して IRP (IoCompleteRequest) を完了します。

    • DispatchPnP ルーチンから返します。

この IRP が成功し、デバイスに対して開いているハンドルがすべて閉じられると、PnP マネージャーは IRP_MN_REMOVE_DEVICE要求をデバイス スタックに送信します。 IRP の削除に応答して、ドライバーは、スタックからデバイス オブジェクトをデタッチし、それらを削除します。 デバイスに対して開いているハンドルがレガシ コンポーネントにあり、I/O エラーが発生してもハンドルが開いたままになっている場合、PnP マネージャーは削除 IRP を送信しません。

すべてのドライバーがこの IRP を処理する必要があり、デバイスがコンピューターから物理的に削除されている点に注目する必要があります。 ただし、一部のドライバーは、IRP を処理しない場合、悪影響を及ぼしません。 たとえば、システム ハードウェア リソースを消費せず、プロトコル ベースのバス (USB や 1394 など) 上に存在するデバイスは、ハードウェア リソースをまったく消費しないため、専有することはありません。 ファンクション ドライバーとフィルター ドライバーは親バス ドライバーを通じてのみデバイスにアクセスするため、取り外された後にドライバーがデバイスにアクセスしようとするリスクはありません。 バスでは削除通知がサポートされるため、親バス ドライバーは、デバイスが消えたときに通知を受け取り、バス ドライバーは、デバイスにアクセスするすべての後続の試行に失敗します。

Windows 98/Me では、PnP マネージャーはこの IRP を送信しません。 ユーザーが適切なユーザー インターフェイスを使用せずにデバイスを取り出した場合、PnP マネージャーはデバイスのドライバーに IRP_MN_REMOVE_DEVICE 要求のみ送信します。 すべての WDM ドライバーは、IRP_MN_SURPRISE_REMOVALIRP_MN_REMOVE_DEVICE の両方を処理する必要があります。 IRP_MN_REMOVE_DEVICE のコードは、ドライバーが事前の予期しない取り出し IRP を受け取ったかどうかをチェックし、両方のケースを処理する必要があります。

GUID_REENUMERATE_SELF_INTERFACE_STANDARD の使用

GUID_REENUMERATE_SELF_INTERFACE_STANDARD インターフェイスを使用すると、ドライバーはデバイスの再有効化を要求できます。

このインターフェイスを使用するには、InterfaceType = GUID_REENUMERATE_SELF_INTERFACE_STANDARD を指定して、バス ドライバーに IRP_MN_QUERY_INTERFACE IRP を送信します。 インターフェイスの個々のルーチンへのポインターを記述した REENUMERATE_SELF_INTERFACE_STANDARD 構造へのポインターがバス ドライバーから提供されます。 ReenumerateSelf ルーチンは、バス ドライバーが子デバイスを再び有効にすることを要求します。

PNP_DEVICE_STATE について

PNP_DEVICE_STATE 型は、デバイスの PnP 状態を記述するビットマスクです。 ドライバーは、IRP_MN_QUERY_PNP_DEVICE_STATE 要求に応答してこの型の値を返します。

typedef ULONG PNP_DEVICE_STATE, *PPNP_DEVICE_STATE;

PNP_DEVICE_STATE 値のフラグ ビットは、以下のように定義されます。

フラグ ビット 説明
PNP_DEVICE_DISABLED

デバイスは物理的に存在しますが、ハードウェアでは無効になっています。

PNP_DEVICE_DONT_DISPLAY_IN_UI

ユーザー インターフェイスにデバイスを表示しないでください。 ノート PC がドッキング解除されたときに使用できないノート PC 上のゲーム ポートなど、物理的に存在するが現在の構成では使用できないデバイス用に設定されます。 (DEVICE_CAPABILITIES 構造の NoDisplayInUI フラグも参照)。

PNP_DEVICE_FAILED

デバイスは存在していますが、正しく機能していません。

このフラグと PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED の両方が設定されている場合、PnP マネージャーが新しいハードウェア リソースを割り当てる前に、デバイスを停止する必要があります (デバイスの非停止再調整はサポートされていません)。

PNP_DEVICE_NOT_DISABLEABLE

コンピューターの起動時にデバイスが必要です。 そのようなデバイスは無効にしないでください。

ドライバーは、適切なシステム操作に必要なデバイスにこのビットを設定します。 たとえば、デバイスがページング パス (DeviceUsageTypePaging の場合は IRP_MN_DEVICE_USAGE_NOTIFICATION) にあるという通知をドライバーが受け取った場合、ドライバーは IoInvalidateDeviceState を呼び出し、結果の IRP_MN_QUERY_PNP_DEVICE_STATE 要求でこのフラグを設定します。

このビットがデバイスに設定されている場合、PnP マネージャーは、デバイスの親デバイス、その親の親デバイスなどにこの設定を伝達します。

このビットがルート列挙デバイスに設定されている場合、デバイスを無効にしたりアンインストールしたりすることはできません。

PNP_DEVICE_REMOVED

デバイスが物理的に取り出されました。

PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED

デバイスのリソース要件が変更されました。

通常、バス ドライバーは、新しい子デバイスを列挙するためにリソース要件を拡張する必要があると判断したとき、このフラグを設定します。

PNP_DEVICE_DISCONNECTED

デバイス ドライバーが読み込まれますが、このドライバーは、デバイスがコンピューターに接続されていないことを検出しました。 通常、このフラグは、ワイヤレス デバイスと通信するファンクション ドライバーに使用されます。 たとえば、このフラグは、デバイスが範囲外に移動したときに設定され、デバイスが範囲に戻って再接続するとクリアされます。

バス ドライバーは通常、このフラグを設定しません。 デバイスが接続されなくなった場合、バス ドライバーは、代わりに子デバイスの列挙を停止する必要があります。 このフラグは、ファンクション ドライバーが接続を管理する場合のみ使用されます。

このフラグの唯一の目的は、デバイスが接続されているかどうかをクライアントに通知することです。 フラグを設定しても、ドライバーが読み込まれるかどうかには影響を与えません。

PnP マネージャーは、IRP_MN_QUERY_PNP_DEVICE_STATE 要求をデバイス スタックに送信することにより、デバイスを起動した直後にデバイスの PNP_DEVICE_STATE に対してクエリを実行します。 この IRP に応答して、デバイスのドライバーは PNP_DEVICE_STATE で適切なフラグを設定します。

最初のクエリの後にいずれかの状態特性が変更された場合、ドライバーは IoInvalidateDeviceState を呼び出すことにより PnP マネージャーに通知します。 IoInvalidateDeviceState の呼び出しに応答して、PnP マネージャーはデバイスの PNP_DEVICE_STATE に対してもう一度クエリを実行します。

デバイスが PNP_DEVICE_NOT_DISABLEABLE とマークされている場合、デバッガーは devnode の DNUF_NOT_DISABLEABLE ユーザー フラグを表示します。 デバッガーには、デバイスを無効にできない理由の数をカウントする DisableableDepends 値も表示されます。 この値は X + Y の合計です。ここで、X はデバイスを無効にできない場合は 1、Y は無効にできないデバイスの子デバイスの数です。