问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

深入解析Windows API中的ReadFile函数

创作时间:
作者:
@小白创作中心

深入解析Windows API中的ReadFile函数

引用
CSDN
7
来源
1.
https://blog.csdn.net/weixin_34405557/article/details/85563077
2.
https://www.easefilter.com/kb/fileapi-readfile.htm
3.
https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile#parameters
4.
https://learn.microsoft.com/zh-cn/windows/win32/fileio/testing-for-the-end-of-a-file
5.
https://learn.microsoft.com/zh-tw/windows/win32/api/fileapi/nf-fileapi-readfile
6.
https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile
7.
https://www.cnblogs.com/liujx2019/p/11691495.html

在Windows操作系统中,ReadFile函数是API的重要组成部分,用于从文件或设备(如串口、硬盘)中读取数据。它是同步和异步I/O操作的基础,广泛应用于文件读取、串口通信、网络通信以及传感器数据读取等多种场景。本文将深入解析ReadFile函数的参数、返回值及其实际应用示例,帮助开发者更好地理解和使用这一关键功能。

01

函数基础介绍

ReadFile函数是Windows API中用于读取文件或I/O设备数据的核心函数。其主要功能是从指定的文件或设备中读取数据到应用程序的缓冲区中。该函数支持同步和异步操作模式,适用于各种I/O设备,包括文件、串口、网络套接字等。

与其他读取函数(如freadReadFileEx)相比,ReadFile具有以下特点:

  • 通用性:支持多种I/O设备,不仅限于文件
  • 灵活性:同时支持同步和异步操作
  • 系统级:直接与操作系统内核交互,性能更优
02

函数原型与参数详解

BOOL ReadFile(
  HANDLE       hFile,
  LPVOID       lpBuffer,
  DWORD        nNumberOfBytesToRead,
  LPDWORD      lpNumberOfBytesRead,
  LPOVERLAPPED lpOverlapped
);
参数名称
类型
描述
hFile
HANDLE
要读取的设备句柄(如文件、串口等)
lpBuffer
LPVOID
存储读取数据的缓冲区指针
nNumberOfBytesToRead
DWORD
请求读取的最大字节数
lpNumberOfBytesRead
LPDWORD
指向变量的指针,接收实际读取的字节数
lpOverlapped
LPOVERLAPPED
异步操作时需提供的OVERLAPPED结构体指针
  • hFile:要读取的设备句柄,必须具有读取权限。对于异步操作,句柄需要通过CreateFile函数使用FILE_FLAG_OVERLAPPED标志创建,或者是一个由socketaccept函数返回的套接字句柄。
  • lpBuffer:指向缓冲区的指针,用于存储读取的数据。在读取操作完成前,不应使用此缓冲区。
  • nNumberOfBytesToRead:请求读取的最大字节数。
  • lpNumberOfBytesRead:指向一个变量的指针,用于接收实际读取的字节数。在同步模式下必须提供,异步模式下可以为NULL。
  • lpOverlapped:指向OVERLAPPED结构体的指针。如果hFile是使用FILE_FLAG_OVERLAPPED打开的,则必须提供有效的OVERLAPPED结构体,否则可以为NULL。
03

返回值与错误处理

ReadFile函数的返回值是一个布尔值:

  • 成功时返回非零值(TRUE
  • 失败时返回零(FALSE

当函数返回FALSE时,可以通过GetLastError()函数获取具体的错误码。常见的错误码包括:

  • ERROR_IO_PENDING:异步操作尚未完成
  • ERROR_HANDLE_EOF:同步读取到达文件末尾
  • ERROR_INVALID_HANDLE:无效的设备句柄
  • ERROR_ACCESS_DENIED:没有读取权限

特别需要注意的是,ERROR_IO_PENDING并不表示错误,而是表明异步操作正在等待完成。在这种情况下,可以通过GetOverlappedResult函数检查操作的最终状态。

04

使用示例

同步读取示例

#include <windows.h>
#include <stdio.h>

int main() {
    HANDLE hFile = CreateFile("example.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("Failed to open file.\n");
        return -1;
    }

    char buffer[1024];
    DWORD bytesRead;

    while (ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL)) {
        if (bytesRead > 0) {
            // 处理读取的数据
            printf("%.*s", (int)bytesRead, buffer);
        } else {
            break; // 文件结束
        }
    }

    DWORD error = GetLastError();
    if (error != ERROR_SUCCESS && error != ERROR_HANDLE_EOF) {
        printf("Error reading file: %lu\n", error);
    }

    CloseHandle(hFile);
    return 0;
}

异步读取示例

#include <windows.h>
#include <stdio.h>

void GoDoSomethingElse() {
    printf("Inside GoDoSomethingElse()\n");
}

DWORD AsyncReadFile(HANDLE hEvent, HANDLE hFile) {
    char inBuffer[64];
    DWORD nBytesToRead = 64;
    DWORD dwBytesRead = 0;
    OVERLAPPED stOverlapped = {0};
    DWORD dwError = 0;
    BOOL bResult = FALSE;
    BOOL bContinue = TRUE;

    stOverlapped.hEvent = hEvent;

    while (bContinue) {
        bResult = ReadFile(hFile, inBuffer, nBytesToRead, &dwBytesRead, &stOverlapped);
        dwError = GetLastError();

        if (!bResult) {
            switch (dwError) {
                case ERROR_HANDLE_EOF:
                    printf("\nReadFile returned FALSE and EOF condition, async EOF not triggered.\n");
                    break;
                case ERROR_IO_PENDING:
                    while (TRUE) {
                        GoDoSomethingElse();
                        bResult = GetOverlappedResult(hFile, &stOverlapped, &dwBytesRead, FALSE);
                        if (!bResult) {
                            dwError = GetLastError();
                            if (dwError == ERROR_HANDLE_EOF) {
                                printf("GetOverlappedResult found EOF\n");
                                break;
                            } else if (dwError == ERROR_IO_INCOMPLETE) {
                                continue;
                            } else {
                                printf("Error in GetOverlappedResult: %lu\n", dwError);
                                break;
                            }
                        } else {
                            // 处理读取的数据
                            printf("%.*s", (int)dwBytesRead, inBuffer);
                            break;
                        }
                    }
                    break;
                default:
                    printf("Error in ReadFile: %lu\n", dwError);
                    break;
            }
        } else {
            // 处理读取的数据
            printf("%.*s", (int)dwBytesRead, inBuffer);
        }
    }

    return dwBytesRead;
}
05

注意事项

  1. 缓冲区管理:确保提供的缓冲区在读取操作完成前保持有效,不要在读取过程中释放或重用缓冲区。
  2. 异步操作的线程安全:在多线程环境中使用异步读取时,需要确保OVERLAPPED结构体的线程安全性。
  3. 错误处理:始终检查ReadFile的返回值,并在失败时调用GetLastError()获取详细错误信息。
  4. 文件末尾处理:在同步读取中,当lpNumberOfBytesRead为0且GetLastError()返回ERROR_HANDLE_EOF时,表示已到达文件末尾。
06

对比分析

ReadFileEx相比,ReadFile的主要区别在于:

  • ReadFile同时支持同步和异步操作,而ReadFileEx仅支持异步操作。
  • ReadFile的异步操作需要手动检查完成状态,而ReadFileEx可以指定一个完成例程,在操作完成后自动调用。

总结来说,ReadFile函数是Windows API中处理文件和设备读取的核心工具。通过理解其参数、返回值和使用场景,开发者可以更有效地进行I/O操作,特别是在需要高性能和灵活控制的系统级开发中。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号