定义 I/O 控制代码

定义新的 IOCTL 时,请务必记住以下规则:

  • 如果新的 IOCTL 可供用户模式软件组件使用,则必须将 IOCTL 与 IRP_MJ_DEVICE_CONTROL 请求一起使用。 用户模式组件通过调用 DeviceIoControl(Win32 函数)发送IRP_MJ_DEVICE_CONTROL请求。
  • 如果新的 IOCTL 仅可用于内核模式驱动程序组件,则必须将 IOCTL 与 IRP_MJ_INTERNAL_DEVICE_CONTROL 请求一起使用。 内核模式组件通过调用 IoBuildDeviceIoControlRequest 创建IRP_MJ_INTERNAL_DEVICE_CONTROL请求。 有关详细信息,请参阅 在驱动程序中创建 IOCTL 请求

I/O 控制代码是由多个字段组成的 32 位值。 下图演示了 I/O 控件代码的布局。

说明 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和更高值。 请注意,供应商分配的值设置 Common 位。

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
指定 直接 I/O 方法,该方法通常用于使用 DMA 或 PIO 读取或写入必须快速传输的大量数据。

如果 DeviceIoControlIoBuildDeviceIoControlRequest 的调用方将数据传递给驱动程序,请指定METHOD_IN_DIRECT。

如果 DeviceIoControlIoBuildDeviceIoControlRequest 的调用方将从驱动程序接收数据,请指定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 提供指定给 DeviceIoControlIoBuildDeviceIoControlRequest 的输入和输出缓冲区的用户模式虚拟地址,而无需验证或映射它们。

有关系统如何为METHOD_NEITHER I/O 控制代码指定数据缓冲区的信息,请参阅 I/O 控制代码的缓冲区说明

仅当可以保证驱动程序在发起 I/O 控制请求的线程的上下文中运行时,才能使用此方法。 只有最高级别的内核模式驱动程序才能保证满足此条件,因此METHOD_NEITHER很少用于传递给低级别设备驱动程序的 I/O 控制代码。

使用此方法时,最高级别驱动程序必须确定是在收到请求时设置缓冲访问还是直接访问用户数据,可能必须锁定用户缓冲区,并且必须将其对用户缓冲区的访问权限包装在结构化异常处理程序中, (请参阅) 处理异常 。 否则,发起用户模式调用方可能会在驱动程序可以使用缓冲数据之前更改缓冲数据,或者调用方可能会在驱动程序访问用户缓冲区时交换出来。

有关详细信息,请参阅 既不使用缓冲 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可以是 ORed。

某些系统定义的 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)

注意

在为新的 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 中定义。