ドライバー内での IOCTL 要求の作成

クラス ドライバーなどの上位ドライバーは、以下のように I/O 制御要求用の IRP を割り当て、次のレベルの下位ドライバーに送信できます。

  1. メジャー関数コードである IRP_MJ_DEVICE_CONTROL または RP_MJ_INTERNAL_DEVICE_CONTROL を使用して、I/O 要求パケット (IRP) を割り当てるか再利用します。 IoBuildDeviceIoControlRequest ルーチンを使用して、IOCTL IRP を特に割り当てることができます。 IoAllocateIrpIoReuseIrpIoInitializeIrp など、汎用 IRP の作成および初期化ルーチンを使用することもできます。 IRP の割り当ての詳細については、「下位レベルのドライバーの IRP の作成」を参照してください。

  2. IOCTL_XXX コードと適切なパラメーターを使用して、IRP の下位のドライバーの I/O スタックの場所を設定します。

  3. IOCTL 要求を非同期的に完了する場合は、KeInitializeEvent ルーチンを呼び出して、通知イベントとしてイベント オブジェクトを初期化します。 ドライバーは、このイベントを使用して、I/O 操作が完了するまで待機します。

  4. 上のドライバーが必要に応じて IoCompletion ルーチンを提供できるように、IRP で IoSetCompletionRoutine を呼び出して、次の操作を行います。

    • 下位ドライバーが特定の要求をどのように処理したかを判断します。

    • 下位ドライバーが要求された操作を完了した後、IRP を再利用して別の要求を送信するか、ドライバーによって作成された IRP を破棄します。 ドライバーは、IoBuildDeviceIoControlRequest によって作成された IRP は再利用できません。 詳細については、「IRP の再利用」を参照してください。

  5. IoCallDriver を呼び出して、下位ドライバーに要求を渡します。

  6. IoCallDriver が STATUS_PENDING を返す場合、KeWaitForSingleObject ルーチンを呼び出して、現在のスレッドを待機状態にします。 ドライバーは、KeInitializeEvent の呼び出しで初期化されたイベント オブジェクトのアドレスにルーチンの Object パラメーターを設定します。

    注: ドライバーが KeWaitForSingleObject を呼び出し、その Timeout パラメーターが NULL または 0 以外の値を含む変数のアドレスに設定されている場合、ドライバーは非固定スレッド コンテキストで IRQL <= APC_LEVEL で実行する必要があります。 それ以外の場合、ドライバーは IRQL <= DISPATCH_LEVEL で実行する必要があります。

IOCTL 要求が完了すると、イベントは IoCompletion ルーチンによって通知されます。 イベントが通知されると、スレッドは実行を再開します。

重要: ドライバーがイベント オブジェクトをスタック上のローカル変数として割り当てる場合、ドライバーは WaitMode パラメーターを KernelMode に設定して、KeWaitForSingleObject を呼び出す必要があります。 このパラメーター値により、スタックがページ アウトされなくなります。

同期の問題やアクセス違反の可能性を回避するために、I/O 制御コードのパラメーターに埋め込みポインターが含まれることはほとんどありません。 特定の SCSI 要求を除き、ドライバーの I/O スタック位置の Irp->AssociatedIrp.SystemBufferIrp->MdlAddress、および Parameters.DeviceIoControl.Type3InputBuffer にあるバッファには、他のデータ バッファへのポインターは含まれず、またシステム定義の I/O 制御コードのポインターを含む構造体も含まれません。 I/O 制御コードを含む IRP でデータ バッファーを使用する方法の詳細については、「I/O 制御コードのバッファーの説明」を参照してください。

ただし、内部 I/O 制御コードを定義するクラス/ポート ドライバーのペアは、ドライバーによって割り当てられたメモリへの埋め込みポインターを上位ドライバーから下位ドライバーに渡すことができます。 このようなクラス/ポート ドライバーのペアは、次のことを保証する責任があります。

  • データにアクセスできるドライバーは一度に 1 つだけです。

  • プライベート データ バッファーには、任意のスレッド コンテキストでポート ドライバーによってアクセスできます。

ディスプレイ ドライバーは、GDI 関数 EngDeviceIoControl を呼び出して、プライベートに定義されたデバイス固有の I/O 制御要求やシステム定義のパブリック I/O 制御要求を、システム ビデオ ポート ドライバーを介して、対応するアダプター固有のビデオ ミニポート ドライバーに送信できます。

ドライバー パッケージのユーザーモード コンポーネントは、DeviceIoControl を呼び出して、ドライバー スタックに I/O コントロール要求を送信できます。 I/O マネージャーは、IRP_MJ_DEVICE_CONTROL 要求を作成し、最上位ドライバーに配信します。