Windows API Reference

DeviceIoControl

The DeviceIoControl function sends a control code directly to a specified device driver to retrieve information or modify device state.

Syntax

BOOL DeviceIoControl(
        HANDLE       hDevice,
        DWORD        dwIoControlCode,
        LPVOID       lpInBuffer,
        DWORD        nInBufferSize,
        LPVOID       lpOutBuffer,
        DWORD        nOutBufferSize,
        LPDWORD      lpBytesReturned,
        LPOVERLAPPED lpOverlapped
    );

Parameters

Parameter Description
hDevice A handle to the device. This handle is obtained by calling the CreateFile function.
dwIoControlCode The control code for the operation. This parameter value specifies the actual device-dependent operation to perform.
lpInBuffer A pointer to a buffer that contains data required for the operation specified by the dwIoControlCode parameter. This parameter can be NULL if the dwIoControlCode parameter specifies an operation that does not require input data.
nInBufferSize The size, in bytes, of the buffer pointed to by lpInBuffer.
lpOutBuffer A pointer to a buffer that receives data from the specified device. This parameter can be NULL if the dwIoControlCode parameter specifies an operation that does not return data.
nOutBufferSize The size, in bytes, of the buffer pointed to by lpOutBuffer.
lpBytesReturned A pointer to a DWORD value that receives the size, in bytes, of the data returned by the device driver, located in the buffer pointed to by lpOutBuffer. If this parameter is NULL, the information is not returned.
lpOverlapped A pointer to an OVERLAPPED structure that is required for asynchronous operations. See the Remarks section for more information. This parameter can be NULL if the hDevice handle is not opened with FILE_FLAG_OVERLAPPED.

Return Value

If the operation succeeds, the function returns a nonzero value. If the operation fails, the return value is zero. To get extended error information, call GetLastError.

Remarks

The DeviceIoControl function is the primary mechanism for device-specific operations that are not covered by the standard file I/O functions. It allows applications to communicate directly with device drivers.

The dwIoControlCode parameter is a crucial part of this function. It is a 32-bit value that encodes information about the operation, including:

Device drivers define their own set of IOCTL codes. These codes are typically defined in header files specific to the device or driver.

Important

The exact set of IOCTL codes available and their behavior is entirely dependent on the specific device driver being targeted. Always consult the documentation for the device or driver to understand the valid IOCTL codes and their associated input and output buffer structures.

Commonly Used IOCTLs

While specific codes vary, here are some general categories and examples of IOCTLs you might encounter:

Example: Getting Disk Geometry

The following example demonstrates how to use DeviceIoControl to get disk geometry information. Note that the exact IOCTL code (e.g., IOCTL_DISK_GET_DRIVE_GEOMETRY) and the structure used for output (DISKDRIVE_GEOMETRY) are standard but specific to disk devices.

#include <windows.h>
#include <fileapi.h>
#include <ioapiset.h>
#include <iostream>

int main() {
    HANDLE hDevice = CreateFile(
        L"\\\\.\\PhysicalDrive0", // Target the first physical drive
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
    );

    if (hDevice == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to open device. Error: " << GetLastError() << std::endl;
        return 1;
    }

    DISKDRIVE_GEOMETRY geometry;
    DWORD bytesReturned;

    if (DeviceIoControl(
        hDevice,
        IOCTL_DISK_GET_DRIVE_GEOMETRY,
        NULL,
        0,
        &geometry,
        sizeof(geometry),
        &bytesReturned,
        NULL
    )) {
        std::cout << "Disk Geometry Information:" << std::endl;
        std::cout << "  Cylinders: " << geometry.Cylinders.QuadPart << std::endl;
        std::cout << "  TracksPerCylinder: " << geometry.TracksPerCylinder << std::endl;
        std::cout << "  SectorsPerTrack: " << geometry.SectorsPerTrack << std::endl;
        std::cout << "  BytesPerSector: " << geometry.BytesPerSector << std::endl;
        std::cout << "  TotalSectors: " << geometry.TotalSectors.QuadPart << std::endl;
    } else {
        std::cerr << "DeviceIoControl failed. Error: " << GetLastError() << std::endl;
    }

    CloseHandle(hDevice);
    return 0;
}

Security Considerations

Using DeviceIoControl directly with arbitrary IOCTL codes can pose security risks if not done carefully. Malicious or malformed IOCTL codes could lead to unintended system behavior, data corruption, or security vulnerabilities. Always validate input and ensure you are using well-documented and trusted IOCTL codes.