正式的答案:
可以将 lpCompletionRoutine 参数设置为 NULL 吗?
没有。该参数是强制性的,不能为NULL。 lpCompletionRoutine 将在成功调用ReadFileEx 后在alertable 状态等待时调用。因此,如果您传递 NULL - 此地址将被调用。但是,如果您永远不会在 alertable 状态下等待,那么您可以并且不会捕获此错误。但是如果你不添加这种代码,或者系统本身将间接地等待这种状态(比如你调用了一些 api 或者在新的 Windows 版本中进行了一些更改) - 你突然崩溃了 - 与绝对无关的调用 ReadFileEx 相去甚远代码 - 并且将进行长期而艰苦的研究为什么会发生这种情况。
我可以使用 GetOverlappedResult 并将 bWait 参数设置为 TRUE
阻塞直到 ReadFileEx/WriteFileEx 完成而不是使用
WaitForSingleObjectEx
是的,你可以这样做(如果 hEvent 来自OVERLAPPED== 0)。 GetOverlappedResult 如果操作仍未完成,当您调用 GetOverlappedResult 时(它通过将 OVERLAPPED 的成员 Internal 与 STATUS_PENDING 进行比较来确定这一点) - 函数调用 WaitForSingleObject[Ex] on hEvent 来自OVERLAPPED 如果它不为 NULL 否则它将在 hFile 上等待。
但来自ReadFileEx:
ReadFileEx 函数忽略 OVERLAPPED 结构的 hEvent
会员。
这意味着它不会将它传递给ZwReadFile(作为第二个参数Event),并且内核作为结果在 i/o 完成时不会设置此事件。但是ReadFileEx 忽略 hEvent 成员,GetOverlappedResult 不会忽略它并使用它,如果它不为 NULL。所以它必须是NULL。在这种情况下,GetOverlappedResult 将与您的 hFile 一起工作(等待),一切都会好起来的(操作完成时,i/o 子系统在FILE_OBJECT 中设置内部事件)
ReadFileEx 和 ReadFile 有什么不同?这两个函数都是ZwReadFile 上的薄壳 - 你可以从用户那里直接调用 - 它更强大。你怎么能看到ZwReadFile有更多参数比较ReadFile[Ex]。这个参数是如何从ReadFile[Ex] 传递到ZwReadFile 的?
-
ReadFile 将 hEvent 从 OVERLAPPED 传递到 ZwReadFile
(Event [in, optional] ) 但ReadFile[Ex] 总是在适当的位置传递 0
Event 到ZwReadFile
-
ApcRoutine [in, optional] - 显然 ReadFile 在这里传递了 0,但 ReadFileEx 在这里传递了一些内部例程
BaseIoCompletion[Simply](总是,即使你的
lpCompletionRoutine 为 0)。此例程将 NTSTATUS 代码从 PIO_STATUS_BLOCK(第二个参数)转换为 win32 错误(通过
致电RtlNtStatusToDosError)。而不是打电话给你
lpCompletionRoutine(它来自ApcContext - 第一个参数)。如果它是 0 - 那么 0 并且将被调用。与众所周知的
结果。
- 重要提示 - 如果 IOCP 端口绑定到您的文件(通过调用
BindIoCompletionCallback, CreateThreadpoolIo,
CreateIoCompletionPort 或通过设置
FileCompletionInformation - 你不能使用ApcRoutine
(非 0)- 这是 I/O 完成的互斥方法。和
你从 i/o 子系统得到STATUS_INVALID_PARAMETER
-
ApcContext [in, optional] - ReadFile 将指针传递给您的
lpOverlapped 在这里。因此您可以在FileIOCompletionRoutine 或IoCompletionCallback 中取回它(指针),或者
直接致电ZwRemoveIoCompletion
ReadFileEx 在此处传递您的 lpCompletionRoutine。从而
BaseIoCompletion[Simply] stub 把它作为 ApcContext 和
用来调用你原来的lpCompletionRoutine(即使它是0)
-
IoStatusBlock ReadFileEx 这里将指针传递给您的lpOverlapped 无条件。 ReadFile 传递你的 lpOverlapped 如果它不是 0。否则它在堆栈中分配它,作为局部变量
并使用这个指针。由此得出ReadFile 在这种情况下
(lpOverlapped==0) 直到操作未完成才能返回 -
因为IO_STATUS_BLOCK 在 i/o 未完成时必须有效,
但函数返回后局部变量无效。
- 传递给
Buffer [out]和Length [in]的东西很明显
-
ByteOffset [in, optional] - 如果 lpOverlapped 不为 0,则分配局部变量 LARGE_INTEGER ByteOffset(好吧,当然
总是声明(如此分配),只是我的意思是它在此使用
case) 并从 Offset 和 OffsetHigh 成员初始化
重叠。如果 lpOverlapped 为 0(如果是 ReadFile)则为 NULL
指针传递为 ByteOffset
-
重要提示。如果文件以异步模式打开(使用
FILE_FLAG_OVERLAPPED 标志如果我们使用 CreateFile 或
WSA_FLAG_OVERLAPPED 如果我们使用 WSASocket( 或
socket) 或者如果我们不使用 FILE_SYNCHRONOUS_IO_NONALERT
和FILE_SYNCHRONOUS_IO_ALERT ZwCreateFile 中的标志或
ZwOpenFile ) ByteOffset 是 强制 参数
(命名管道和邮槽文件类型除外)。如果它将是 0 - i/o
子系统返回为STATUS_INVALID_PARAMETER。结果
lpOverlapped 参数不能为 NULL - 因为 (7)。这是ReadFile的文档中所说的:
如果使用 FILE_FLAG_OVERLAPPED 打开 hFile,则 lpOverlapped
参数不能为 NULL。它必须指向一个有效的 OVERLAPPED
结构。
-
Key [in, optional] - 始终为 0,当调用 ReadFile[Ex] 时我们无法控制它。如果需要,请查看 ZwLockFile Key 参数
了解这个参数。 win32 外壳LockFileEx 是
受限。
ZwReadFile 被调用并返回后NTSTATUS 状态 -
-
ReadFileEx 检查 NT_ERROR(status) 如果这是真的
转换并设置 win32 错误并返回 FALSE 否则返回
正确。所以说STATUS_PENDING这个函数返回TRUE。更有趣的是NT_WARNING(status)(这真的是
错误状态)它也返回TRUE。但是我从不查看案例
ZwReadFile 返回这个范围的状态,但是一些自定义的驱动
当然可以这样做
-
ReadFile 必须有 STATUS_PENDING 的特殊情况,当
lpOverlapped 是 0 我在 (5) 中的解释。所以它调用ZwWaitForSingleObject(hFile..) 等待操作完成
而不是使用来自IO_STATUS_BLOCK的状态
如果 lpOverlapped 不是 NULL - ReadFile 不等待自己。它
如果不是NT_SUCCESS(status) 和
返回FALSE。它还检查STATUS_PENDING 并返回
FALSE 在这种情况下也是(并将最后一个错误设置为 ERROR_IO_PENDING
)。否则返回TRUE。错误状态存在特殊情况 - STATUS_END_OF_FILE:
当同步读取操作到达文件末尾时,ReadFile
返回 TRUE 并将 *lpNumberOfBytesRead 设置为零。
但为什么会出现这种特殊情况?!为什么不将其转换为ERROR_HANDLE_EOF 并返回FALSE?
注意,如果是串行文件句柄,当我们调用SetCommTimeouts - 串行驱动程序时,如果超时过期 - 取消读取操作并返回STATUS_TIMEOUT 作为最终状态。但这不是错误状态。结果ReadFile 和ReadFileEx失去了这个状态。它返回TRUE 并且没有将ERROR_TIMEOUT 设置为最后一个错误,或者没有使用此代码调用您的FileIOCompletionRoutine。但使用NOERROR 代码。因此,如果使用 win32 api,将无法确定您的读取操作是否因超时而结束。需要读取检查字节。但是,如果使用 ZwReadFile - 没有任何问题 - 我们在 IO_STATUS_BLOCK 中得到了 STATUS_TIMEOUT。并且会确切地知道超时是什么。
由此得出的结论是什么?主要读取操作将在内核模式下,在驱动程序代码中。它是同步的还是同步的在驱动程序中 - 我们几乎无法控制。驱动程序可以忽略FILE_OBJECT 中的FO_SYNCHRONOUS_IO 标志。然而,大多数驱动程序异步处理 i/o 操作(包括读取),即使您以同步模式打开文件也是如此。并返回STATUS_PENDING。 i/o 子系统已经检查了这个特殊状态并等待如果挂起返回并且你以同步模式打开文件。所有这一切都在ZwReadFile 电话中。在内核中。所以此时ReadFile 和ReadFileEx 之间没有区别。不同的只是之后 ZwReadFile return - ReadFileEx 只是返回给你。当 ReadFile 将等待如果等待返回并且 lpOverlapped 为 0。但我们通过 lpOverlapped 完全控制此行为 - 将其设置为指向有效 OVERLAPPED 结构的指针 - 和 @ 987654474@ 永远不会等待。
所以读取的同步或异步行为不是由选择ReadFile 或ReadFileEx 决定的,而是您以哪种模式(使用哪些标志)打开文件。 (我所说的驱动程序有时可以忽略异步句柄并同步处理 i/o)。并由 lpOverlapped (0 或非 0)以防 ReadFile(但这里都是完全确定的)
调用SetCommTimeouts 或IOCTL_SERIAL_SET_TIMEOUTS(SetCommTimeouts 只需将此ioctl 发送给驱动程序)对文件有效。在这之后绝对没有什么不同 - 使用你 ReadFile 或 ReadFileEx - 在超时的情况下都会给你相同的结果。
最后 - 如果您只想在等待返回后等待操作完成 - 这实际上是同步 I/O。在这种情况下,您不需要以异步模式打开文件 - 这种代码毫无意义。只需同步打开并使用ReadFile