如果 hFile 是用FILE_FLAG_OVERLAPPED 打开的
lpNumberOfBytesWritten 参数应该设置为NULL。
这不是真的(错误或谎言)。 lpNumberOfBytesWritten 可以设置为NULL,但不应该。如果 I/O 请求完成同步并在 *lpNumberOfBytesWritten 中成功将是有效的字节数。
还要注意lpNumberOfBytes 不得指向在操作完成之前有效的位置(如lpOverlapped) - 它可以指向本地变量例如函数,您可以在 I/O 完成之前退出函数 - 这也可以。系统只需将InternalHigh 从OVERLAPPED 复制到*lpNumberOfBytes。所以在伪代码中:
if (lpNumberOfBytes) *lpNumberOfBytes = (ULONG)lpOverlapped->InternalHigh;
*lpNumberOfBytes 中的明显正确值只有在 I/O 已经成功完成时才会出现。所以并且只能在这种情况下使用它。系统不记得 lpNumberOfBytes 的值 - 因为它必须仅在 [Write|Read]File 调用期间有效,但在 I/O 活动期间无效(在异步 I/O 的情况下可能更长)
GetOverlappedResult 如果 I/O 请求完成且与成功同步(如果是 ReadFile 或 WriteFile,如果返回 TRUE)或者如果挂起返回,我们可以调用。只有在 I/O 请求失败时才能调用此 API(ReadFile 或 WriteFile 返回 FALSE 和 GetLastError() != 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);
}