I/O 制御コードの定義
新しい IOCTL を定義するときは、次の規則を覚えておく必要があります。
- ユーザーモードのソフトウェア コンポーネントで新しい IOCTL を使用できる場合は、IOCTL を IRP_MJ_DEVICE_CONTROL 要求で使用する必要があります。 ユーザーモード コンポーネントは、Win32 関数である DeviceIoControl を呼び出して、IRP_MJ_DEVICE_CONTROL 要求を送信します。
- 新しい IOCTL がカーネルモード ドライバー コンポーネントでのみ使用できる場合は、IOCTL を IRP_MJ_INTERNAL_DEVICE_CONTROL 要求で使用する必要があります。 カーネルモード コンポーネントは、IoBuildDeviceIoControlRequest を呼び出して、IRP_MJ_INTERNAL_DEVICE_CONTROL 要求を作成します。 詳細については、「ドライバーでの IOCTL 要求の作成」を参照してください。
I/O 制御コードは、複数のフィールドで構成される 32 ビット値です。 次の図は、I/O 制御コードのレイアウトを示しています。
Wdm.h および Ntddk.h で定義されているシステム提供の CTL_CODE マクロを使用して、新しい I/O 制御コードを定義します。 新しい IOCTL コードの定義は、IRP_MJ_DEVICE_CONTROL 要求と IRP_MJ_INTERNAL_DEVICE_CONTROL 要求のどちらで使用するかに関係なく、次の形式を使用します。
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
IOCTL のわかりやすい定数名を、IOCTL_Device_Function という形式で選択します。Device はデバイスの種類を示し、Function は操作を示します。 定数名はたとえば、IOCTL_VIDEO_ENABLE_CURSOR のようになります。
CTL_CODE マクロには、次のパラメーターを指定します。
DeviceType
デバイスの種類を特定します。 この値は、ドライバー の DEVICE_OBJECT 構造体の DeviceType メンバーに設定されている値と一致する必要があります (「デバイスの種類の指定」を参照)。 0x8000 未満の値は、Microsoft 用に予約されています。 ベンダーは、0x8000 以上の値を使用できます。 ベンダー割り当て値によって共通ビットが設定されることに注意してください。
FunctionCode
ドライバーによって実行される関数を特定します。 0x800 未満の値は、Microsoft 用に予約されています。 ベンダーは、0x800 以上の値を使用できます。 ベンダー割り当て値によってカスタムビットが設定されることに注意してください。
TransferType
DeviceIoControl (または IoBuildDeviceIoControlRequest) の呼び出し元と IRP を処理するドライバーの間で、システムがデータを渡す方法を示します。
次のいずれかのシステム定義定数を使用します。
METHOD_BUFFERED
バッファー付き I/O メソッドを指定します。これは通常、要求ごとに少量のデータを転送するために使用されます。 デバイス ドライバーと中間ドライバーのほとんどの I/O 制御コードでは、この TransferType 値を使用します。
システムが METHOD_BUFFERED I/O 制御コードのデータ バッファーを指定する方法については、「I/O 制御コードのバッファー記述」を参照してください。
バッファー付き I/O の詳細については、「バッファー I/O の使用」を参照してください。
METHOD_IN_DIRECT または METHOD_OUT_DIRECT
DMA または PIO を 使用して大量のデータの読み取りまたは書き込みに通常使用されるダイレクト I/O メソッドを指定します。このメソッドは、迅速に転送する必要があります。
DeviceIoControl または IoBuildDeviceIoControlRequest の呼び出し元がドライバーにデータを渡す場合は、METHOD_IN_DIRECT を指定します。
DeviceIoControl または IoBuildDeviceIoControlRequest の呼び出し元がドライバーからデータを受信する場合は、METHOD_OUT_DIRECT を指定します。
システムが METHOD_IN_DIRECT および METHOD_OUT_DIRECT I/O 制御コードのデータ バッファーを指定する方法については、「I/O 制御コードのバッファー記述」を参照してください。
ダイレクト I/O の詳細については、「ダイレクト I/O の使用」を参照してください。
METHOD_NEITHER
バッファー付き I/O とダイレクト I/O のいずれも指定しません。 I/O マネージャーは、システム バッファーと MDL のいずれも提供しません。 IRP は、検証とマッピングのどちらも行わずに、DeviceIoControl または IoBuildDeviceIoControlRequest に指定された I/O バッファーのユーザーモードの仮想アドレスを提供します。
システムが METHOD_NEITHER I/O 制御コードのデータ バッファーを指定する方法については、「I/O 制御コードのバッファー記述」を参照してください。
このメソッドは、ドライバーが I/O 制御要求を発信したスレッドのコンテキストで実行できることが保証される場合にのみ使用できます。 この条件を満たすことが保証されるのは最上位カーネルモード ドライバーのみであるため、低レベル デバイス ドライバーに渡される I/O 制御コードに METHOD_NEITHER が使われることはほとんどありません。
このメソッドでは、最高レベルのドライバーは、要求の受信時にユーザー データへのバッファーされたアクセスまたは直接アクセスを設定するかどうかを決定する必要があります。場合によっては、ユーザー バッファーをロックダウンして、構造化例外ハンドラーでユーザー バッファーへのアクセスをラップしなければなりません (「例外処理」を参照)。 そうしなければ、ドライバーがそれを使用する前に元のユーザーモードの呼び出し元がバッファーされたデータを変更したり、ドライバーがユーザー バッファーにアクセスした場合と同じように呼び出し元がスワップ アウトされたりする可能性があります。
詳細については「バッファー付き I/O とダイレクト I/O のどちらも使用しない」を参照してください。
RequiredAccess
デバイスを表すファイル オブジェクトを開くときに呼び出し元が要求すべきアクセスの種類を示します (「IRP_MJ_CREATE」を参照)。 I/O マネージャーは、呼び出し元が指定したアクセス権を要求した場合にのみ IRP を作成し、、特定の I/O 制御コードを使用してドライバーを呼び出します。 RequiredAccess は、次のシステム定義定数を使用して指定します。
FILE_ANY_ACCESS
I/O マネージャーは、ターゲット デバイス オブジェクトを表すファイル オブジェクトへのハンドルを持つ、すべての呼び出し元に対して IRP を送信します。
FILE_READ_DATA
I/O マネージャーは、読み取りアクセス権を持つ呼び出し元に対してのみ IRP を送信します。これにより、基になるデバイス ドライバーがデバイスからシステム メモリにデータを転送できます。
FILE_WRITE_DATA
I/O マネージャーは、書き込みアクセス権を持つ呼び出し元に対してのみ IRP を送信します。これにより、基になるデバイス ドライバーがシステム メモリからデバイスにデータを転送できます。
呼び出し元に読み取りアクセス権と書き込みアクセス権の両方が必要な場合は、FILE_READ_DATA と FILE_WRITE_DATA を OR で指定できます。
一部のシステム定義 I/O 制御コードでは、RequiredAccess 値が FILE_ANY_ACCESS に設定されている場合があります。この場合、呼び出し元は、デバイスに付与されたアクセス権と関係なしに、特定の IOCTL を送信できます。 たとえば、排他的デバイスのドライバーに送信される I/O 制御コードがこれに当たります。
その他のシステム定義の I/O 制御コードでは、呼び出し元に読み取りアクセス権、書き込みアクセス権、またはその両方が必要です。 たとえば、パブリック I/O 制御コードである IOCTL_DISK_SET_PARTITION_INFO の次の定義は、呼び出し元が読み取りアクセス権と書き込みアクセス権の両方を持っている場合にのみ、この I/O 要求をドライバーに送信できることを示しています。
#define IOCTL_DISK_SET_PARTITION_INFO\
CTL_CODE(IOCTL_DISK_BASE, 0x008, METHOD_BUFFERED,\
FILE_READ_DATA | FILE_WRITE_DATA)
Note
悪意のあるユーザーがシステムを侵害するパスを作成する可能性がないことを絶対に確認できない限り、新しい IOCTL コードに FILE_ANY_ACCESS を指定して、デバイスへの無制限のアクセスを許可してはなりません。
ドライバーは IoValidateDeviceIoControlAccess を使用することで、IOCTL の RequiredAccess ビットによって提供されるアクセス チェックよりも厳密なアクセスを実行できます。
その他の便利なマクロ
次のマクロは、IOCTL コードから 16 ビットの DeviceType フィールドと 2 ビットの TransferType フィールドを抽出する場合に役立ちます。
#define DEVICE_TYPE_FROM_CTL_CODE(ctrlCode) (((ULONG)(ctrlCode & 0xffff0000)) >> 16)
#define METHOD_FROM_CTL_CODE(ctrlCode) ((ULONG)(ctrlCode & 3))
これらのマクロは Wdm.h と Ntddk.h で定義されています。