次の方法で共有


I/O 制御コードの定義

この記事では、一意の I/O 制御コード (IOCTL) を作成する方法について説明します。 IOCTL には、次のことができます。

  • パブリック IOCTL。通常はシステム定義であり、Microsoft によって文書化されています。
  • プライベート IOCTL。通常は、ベンダーのソフトウェア コンポーネントが相互に通信するために排他的に使用することを目的とします。 通常、これらはベンダーのヘッダー ファイルで定義されており、Microsoft によって文書化されていません。

IOCTL レイアウト

IOCTL は、複数のフィールドで構成される 32 ビット値です。 次の図は、IOCTL のビットごとのレイアウトを示しています。

32 ビット i/o 制御コードのビットごとのレイアウトを示す図。

次の表に示すように、IOCTL の各フィールドには特定の目的があります。

フィールド IOCTL のビット数 説明
コモン 31 ベンダーは、 DeviceType にベンダー割り当て値を使用する場合に、このビットを設定する必要があります。
DeviceType 16-30 デバイスの種類を識別します。 この値は、ドライバーのDEVICE_OBJECT構造体の DeviceType メンバーに設定されている値と一致する必要があります。 ベンダーは、32768 ~ 65535 (0x8000 ~ 0xffff) の値を使用し、 共通 ビットを設定する必要があります。 値 0 ~ 32767 (0x0000 ~ 0x7fff) は、Microsoft 向けに予約されています。 詳細については、「デバイスの種類の指定」を参照してください。
アクセス 14-15 デバイスを表すファイル オブジェクトを開くときに呼び出し元が要求する必要があるアクセスの種類を示します ( IRP_MJ_CREATEを参照)。 I/O マネージャーは、IRP を作成し、呼び出し元が指定したアクセス権を要求した場合にのみ、特定の IOCTL でドライバーを呼び出します。 このフィールドは、システム定義の定数 (FILE_ANY_ACCESS、FILE_READ_DATA、およびFILE_WRITE_DATA) を使用して指定します。
カスタム 13 設定すると、IOCTL がベンダー定義の IOCTL であることを示します。
関数 2-12 実行する関数を識別するドライバーの一意のコード。 ベンダーが作成した IOCTL の場合は、値 2048 ~ 4095 (0x800 ~ 0xfff) を使用し、 カスタム ビットを設定します。 2048 未満 (0x000 ~ 0x7ff) の値は、Microsoft 向けに予約されています。
方式 0-1 DeviceIoControl (または IoBuildDeviceIoControlRequest) の呼び出し元と IRP を処理するドライバーの間で、システムがデータを渡す方法を示します。 詳細については、 メソッド ビットの設定に関するガイダンスを参照してください。

I/O 制御コードを定義するためのマクロ

システム提供の CTL_CODE マクロを使用して、新しい I/O 制御コードを定義します。 このマクロは devioctl.h で次のように定義されています。

#define CTL_CODE( DeviceType, Function, Method, Access ) (                 \
    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
)

DeviceTypeFunctionMethodAccess の説明については、前のセクションを参照してください。

新しい I/O 制御コードを定義するときは、次の規則に注意してください。

  • ユーザーモードのソフトウェア コンポーネントで新しい IOCTL を使用できる場合は、IRP_MJ_DEVICE_CONTROL要求に関連して使用する必要があります。 ユーザー モード コンポーネントは 、DeviceIoControl を呼び出して IRP_MJ_DEVICE_CONTROL 要求を送信します。

  • 新しい IOCTL がカーネル モード ドライバー コンポーネントでのみ使用できる場合は、 IRP_MJ_INTERNAL_DEVICE_CONTROL 要求と共に使用する必要があります。 カーネル モード コンポーネントは、IoBuildDeviceIoControlRequest を呼び出すことによってIRP_MJ_INTERNAL_DEVICE_CONTROL要求を作成できます。 詳細については、「ドライバーでの IOCTL 要求の作成」を参照してください。

新しい IOCTL コードの定義は、 IRP_MJ_DEVICE_CONTROL 要求と IRP_MJ_INTERNAL_DEVICE_CONTROL 要求のどちらで使用するかを問わず、次の形式を使用します。

#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

IOCTL のわかりやすい定数名を、IOCTL_Device_Function という形式で選択します。 デバイスデバイスの種類 を示し、 Function は操作を示します。 たとえば、システム提供のIOCTL_VIDEO_ENABLE_CURSOR定数では 、デバイス に "VIDEO" を使用し、 関数に "ENABLE_CURSOR" を使用します。

Access ビットの設定に関するガイダンス

新しい IOCTL を定義するときは、デバイスを表すファイル オブジェクトを開くときに呼び出し元が要求する必要があるアクセスの種類を示す Access ビット フィールドの値を選択する必要があります。 I/O マネージャーは、IRP を作成し、呼び出し元が指定したアクセス権を要求した場合にのみ、特定の IOCTL でドライバーを呼び出します。

Access は、次のシステム定義定数を使用して指定します。

  • FILE_ANY_ACCESS

    I/O マネージャーは、ターゲット デバイス オブジェクトを表すファイル オブジェクトへのハンドルを持つすべての呼び出し元の IRP を送信します。 新しい IOCTL コードのFILE_ANY_ACCESSを指定する前に、デバイスへの無制限のアクセスを許可しても、悪意のあるユーザーがシステムを侵害する可能性のあるパスが作成されない可能性があることを確実にする必要があります。

  • FILE_READ_DATA

    I/O マネージャーは、読み取りアクセス権を持つ呼び出し元に対してのみ IRP を送信し、基になるデバイス ドライバーがデバイスからシステム メモリにデータを転送できるようにします。

  • ファイル書き込みデータ

    I/O マネージャーは、書き込みアクセス権を持つ呼び出し元に対してのみ IRP を送信し、基になるデバイス ドライバーがシステム メモリからデバイスにデータを転送できるようにします。

