共用方式為


定義 I/O 控制程式碼

本文說明如何建立唯一的 I/O 控件程式代碼 (IOCTL)。 IOCTLs 可以是:

  • 公用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),並應設定 Common 位。 值 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_CONTROLIRP_MJ_INTERNAL_DEVICE_CONTROL 請求,都會使用下列格式:

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

選擇IOCTL的描述性常數名稱,其格式為 IOCTL_Device_Function,其中 Device 會指出 裝置的類型 ,而 Function 表示作業。 例如,系統提供的 IOCTL_VIDEO_ENABLE_CURSOR 常數使用 “VIDEO” 作為 裝置,而使用 “ENABLE_CURSOR” 作為 函數

設定存取位元的指引

定義新的 IOCTL 時,您必須為 Access 位欄位選擇值,指出呼叫端 在開啟代表裝置的檔案物件時必須要求的存取類型。 只有在呼叫端要求指定的訪問許可權時,I/O 管理員才會建立 IRP,並使用特定的 IOCTL 呼叫驅動程式。

存取 是使用下列系統定義的常數來指定:

  • 任何文件存取權限

    I/O 管理員會將 IRP 傳送給任何具有控制代碼的呼叫端,而該控制代碼是代表目標設備物件的檔案物件。 在為新的 IOCTL 程式代碼指定FILE_ANY_ACCESS之前,您絕對必須確定允許不受限制的裝置存取不會建立惡意使用者入侵系統可能的路徑。

  • 文件_讀取_數據

    I/O 管理員只會針對具有讀取訪問許可權的呼叫端傳送 IRP,讓基礎設備驅動器將資料從裝置傳輸到系統記憶體。

  • FILE_WRITE_DATA(寫入資料權限)

    I/O 管理員只會針對具有寫入訪問許可權的呼叫端傳送 IRP,讓基礎設備驅動器將數據從系統記憶體傳輸到其裝置。

如果呼叫端必須同時具有讀取和寫入訪問權限,FILE_READ_DATA 和 FILE_WRITE_DATA 可以一起進行 OR 運算。

某些系統定義的 I/O 控制程式代碼具有 access 值FILE_ANY_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 的Access 位所提供的更嚴格的存取檢查。

設定方法位的指引

定義新的 IOCTL 時,您必須為 方法 位字段選擇一個值,指出系統如何在 DeviceIoControl 呼叫者(或 IoBuildDeviceIoControlRequest) 和處理 IRP 的驅動程式之間傳遞數據。

使用下列其中一個系統定義的常數來設定 [方法] 欄位。

  • METHOD_BUFFERED(緩衝方法)

    指定 緩衝的 I/O 方法,通常用於每次請求傳輸少量資料。 裝置和中繼驅動程式的大部分 I/O 控制碼都會使用此值。

    如需系統如何指定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

  • 方法_兩者皆非

    指定 I/O 方法未經過緩衝處理或直接處理。 I/O 管理員不會提供任何系統緩衝區或 MDL。 IRP 提供指定給 DeviceIoControlIoBuildDeviceIoControlRequest 之輸入和輸出緩衝區的使用者模式虛擬位址,而不需要驗證或對應它們。

    如需系統如何指定METHOD_NEITHER I/O 控制碼之數據緩衝區的資訊,請參閱 I/O 控件代碼的緩衝區描述

    只有當驅動程式保證在產生 I/O 控制項要求的線程內容中執行時,才能使用這個方法。 保證只有最高層級的核心模式驅動程式符合此條件,因此METHOD_NEITHER很少用於傳遞至低階設備驅動器的 IOCTL。

    使用此方法時,最高層級的驅動程式就能:

    • 必須判斷是否要在收到要求時設定緩衝處理或直接存取用戶數據。
    • 可能必須鎖定用戶緩衝區。
    • 必須在結構化例外狀況處理程式中包裝用戶緩衝區的存取權(請參閱 處理例外狀況)。

    否則,原始的使用者模式呼叫端可能會在驅動程式使用之前更改緩衝資料,或者在驅動程式存取使用者緩衝區時,呼叫端可能被交換出去。

    如需詳細資訊,請參閱 不使用緩衝或直接 I/O

其他有用的巨集

下列巨集適用於從 IOCTL 擷取 16 位 DeviceType 和 2 位 Method 字段。

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

這些巨集定義於 Wdm.hNtddk.h 中。