我想对串行端口上的异步I / O使用 ReadFileEx / WriteFileEx 函数,我不需要 lpCompletionRoutine 参数APC。
1- lpCompletionRoutine参数是否可以设置为 NULL ?
2-我可以使用 GetOverlappedResult 并将 bWait 参数设置为 TRUE 来阻止,直到 ReadFileEx / WriteFileEx 完成而不是使用 WaitForSingleObjectEx ,因为我已经使用 SetCommTimeouts 设置了通讯超时!!! ???
感谢您的理解。
答案 0 :(得分:2)
正式答案:
lpCompletionRoutine参数是否可以设置为NULL?
否即可。此参数是必需的,不能为NULL。如果您在成功通话ReadFileEx
后等待警报状态,则会调用 lpCompletionRoutine 。所以如果你传递NULL - 这个地址将被调用。但是,如果您永远不会在警报状态下等待,那么您可以抓住这个错误。但是如果你要添加这种代码,或者系统本身将在这种状态下间接等待(比如你在新的Windows版本中调用一些api或一些更改) - 你突然崩溃了 - 离调用ReadFileEx
很远绝对不相关的代码 - 并且将长期而艰苦地研究为什么会发生这种情况。
我可以使用GetOverlappedResult并将bWait参数设置为TRUE 阻止,直到ReadFileEx / WriteFileEx完成而不是使用 WaitForSingleObjectEx
是的,您可以执行此操作(如果来自OVERLAPPED
== 0
的 hEvent )。 GetOverlappedResult
如果操作仍未完成,则在您致电GetOverlappedResult
时(通过Internal
成员OVERLAPPED
与STATUS_PENDING
进行比较来确定) - 来自OVERLAPPED
的 hEvent 的函数调用WaitForSingleObject[Ex]
如果它不为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
的{{1}}次传递OVERLAPPED
( ZwReadFile
)但Event [in, optional]
始终传递0
ReadFile[Ex]
至Event
ZwReadFile
- 显然ApcRoutine [in, optional]
在这里传递0,但是ReadFile
在这里传递了一些内部例程
ReadFileEx
(总是,即使你的
lpCompletionRoutine 为0)。此例程将 BaseIoCompletion[Simply]
代码从PIO_STATUS_BLOCK
(第二个参数)转换为win32错误(通过
致电RtlNtStatusToDosError
)。而不是打电话给你
lpCompletionRoutine (它取自NTSTATUS
- 第一个参数)。如果它为0 - 那么0并将被调用。众所周知
结果BindIoCompletionCallback
,CreateThreadpoolIo
,
CreateIoCompletionPort
或通过设置
FileCompletionInformation
- 您无法使用ApcContext
(不是0) - 这是I / O完成的互斥方法。和
你从i / o子系统获得了ApcRoutine
STATUS_INVALID_PARAMETER
- 指向您的ApcContext [in, optional]
传递指针
lpOverlapped 在这里。因此你可以把它(指针)放回FileIOCompletionRoutine
或IoCompletionCallback
,或者
直接致电ReadFile
ZwRemoveIoCompletion
将 lpCompletionRoutine 传递给您。从那以后
ReadFileEx
存根将其作为 ApcContext 返回
用来调用原来的 lpCompletionRoutine (即使它是0)
BaseIoCompletion[Simply]
IoStatusBlock
此处将指针传递给 lpOverlapped 无条件。如果不是0,则ReadFileEx
传递 lpOverlapped 。否则将其分配到堆栈中,作为局部变量
并使用此指针。由此得出ReadFile
在这种情况下
( lpOverlapped == 0 )在操作未完成之前无法返回 -
因为IO_STATUS_BLOCK
必须有效,而i / o不完整,
但是函数返回后局部变量无效。ReadFile
和 Buffer [out]
的内容显然是Length [in]
- 如果 lpOverlapped 不是0,则分配了本地变量ByteOffset [in, optional]
(好吧,当然
声明(如此分配)总是,我只是说它使用
case)并从 Offset 和 OffsetHigh 成员初始化
重叠。如果 lpOverlapped 为0(如果LARGE_INTEGER ByteOffset
)则为NULL
指针传递为 ReadFile
重要提示。如果文件以异步模式打开(使用
如果我们使用CreateFile
或使用ByteOffset
,则会FILE_FLAG_OVERLAPPED
标记
WSA_FLAG_OVERLAPPED
如果我们使用WSASocket
(或
socket
)或者我们不使用 FILE_SYNCHRONOUS_IO_NONALERT
ZwCreateFile
或ZwOpenFile
中的FILE_SYNCHRONOUS_IO_ALERT
个标记
ZwLockFile
) ByteOffset
是强制性参数
(命名管道和邮件槽文件类型除外)。如果它是0 - i / o
子系统返回STATUS_INVALID_PARAMETER
。结果
lpOverlapped 参数不能为NULL - 因为(7)。这在ReadFile
:
如果使用FILE_FLAG_OVERLAPPED打开hFile,则lpOverlapped 参数不能为NULL。它必须指向有效的OVERLAPPED 结构
Key [in, optional]
- 始终为0,我们在调用ReadFile[Ex]
时无法控制此项。如果需要,请查看LockFileEx
密钥参数
理解这个参数。 win32 shell NT_ERROR(status)
是
限制。在调用ZwReadFile
并返回 NTSTATUS状态后 -
ReadFileEx
检查NT_WARNING(status)
是否属实
转换并设置win32错误并返回 FALSE 否则返回
TRUE 。所以说STATUS_PENDING
这个函数返回TRUE
。更有意思的是NT_SUCCESS(status)
(这是真的
错误状态)它也返回TRUE
。但是我从来没有看过案例
ZwReadFile
返回此范围的状态,但是一些自定义驱动程序
当然可以做到这一点 ReadFile
必须具有STATUS_PENDING
的特殊情况
lpOverlapped 是0如何在(5)中解释。所以它调用ZwWaitForSingleObject(hFile..)
等待操作完成
而不是使用IO_STATUS_BLOCK
如果 lpOverlapped 不是NULL - ReadFile
不等待自己。它
如果不是SetCommTimeouts
,则将状态转换为win32错误
返回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
- 没有任何问题 - 我们在STATUS_TIMEOUT
中获得了IO_STATUS_BLOCK
。并且将确切地知道超时是。
这是什么结论?主读操作将在内核模式下,在驱动程序代码中。它是同步还是同步 in driver - 我们几乎无法控制。司机可以忽略FO_SYNCHRONOUS_IO
中的FILE_OBJECT
标记。但是,即使您以同步模式打开文件,大多数驱动程序也会异步处理I / O操作(包括读取)。并返回STATUS_PENDING
。 i / o子系统已检查此特殊状态,等待如果挂起,则返回和以同步模式打开文件。以及ZwReadFile
内部的所有这一切。在内核中。因此,ReadFile
和ReadFileEx
之间没有区别。只有 ZwReadFile
之后返回 - ReadFileEx
只会返回给您。当ReadFile
等待返回并且 lpOverlapped 为0.但是我们通过 lpOverlapped 完全控制此行为时 - 将其设置为指向有效OVERLAPPED结构的指针 - 而ReadFile
永远不会等待。
所以读取的同步或异步行为不是由select ReadFile
或ReadFileEx
确定,而是在哪种模式(使用哪些标志)打开文件。 (驱动程序我怎么说有时可以忽略异步句柄并同步处理i / o)。在ReadFile
的情况下通过 lpOverlapped (0或不是0)(但这里完全确定了)
调用IOCTL_SERIAL_SET_TIMEOUTS
或{{3}}(SetCommTimeouts
只是将此ioctl发送给驱动程序)对文件有效。并且在此之后绝对没有区别 - 使用你ReadFile
或ReadFileEx
- 在超时的情况下都会给你相同的结果。
和last - 如果你想在挂起返回后等待操作完成 - 这实际上就是同步I / O.在这种情况下,你不需要在异步模式下打开文件 - 这对于这种代码来说是毫无意义的。只需打开同步并使用ReadFile
答案 1 :(得分:1)
在调用ReadFileEx
时,您必须提供有效的完成例程,因为如果您提供的地址无效,您的程序将在处理完APC后立即崩溃。 (或者,如果您不处理APC,则存在太多排队的I / O完成可能最终会导致问题。除了其他任何内容之外,在调用APC之前,您无法合法地重用I / O缓冲区。 )
在任何情况下,完成例程是告诉I / O完成的唯一方法。如文档中所述,ReadFileEx
函数忽略OVERLAPPED
结构的hEvent
成员。当然,您总是可以将事件对象放在hEvent
中,并将ReadFileEx
指向一个除了设置事件之外什么都不做的完成例程。
然而,您的原始前提(使用ReadFileEx
将避免同步读取的可能性)不正确。确实,在某些情况下,Windows将同步完成名义上的异步I / O操作,但这是由于I / O驱动程序的限制并影响ReadFile
和 {{1以完全相同的方式。
我不清楚你在评论中所说的实际情况是什么,或者你认为是由于意外同步I / O而遇到的问题。串行端口驱动程序可能比大多数人更可能表现出这种行为,因此这可能是导致问题的原因,但您需要找到另一种解决方案。例如,您可以使用ReadFileEx
来检测输入是否可用,并设置超时以使I / O无阻塞,这将使I / O语义与Linux提供的语义类似。
如果您不确定如何继续,我建议您提出另一个问题,包括您要实现的目标以及究竟出现了什么问题的详细信息。