【问题标题】:CloseHandle() returns before the serial port is actually closedCloseHandle() 在串口实际关闭之前返回
【发布时间】:2012-02-11 17:35:23
【问题描述】:

我正在努力弄清楚串行端口何时完成关闭,以便我可以重新打开它。原来CloseHandle()在端口真正解锁之前就返回了。

我正在使用CreateFile(FILE_FLAG_OVERLAPPED) 打开一个串行端口,使用CreateIoCompletionPort() 将其与CompletionPort 相关联,使用ReadFile()WriteFile() 对其进行读/写,并使用CloseHandle() 将其关闭。

我注意到,如果我足够快地关闭并重新打开一个串行端口,我会从CreateFile() 收到一个ERROR_ACCESS_DENIED。尽管我正在等待CloseHandle() 返回,然后等待与该句柄关联的所有未完成的读/写操作从完成端口返回,但这种情况仍在发生。当然有更好的方法:)

如何同步关闭串行端口?请不要重试循环、sleep() 或其他一些廉价的黑客攻击。

编辑:也许这与我使用完成端口和 FILE_FLAG_OVERLAPPED 有关。当读/写操作完成时,我会收到一个回调。端口关闭是否有某种回调?

【问题讨论】:

  • 可能和你具体的串口驱动有关。你在 SO 上看到过这个吗:stackoverflow.com/questions/2948428/…
  • 只是为了确定......你没有在任何地方使用过DuplicateHandle
  • 我不确定完成端口是否相关,我一直使用带有ReadFileExWriteFileEx 的APC 回调。这也更简单,因为 APC 仅在线程进入警报等待时才运行,因此没有跨线程同步问题(只是要小心重入)。
  • 我怀疑这是由不完整的 I/O 请求引起的。但是从根本上解决问题,关闭串口并重新打开它是没有意义的。
  • 实际上,这不是串行端口所独有的,我不认为这是你做错了什么。我在文件句柄上看到了同样的问题。 CloseHandle 返回的时间与实际释放的对象之间存在明显的滞后。我怀疑最终你会发现一些涉及重试循环和睡眠的廉价黑客将是唯一的解决方案。

标签: windows winapi serial-port iocp io-completion-ports


【解决方案1】:

当您在线程中打开 COM 端口时,操作系统会打开另一个隐藏线程来执行 I/O。我也在尝试解决关闭端口的问题。据我所知,一种可行的方法是:1)暂停打开 COM 端口的线程,2)PurgeComm() 停止所有 I/O 并清空所有读/写 I/O 缓冲区,3)然后尝试关闭 COM 端口。可能需要一个 waitforsingleobject 来确定具有 COM 端口的线程何时实际挂起。除非 COM 端口绝对需要关闭,否则我正在考虑让 COM 端口保持打开状态并让 WinProc 处理 IDM_Exit 命令以关闭 COM 端口、线程和应用程序。不是我想要的,而是我可以接受的选择。我正在使用 USB 到串行端口连接,但不确定这会给我带来什么问题。我知道如果您使用的是 USB 转串口接口,您需要将引脚 20 设置为高电平 (DTR)。引脚 20 有助于为接口供电,但仅提供大约 30ma 左右。如果这一切正常,我将对这篇文章进行更新,并让你知道我是否能够关闭 COM 端口。 Windows 把串行端口驱动程序弄得一团糟,似乎满足于让事情变得混乱!

【讨论】:

    【解决方案2】:

    在您发出 CloseHandle() 请求(可能在其他线程上)之后,确保您的读取器和写入器失败(句柄无效),然后再尝试再次触摸设备。在尝试发出另一个 CreateFile 之前,您可以使用它作为清理所有 io 处理的提示。我必须说完成端口确实看起来毫无必要的巴洛克风格。

    【讨论】:

    • 不错的技巧,但不可靠。因为同一个句柄可以(理论上)重复使用。
    • 另外,您可能没有任何未完成的读取/写入,所以现在您还必须跟踪它。
    【解决方案3】:

    我认为问题出在服务于 COM 端口的驱动程序上。因此 - 将没有 API 来“实际关闭”COM 端口。

    顺便说一句,关闭文件句柄后,无需等待所有未完成的 I/O 完成并出现错误。在CloseHandle 返回时,所有未完成的 I/O 都已经完成/取消,您只需异步接收回调(无论是通过完成端口还是 APC 队列)。

    众所周知,FTDI 驱动程序(模拟 COM->USB 的驱动程序)非常容易出错。

    我可能只建议在关闭句柄之前尝试刷新数据。您可以等待所有 I/O 完成关闭 COM 端口(如果这适用于您的情况)。或者,您可以致电SetCommMaskWaitCommEvent 以确保没有待发送的数据。希望这可能会有所帮助。

    编辑:

    CloseHandle 是否立即(在它返回之前)取消文件句柄上的所有未决 I/O?

    严格来说 - 没有

    可能还有其他对文件对象的引用。例如,用户模式代码可能调用DuplicateHandle,或者内核模式驱动程序可能调用ObReferenceObjectByXXXX。在这种情况下,句柄引用的对象不一定被释放。

    当最后一个句柄关闭时,驱动程序的DispatchCleanup 被调用。它必须根据this 取消所有未完成的 I/O。

    但是,当一个线程在 CloseHandle 中时 - 从理论上讲,您可以从另一个线程发出另一个 I/O(如果幸运的话 - 句柄仍然有效)。当您在调用 I/O 函数(例如 WriteFile 等)时,操作系统会暂时增加对象的引用计数器。这反过来可能会启动另一个 I/O,它不会被 CloseHandle 调用直接取消。

    但是在这种情况下,在发出新的 I/O 后,O/S 将立即关闭句柄,因为对对象的引用计数再次达到 0。

    因此,在这种变态的情况下,可能会发生这样一种情况,即在调用 CloseHandle 之后,您将无法再次重新打开文件。

    【讨论】:

    • 我正在使用主板上的串行端口(使用内置的 Microsoft 驱动程序)。没有 COM 到 USB 适配器在起作用。刷新数据的问题是,如果启用硬件流控制,它可能会永远阻塞。理想情况下,我想中止所有未完成的命令,关闭端口并在关闭真正完成后从我的函数返回。如果没有用于确保关闭的 API,那么这一切都是有争议的。 PS:你怎么知道CloseHandle()在返回之前取消了所有未完成的I/O?我在规范中没有看到这样的保证。
    • 请看我的编辑。您也可以尝试使用CancelIoEx。而且,当然,不要在另一个线程上做任何 I/O maenwhile(我希望你不要这样做)
    • 好的,我在尝试打开端口时最终使用了重试循环。感谢您提供参考信息。
    猜你喜欢
    • 1970-01-01
    • 2021-10-25
    • 1970-01-01
    • 2020-08-15
    • 2015-04-15
    • 2022-10-14
    • 1970-01-01
    • 2016-12-07
    • 2020-11-13
    相关资源
    最近更新 更多