Reading From and Writing to Files

This document provides an overview of how to perform basic file input and output operations in Windows applications. Understanding these fundamental operations is crucial for managing data persistence and interacting with the file system.

Core Concepts

File operations typically involve:

Using the File System API

The Windows API provides a rich set of functions for file manipulation. For low-level file operations, the CreateFile function is central.

Opening a File with CreateFile

The CreateFile function is used to open or create a file and returns a handle to the file. This handle is then used in subsequent I/O operations.


HANDLE CreateFile(
  LPCSTR                lpFileName,              // File name
  DWORD                 dwDesiredAccess,         // Access mode (e.g., GENERIC_READ, GENERIC_WRITE)
  DWORD                 dwShareMode,             // Sharing mode
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,    // Security attributes
  DWORD                 dwCreationDisposition,   // How to create or open
  DWORD                 dwFlagsAndAttributes,    // File attributes and flags
  HANDLE                hTemplateFile            // Handle to template file
);
            

Key parameters:

If the function succeeds, the return value is an open handle to the specified file. If the function fails, the return value is INVALID_HANDLE_VALUE.

Reading from a File with ReadFile

Once a file handle is obtained, ReadFile can be used to read data from the file.


BOOL ReadFile(
  HANDLE       hFile,               // Handle to the file
  LPVOID       lpBuffer,            // Buffer that receives the data
  DWORD        nNumberOfBytesToRead,// Number of bytes to read
  LPDWORD      lpNumberOfBytesRead, // Number of bytes actually read
  LPOVERLAPPED lpOverlapped         // Overlapped structure for asynchronous I/O
);
            

The data read is placed into the buffer pointed to by lpBuffer. lpNumberOfBytesRead receives the actual number of bytes read.

Writing to a File with WriteFile

Similarly, WriteFile is used to write data to an opened file.


BOOL WriteFile(
  HANDLE       hFile,                  // Handle to the file
  LPCVOID      lpBuffer,               // Buffer containing the data
  DWORD        nNumberOfBytesToWrite,  // Number of bytes to write
  LPDWORD      lpNumberOfBytesWritten, // Number of bytes written
  LPOVERLAPPED lpOverlapped            // Overlapped structure for asynchronous I/O
);
            

The data to be written is supplied in lpBuffer. lpNumberOfBytesWritten will contain the number of bytes successfully written.

Closing a File with CloseHandle

It is essential to close file handles when they are no longer needed to free system resources.


BOOL CloseHandle(
  HANDLE hObject // Handle to the object
);
            

This function closes an open object handle, including file handles.

Example: Basic File Writing

The following C++ snippet demonstrates writing a string to a text file:


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

int main() {
    HANDLE hFile;
    DWORD dwBytesWritten;
    const char* message = "Hello, Windows File I/O!";
    const char* filename = "example.txt";

    hFile = CreateFile(
        filename,
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFile == INVALID_HANDLE_VALUE) {
        std::cerr << "Unable to open file: " << GetLastError() << std::endl;
        return 1;
    }

    if (!WriteFile(
            hFile,
            message,
            strlen(message),
            &dwBytesWritten,
            NULL)) {
        std::cerr << "Unable to write to file: " << GetLastError() << std::endl;
        CloseHandle(hFile);
        return 1;
    }

    std::cout << dwBytesWritten << " bytes written to " << filename << std::endl;

    CloseHandle(hFile);
    return 0;
}
            

Example: Basic File Reading

Here's a C++ example for reading the content of a text file:


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

int main() {
    HANDLE hFile;
    DWORD dwBytesRead;
    const char* filename = "example.txt";
    char buffer[1024];

    hFile = CreateFile(
        filename,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFile == INVALID_HANDLE_VALUE) {
        std::cerr << "Unable to open file: " << GetLastError() << std::endl;
        return 1;
    }

    while (true) {
        if (!ReadFile(
                hFile,
                buffer,
                sizeof(buffer) - 1, // Leave space for null terminator
                &dwBytesRead,
                NULL)) {
            std::cerr << "Unable to read from file: " << GetLastError() << std::endl;
            CloseHandle(hFile);
            return 1;
        }

        if (dwBytesRead == 0) {
            break; // End of file
        }

        buffer[dwBytesRead] = '\0'; // Null-terminate the string
        std::cout << buffer;
    }

    CloseHandle(hFile);
    return 0;
}
            

High-Level I/O with .NET Framework

For managed code applications, the .NET Framework provides a more abstract and object-oriented approach to file I/O using classes in the System.IO namespace. These classes handle many of the underlying details, making file operations simpler and safer.

Writing to a File (.NET)

The File.WriteAllText method is a convenient way to write a string to a file.


using System.IO;

// ...

string textToWrite = "This is written using .NET!";
string filePath = "dotnet_example.txt";

try
{
    File.WriteAllText(filePath, textToWrite);
    Console.WriteLine($"Successfully wrote to {filePath}");
}
catch (Exception ex)
{
    Console.WriteLine($"Error writing file: {ex.Message}");
}
            

Reading from a File (.NET)

Similarly, File.ReadAllText reads the entire content of a file into a string.


using System.IO;

// ...

string filePath = "dotnet_example.txt";

try
{
    string fileContent = File.ReadAllText(filePath);
    Console.WriteLine($"Content of {filePath}:\n{fileContent}");
}
catch (Exception ex)
{
    Console.WriteLine($"Error reading file: {ex.Message}");
}
            
Always ensure you handle potential exceptions when performing file operations, as operations can fail due to permissions, disk full errors, or file not found issues.