Definieren von I/O-Steuerelementcodes

Beim Definieren neuer IOCTLs ist es wichtig, sich die folgenden Regeln zu merken:

  • Wenn ein neues IOCTL für Benutzermodus-Softwarekomponenten zur Verfügung steht, muss das IOCTL mit IRP_MJ_DEVICE_CONTROL Anforderungen verwendet werden. Benutzermoduskomponenten senden IRP_MJ_DEVICE_CONTROL Anforderungen, indem Sie deviceIoControl aufrufen, was eine Win32-Funktion ist.
  • Wenn ein neues IOCTL nur für Kernelmodustreiberkomponenten verfügbar ist, muss das IOCTL mit IRP_MJ_INTERNAL_DEVICE_CONTROL Anforderungen verwendet werden. Kernelmodus-Komponenten erstellen IRP_MJ_INTERNAL_DEVICE_CONTROL Anforderungen durch Aufrufen von IoBuildDeviceIoControlRequest. Weitere Informationen finden Sie unter Erstellen von IOCTL-Anforderungen in Treibern.

Ein I/O-Steuerelementcode ist ein 32-Bit-Wert, der aus mehreren Feldern besteht. In der folgenden Abbildung wird das Layout von I/O-Steuerelementcodes veranschaulicht.

diagram illustrating the i/o control code layout.

Verwenden Sie das vom System bereitgestellte CTL_CODE-Makro , das in Wdm.h und Ntddk.h definiert ist, um neue I/O-Steuerelementcodes zu definieren. Die Definition eines neuen IOCTL-Codes, ob für die Verwendung mit IRP_MJ_DEVICE_CONTROL oder IRP_MJ_INTERNAL_DEVICE_CONTROL Anforderungen vorgesehen, verwendet das folgende Format:

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

Wählen Sie einen beschreibenden Konstantennamen für das IOCTL aus, des Formulars IOCTL_Device_Function, wobei Device den Typ des Geräts und der Funktion angibt, den Vorgang angibt. Ein Beispielkonstantenname ist IOCTL_VIDEO_ENABLE_CURSOR.

Geben Sie die folgenden Parameter an das CTL_CODE-Makro an:

DeviceType
Identifiziert den Gerätetyp. Dieser Wert muss dem Wert entsprechen, der im DeviceType-Element der DEVICE_OBJECT Struktur des Treibers festgelegt ist. (Siehe Angeben von Gerätetypen). Werte von weniger als 0x8000 sind für Microsoft reserviert. Werte von 0x8000 und höher können von Anbietern verwendet werden. Beachten Sie, dass die vom Anbieter zugewiesenen Werte das Allgemeine Bit festlegen.

FunctionCode
Identifiziert die funktion, die vom Treiber ausgeführt werden soll. Werte von weniger als 0x800 sind für Microsoft reserviert. Werte von 0x800 und höher können von Anbietern verwendet werden. Beachten Sie, dass die vom Anbieter zugewiesenen Werte das benutzerdefinierte Bit festlegen.

TransferType
Gibt an, wie das System Daten zwischen dem Aufrufer von DeviceIoControl (oder IoBuildDeviceIoControlRequest) und dem Treiber übergeben, der die IRP behandelt.

Verwenden Sie eine der folgenden systemdefinierten Konstanten:

METHOD_BUFFERED
Gibt die gepufferte I/O-Methode an, die in der Regel zum Übertragen kleiner Datenmengen pro Anforderung verwendet wird. Die meisten I/O-Steuerelementcodes für Geräte- und Zwischentreiber verwenden diesen TransferType-Wert .

Informationen dazu, wie das System Datenpuffer für METHOD_BUFFERED I/O-Steuerelementcodes angibt, finden Sie unter "Pufferbeschreibungen für I/O-Steuerelementcodes".

Weitere Informationen zu gepufferten I/O finden Sie unter Verwenden von Puffer-I/O.

METHOD_IN_DIRECT oder METHOD_OUT_DIRECT
Gibt die direkte I/O-Methode an, die in der Regel für das Lesen oder Schreiben großer Datenmengen mit DMA oder PIO verwendet wird, die schnell übertragen werden müssen.

Geben Sie METHOD_IN_DIRECT an, wenn der Aufrufer von DeviceIoControl oder IoBuildDeviceIoControlRequest Daten an den Treiber übergeben.

Geben Sie METHOD_OUT_DIRECT an, wenn der Aufrufer von DeviceIoControl oder IoBuildDeviceIoControlRequest Daten vom Treiber erhält.

Informationen dazu, wie das System Datenpuffer für METHOD_IN_DIRECT und METHOD_OUT_DIRECT I/O-Steuerelementcodes angibt, finden Sie unter Pufferbeschreibungen für I/O-Steuerelementcodes.

Weitere Informationen zu direct I/O finden Sie unter Verwenden von Direct I/O.

