I/O Devices

Introduction to I/O Devices

The Windows operating system provides a comprehensive set of APIs for interacting with various input/output (I/O) devices. These APIs allow applications to communicate with hardware such as disks, keyboards, mice, printers, network adapters, and specialized peripherals.

Understanding how to manage and utilize I/O devices is crucial for developing robust and efficient Windows applications. This section covers the fundamental concepts and key APIs related to device management, data transfer, and error handling.

Key Concepts

  • Device Drivers: Software components that translate generic I/O requests from the operating system into specific commands for hardware devices.
  • I/O Request Packets (IRPs): Structures used by the operating system to communicate I/O operations to device drivers.
  • File Handles: Abstract representations of open devices or files, used to perform I/O operations.
  • Input/Output Control (IOCTL): A mechanism for applications to send custom commands directly to device drivers.
  • Asynchronous I/O: Allows an application to initiate an I/O operation and continue processing without waiting for the operation to complete.

Core APIs

The following Win32 APIs are fundamental for working with I/O devices:

Device Management

  • CreateFile: Opens an existing file or creates a new file. It can also open existing devices, specify desired access, and share modes.
  • CloseHandle: Closes an open object handle, such as a file or device handle.
  • DeviceIoControl: Sends a custom control code directly to a specified device driver, allowing for device-specific operations.
  • SetupDiGetDeviceInterfaceList, SetupDiEnumDeviceInfo, SetupDiGetDeviceRegistryProperty: APIs for enumerating devices and retrieving their properties.

Data Transfer

  • ReadFile: Reads data from a file or device.
  • WriteFile: Writes data to a file or device.
  • ReadFileEx, WriteFileEx: Asynchronous versions of ReadFile and WriteFile, which use I/O completion routines.

Error Handling

  • GetLastError: Retrieves the last error code set by a function. Common error codes for I/O operations include ERROR_IO_PENDING (for asynchronous operations), ERROR_FILE_NOT_FOUND, and ERROR_DEVICE_DOES_NOT_EXIST.

Important Note on Device Access

Accessing raw hardware devices often requires administrative privileges. Applications designed for general users should typically interact with devices through higher-level abstractions like file system APIs or COM interfaces rather than directly manipulating device drivers.

Example: Reading from a Serial Port

This example demonstrates a simplified approach to reading data from a serial port using CreateFile and ReadFile.


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

int main() {
    HANDLE hSerial = CreateFile(
        L"\\\\.\\COM1",       // Port name
        GENERIC_READ | GENERIC_WRITE, // Read and write access
        0,                    // No sharing
        NULL,                 // Default security attributes
        OPEN_EXISTING,        // Open only if it exists
        FILE_ATTRIBUTE_NORMAL,// Normal file attributes
        NULL                  // No template file
    );

    if (hSerial == INVALID_HANDLE_VALUE) {
        std::cerr << "Error opening serial port: " << GetLastError() << std::endl;
        return 1;
    }

    std::cout << "Serial port opened successfully." << std::endl;

    // Configuration (omitted for brevity, involves GetCommState, SetCommState, etc.)

    BYTE buffer[256];
    DWORD bytesRead;

    // Example read operation
    if (ReadFile(hSerial, buffer, sizeof(buffer) - 1, &bytesRead, NULL)) {
        buffer[bytesRead] = '\0'; // Null-terminate the string
        std::cout << "Read " << bytesRead << " bytes: " << buffer << std::endl;
    } else {
        std::cerr << "Error reading from serial port: " << GetLastError() << std::endl;
    }

    CloseHandle(hSerial);
    std::cout << "Serial port closed." << std::endl;

    return 0;
}
                

Further Reading