【问题标题】:ReadFile and WriteFile with Overlapped IO result if not ERROR_IO_PENDING?如果不是 ERROR_IO_PENDING,则 ReadFile 和 WriteFile 具有重叠的 IO 结果?
【发布时间】:2019-07-11 05:47:25
【问题描述】:

WriteFile() 上 FILE_FLAG_OVERLAPPED 的文档说您必须提供 OVERLAP 并建议为 lpNumberOfBytesWritten 设置 NULL,因为值具有误导性。但是GetOverlappedResult() 的文档说只有在WriteFile() 返回 FALSE 和 ERROR_IO_PENDING 时才调用。这样就剩下了 ReadFile() / WriteFile() 在 API 调用本身中完成的情况。你应该如何获得读/写的字节数?你认为这是请求的号码吗?但是WriteFile() 说“当写入缓冲区空间不足的非阻塞、字节模式管道句柄时,WriteFile 返回 TRUE,并带有 * lpNumberOfBytesWritten

TIA!!

【问题讨论】:

  • 您也可以在 I/O 请求(读/写)返回 true 时调用 GetOverlappedResult。 msdn照常出错。无论如何,如果请求没有因为错误而失败 - 字节数将在OVERLAPPED.InternalHigh 中。 lpNumberOfBytesWritten 参数应该设置为 NULL - 这也是绝对错误的。如果 I/O 返回 true,您可以在此处传递非 0 并使用它的值

标签: windows winapi storage


【解决方案1】:

如果 hFile 是用FILE_FLAG_OVERLAPPED 打开的 lpNumberOfBytesWritten 参数应该设置为NULL

这不是真的(错误或谎言)。 lpNumberOfBytesWritten 可以设置为NULL,但不应该。如果 I/O 请求完成同步并在 *lpNumberOfBytesWritten 中成功将是有效的字节数。

还要注意lpNumberOfBytes 不得指向在操作完成之前有效的位置(如lpOverlapped) - 它可以指向本地变量例如函数,您可以在 I/O 完成之前退出函数 - 这也可以。系统只需将InternalHighOVERLAPPED 复制到*lpNumberOfBytes。所以在伪代码中:

if (lpNumberOfBytes) *lpNumberOfBytes = (ULONG)lpOverlapped->InternalHigh;

*lpNumberOfBytes 中的明显正确值只有在 I/O 已经成功完成时才会出现。所以并且只能在这种情况下使用它。系统不记得 lpNumberOfBytes 的值 - 因为它必须仅在 [Write|Read]File 调用期间有效,但在 I/O 活动期间无效(在异步 I/O 的情况下可能更长)

GetOverlappedResult 如果 I/O 请求完成且与成功同步(如果是 ReadFileWriteFile,如果返回 TRUE)或者如果挂起返回,我们可以调用。只有在 I/O 请求失败时才能调用此 API(ReadFileWriteFile 返回 FALSEGetLastError() != ERROR_IO_PENDING

所以最好总是将非 0 lpNumberOfBytes 传递给 api 并使用它,如果 api 成功完成。否则使用GetOverlappedResult 或者如果说你使用BindIoCompletionCallback - 你直接在回调中得到dwNumberOfBytesTransfered

所以在概念上可以使用下一个代码:

inline ULONG BOOL_TO_ERROR(BOOL f)
{
    return f ? NOERROR : GetLastError();
}

HANDLE hFile = CreateFile(*, FILE_GENERIC_READ, FILE_SHARE_READ, 0, 
    OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

if (hFile != INVALID_HANDLE_VALUE)
{
    UCHAR buf[0x200];
    OVERLAPPED ov = {};
    ULONG NumberOfBytesRead;

    ULONG dwError = BOOL_TO_ERROR(ReadFile(hFile, buf, sizeof(buf), &NumberOfBytesRead, &ov));

    switch (dwError)
    {
    case ERROR_IO_PENDING:
        dwError = BOOL_TO_ERROR(GetOverlappedResult(hFile, &ov, &NumberOfBytesRead, TRUE));
        if (dwError != NOERROR) goto __default;
        [[fallthrough]];

    case NOERROR:
        DbgPrint("NumberOfBytesRead=%x\n", NumberOfBytesRead);
        // use GetOverlappedResult(hFile, &ov, &NumberOfBytesRead, TRUE) here also possible
        break;
__default:
    default:
    DbgPrint("dwError = %u\n", dwError);
    }

    CloseHandle(hFile);
}

【讨论】:

  • @eryksun - 是的,当然OffsetOffsetHigh 必须明确设置,如果文件支持文件位置(文件系统)。因为管道偏移无关紧要。但我并不专注于此。问题是关于其他的
  • 如果 GetOverlappedResult 返回 FALSE,NumberOfBytesRead 是否有效。在发生错误或未定义值之前说部分读取?
  • @user3161924 - 如果 GetOverlappedResult 返回 FALSE - NumberOfBytesRead 无效。 NumberOfBytes(读/写)仅在操作成功完成后才有效。如果 io 失败 - NumberOfBytes 无效且实际值为 0
  • 不得指向一个位置......”我认为这很容易产生误导。更好的是,IMO 将是“不必指向某个位置……”。不能给人以它不能/不应该“指向某个位置……”的印象。
  • @nyholku - 是的,同意。我只是不懂英语
猜你喜欢
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 2023-02-02
  • 2015-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多