METHOD_NEITHER
Gibt weder pufferte noch direkte I/O an. Der I/O-Manager stellt keine Systempuffer oder MDLs bereit. Das IRP liefert die virtuellen Adressen des Benutzermodus der Eingabe- und Ausgabepuffer, die für DeviceIoControl oder IoBuildDeviceIoControlRequest angegeben wurden, ohne sie zu überprüfen oder zuzuordnen.

Informationen dazu, wie das System Datenpuffer für METHOD_NEITHER I/O-Steuerelementcodes angibt, finden Sie unter Pufferbeschreibungen für I/O-Steuerelementcodes.

Diese Methode kann nur verwendet werden, wenn der Treiber im Kontext des Threads ausgeführt werden kann, der die I/O-Steuerelementanforderung stammt. Nur ein Kernelmodustreiber auf höchster Ebene ist garantiert, diese Bedingung zu erfüllen, daher wird METHOD_NEITHER selten für die I/O-Steuerelementcodes verwendet, die an Gerätetreiber mit niedriger Ebene übergeben werden.

Mit dieser Methode muss der höchste Treiber bestimmen, ob der pufferige oder direkte Zugriff auf Benutzerdaten zum Empfang der Anforderung eingerichtet werden soll, möglicherweise den Benutzerpuffer sperren muss und den Zugriff auf den Benutzerpuffer in einem strukturierten Ausnahmehandler umschließen muss (siehe Behandeln von Ausnahmen). Andernfalls kann der ursprüngliche Benutzermodusanrufer die zwischengespeicherten Daten ändern, bevor der Treiber es verwenden kann, oder der Anrufer könnte wie der Treiber auf den Benutzerpuffer zugreifen.

Weitere Informationen finden Sie unter "Weder pufferte noch direkte I/O".

RequiredAccess
Gibt den Zugriffstyp an, den ein Anrufer beim Öffnen des Dateiobjekts anfordern muss, das das Gerät darstellt (siehe IRP_MJ_CREATE). Der I/O-Manager erstellt IRPs und ruft den Treiber nur mit einem bestimmten I/O-Steuerelementcode auf, wenn der Anrufer die angegebenen Zugriffsrechte angefordert hat. RequiredAccess wird mithilfe der folgenden systemdefinierten Konstanten angegeben:

FILE_ANY_ACCESS
Der I/O-Manager sendet den IRP für jeden Aufrufer, der über einen Handle an das Dateiobjekt verfügt, das das Zielgerätobjekt darstellt.

FILE_READ_DATA
Der I/O-Manager sendet das IRP nur für einen Anrufer mit Lesezugriffsrechten, sodass der zugrunde liegende Gerätetreiber Daten vom Gerät in den Systemspeicher übertragen kann.

FILE_WRITE_DATA
Der I/O-Manager sendet das IRP nur für einen Anrufer mit Schreibzugriffsrechten, sodass der zugrunde liegende Gerätetreiber Daten vom Systemspeicher an sein Gerät übertragen kann.

FILE_READ_DATA und FILE_WRITE_DATA können zusammengelassen werden, wenn der Anrufer sowohl Lese- als auch Schreibzugriffsrechte haben muss.

Einige systemdefinierte I/O-Steuerelementcodes verfügen über einen RequiredAccess-Wert von FILE_ANY_ACCESS, mit dem der Anrufer die bestimmte IOCTL unabhängig vom Zugriff auf das Gerät senden kann. Beispiele sind I/O-Steuerelementcodes, die an Treiber von exklusiven Geräten gesendet werden.

Andere systemdefinierte I/O-Steuerelementcodes erfordern, dass der Anrufer Leserechte, Schreibzugriffsrechte oder beides hat. Die folgende Definition des öffentlichen I/O-Steuerelementcodes IOCTL_DISK_SET_PARTITION_INFO zeigt beispielsweise, dass diese I/O-Anforderung nur an einen Treiber gesendet werden kann, wenn der Anrufer sowohl Lese- als auch Schreibzugriffsrechte hat:

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

Hinweis

Bevor Sie FILE_ANY_ACCESS für einen neuen IOCTL-Code angeben, müssen Sie absolut sicher sein, dass der uneingeschränkte Zugriff auf Ihr Gerät keinen möglichen Pfad für böswillige Benutzer erstellt, um das System zu kompromittieren.

Treiber können IoValidateDeviceIoControlAccess verwenden, um eine striktere Zugriffsüberprüfung durchzuführen, als dies von den Von einem IOCTL-RequiredAccess-Bit bereitgestellt wird.

Weitere nützliche Makros

Die folgenden Makros sind nützlich für das Extrahieren der Felder 16-Bit-DeviceType und 2-Bit-TransferType aus einem IOCTL-Code:

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

Diese Makros werden in Wdm.h und Ntddk.h definiert.