呼び出し元が読み取りアクセス権と書き込みアクセス権の両方を持っている必要がある場合は、FILE_READ_DATAとFILE_WRITE_DATAを一緒に ORed にすることができます。

システム定義の I/O 制御コードの中には、 FILE_ANY_ACCESSの Access 値があります。これにより、呼び出し元は、デバイスに付与されたアクセスに関係なく、特定の IOCTL を送信できます。 たとえば、 排他的デバイスのドライバーに送信される I/O コントロール コードがあります。

その他のシステム定義の I/O 制御コードでは、呼び出し元に読み取りアクセス権、書き込みアクセス権、またはその両方が必要です。 たとえば、パブリック IOCTL_DISK_SET_PARTITION_INFO IOCTL の次の定義は、呼び出し元が読み取りアクセス権と書き込みアクセス権の両方を持っている場合にのみ、この I/O 要求をドライバーに送信できることを示しています。

#define IOCTL_DISK_SET_PARTITION_INFO\
        CTL_CODE(IOCTL_DISK_BASE, 0x008, METHOD_BUFFERED,\
        FILE_READ_DATA | FILE_WRITE_DATA)

ドライバーは IoValidateDeviceIoControlAccess を使用して、IOCTL のアクセス ビットによって提供されるよりも厳密な アクセス チェックを実行できます。

メソッド ビットの設定に関するガイダンス

新しい IOCTL を定義するときは、DeviceIoControl (または IoBuildDeviceIoControlRequest) の呼び出し元と IRP を処理するドライバーの間でデータを渡す方法を示すメソッド ビット フィールドの値を選択する必要があります。

次のいずれかのシステム定義定数を使用して、 メソッド フィールドを設定します。

  • メソッド_バッファード

    バッファー処理された I/O メソッドを指定します。これは通常、要求ごとに少量のデータを転送するために使用されます。 デバイス ドライバーと中間ドライバーのほとんどの I/O コントロール コードでは、この値が使用されます。

    システムがMETHOD_BUFFERED I/O 制御コードのデータ・バッファーを指定する方法については、入出力制御コード のバッファー記述を参照してください。

    バッファー I/O の詳細については、「バッファー I/O の 使用」を参照してください。

  • METHOD_IN_DIRECT または METHOD_OUT_DIRECT

    ダイレクト I/O メソッドを指定します。通常、DMA または PIO を使用して大量のデータを読み取ったり書き込んだりするために使用され、迅速に転送する必要があります。

    • DeviceIoControl または IoBuildDeviceIoControlRequest の呼び出し元がドライバーにデータを渡す場合は、METHOD_IN_DIRECTを指定します。

    • DeviceIoControl または IoBuildDeviceIoControlRequest の呼び出し元がドライバーからデータを受信するかどうかをMETHOD_OUT_DIRECT指定します。

    システムが METHOD_IN_DIRECT および METHOD_OUT_DIRECT I/O 制御コードのデータ・バッファーを指定する方法については、入出力制御コード のバッファー記述を参照してください。

    ダイレクト I/O の詳細については、「 ダイレクト I/O の使用」を参照してください。

  • メソッド_どちらもなし

    I/O メソッドがバッファーまたはダイレクトでないことを指定します。 I/O マネージャーは、システム バッファーまたは MDL を提供しません。 IRP は、検証またはマッピングせずに 、DeviceIoControl または IoBuildDeviceIoControlRequest に指定された入力バッファーと出力バッファーのユーザー モードの仮想アドレスを提供します。

    システムがMETHOD_NEITHER I/O 制御コードのデータ・バッファーを指定する方法については、入出力制御コード のバッファー記述を参照してください。

    このメソッドは、ドライバーが I/O 制御要求を発生させたスレッドのコンテキストで実行されていることが保証されている場合にのみ使用できます。 この条件を満たすことが保証されるのは最上位レベルのカーネル モード ドライバーのみであるため、低レベルのデバイス ドライバーに渡される IOCTL にはMETHOD_NEITHERはほとんど使用されません。

    このメソッドでは、最上位レベルのドライバーは次のようになります。

    • 要求を受信したユーザー データに対するバッファーアクセスまたは直接アクセスを設定するかどうかを決定する必要があります。
    • 場合によっては、ユーザー バッファーをロックダウンする必要があります。
    • 構造化例外ハンドラーでユーザー バッファーへのアクセスをラップする必要があります (例外の 処理を参照)。

    それ以外の場合、元のユーザー モードの呼び出し元は、ドライバーがそれを使用する前にバッファーされたデータを変更する可能性があります。または、ドライバーがユーザー バッファーにアクセスする場合と同じように呼び出し元をスワップ アウトできます。

    詳細については、「バッファー付 I/O もダイレクト I/O も使用しない方法」を参照してください。

その他の便利なマクロ

次のマクロは、IOCTL から 16 ビット DeviceType フィールドと 2 ビット メソッド フィールドを抽出する場合に役立ちます。

#define DEVICE_TYPE_FROM_CTL_CODE(ctrlCode)   (((ULONG)(ctrlCode & 0xffff0000)) >> 16)
#define METHOD_FROM_CTL_CODE(ctrlCode)        ((ULONG)(ctrlCode & 3))

これらのマクロは 、Wdm.h および Ntddk.h で定義されています。