当 USB 模拟串行端口消失并重新出现时,我的应用程序如何恢复?
How can my application recover when a USB emulated serial port vanishes and reappears?
我正在与一个嵌入式设备通信,该设备会定期关闭其 USB 串行端口仿真,将 USB 端口用于其他用途,然后重新启动其串行端口仿真。在这段短暂的时间里,串行端口从 windows 设备管理器和注册表中消失,然后重新出现在这两个位置。
我的 .net 4.5 应用程序处理得很好,只要在它发生时我与串行端口断开连接:在串行端口仿真重新启动后,我可以像往常一样重新连接和通信。
但是,如果我连接到虚拟串行端口时设备断开并稍后重新启动其串行端口仿真,我的程序不会挂起但我无法访问重新启动的端口。
在这种情况下,windows 设备管理器正确显示端口消失和重新出现,而注册表显示端口消失但从未重新出现。当然,这意味着 SerialPort.GetPortNames() 随后找不到端口,当然,如果我试图关闭已经消失的端口,我会得到一个 IOException。
编辑:
我没有使用工作线程,而是使用 BaseStream.ReadAsync 并且我能够捕获 IOException 并处理原始端口对象。
正如我提到的,重新启动的模拟端口在设备管理器中显示得很好,但没有 显示在注册表中的 HKLM/Hardware/DeviceMap/SerialComm。
一旦我在 .net 中处理(甚至尝试关闭但失败)端口,如果我随后强制嵌入式设备重新启动,那么模拟端口将重新出现在注册表中,我可以重新连接到它。不幸的是,这需要与设备进行物理交互,这通常是不可能的。
我正在寻找以编程方式恢复的方法,因为我通常无法访问设备以强制重启。
我想在我处理我的串行端口对象后可能有一些方法可以让 Windows 刷新注册表,但我一直无法找到关于如何完成的任何线索.
我发现了很多与我相似的 SO 问题,但是 none 完全相同并且 none 为我的问题提供了答案。
Hans Passant 建议在 USB 电缆上放置一个标签,邀请人们将其拔下并插回几次,看看会发生什么。我喜欢这种想法,但在我的例子中,仿真是由嵌入式设备停止和启动的——由于硬件的设计,这种行为是必需的。
我试过盲目添加<legacyUnhandledExceptionPolicy enabled="1"/>
到我的 app.exe.config 文件,它似乎什么也没做。
根据对类似问题的回答,我尝试将对 Close() 的调用移至单独的线程,还尝试使用异步等待调用关闭。该方法因系统反射错误终止了我的应用程序。
如何让我的应用程序在端口消失并重新出现时处理它的连接(或以其他方式让开),以便正确刷新注册表以匹配设备管理器?
或者我有什么办法可以在不终止和重新启动应用程序的情况下从模拟串行端口的瞬间丢失中恢复过来?
框架 System.IO.Ports.SerialPort
class 当然不会让这一切变得简单。
您需要一些 p/invoke 或 C++/CLI 来获取断开连接和重新连接事件...RegisterDeviceNotification
函数是关键。
在那之后,你应该避免使用工作线程,因为 System.IO.Ports.SerialPort
会在设备消失或端口被处置时抛出异常,你将没有机会安装适当的异常处理程序在完成端口工作线程上。
如果您取消 DataReceived
事件并使用 port.BaseStream.ReadAsync
进行事件驱动的重叠接收,您可以在 ReadAsync
调用周围放置一个 try/catch 并且能够通过处理旧端口对象来恢复。作为额外的好处,传入的数据随后通过主线程上的同步上下文传送,因此您不必自己担心线程同步。
我正在与一个嵌入式设备通信,该设备会定期关闭其 USB 串行端口仿真,将 USB 端口用于其他用途,然后重新启动其串行端口仿真。在这段短暂的时间里,串行端口从 windows 设备管理器和注册表中消失,然后重新出现在这两个位置。
我的 .net 4.5 应用程序处理得很好,只要在它发生时我与串行端口断开连接:在串行端口仿真重新启动后,我可以像往常一样重新连接和通信。
但是,如果我连接到虚拟串行端口时设备断开并稍后重新启动其串行端口仿真,我的程序不会挂起但我无法访问重新启动的端口。
在这种情况下,windows 设备管理器正确显示端口消失和重新出现,而注册表显示端口消失但从未重新出现。当然,这意味着 SerialPort.GetPortNames() 随后找不到端口,当然,如果我试图关闭已经消失的端口,我会得到一个 IOException。
编辑: 我没有使用工作线程,而是使用 BaseStream.ReadAsync 并且我能够捕获 IOException 并处理原始端口对象。
正如我提到的,重新启动的模拟端口在设备管理器中显示得很好,但没有 显示在注册表中的 HKLM/Hardware/DeviceMap/SerialComm。
一旦我在 .net 中处理(甚至尝试关闭但失败)端口,如果我随后强制嵌入式设备重新启动,那么模拟端口将重新出现在注册表中,我可以重新连接到它。不幸的是,这需要与设备进行物理交互,这通常是不可能的。
我正在寻找以编程方式恢复的方法,因为我通常无法访问设备以强制重启。
我想在我处理我的串行端口对象后可能有一些方法可以让 Windows 刷新注册表,但我一直无法找到关于如何完成的任何线索.
我发现了很多与我相似的 SO 问题,但是 none 完全相同并且 none 为我的问题提供了答案。
Hans Passant 建议在 USB 电缆上放置一个标签,邀请人们将其拔下并插回几次,看看会发生什么。我喜欢这种想法,但在我的例子中,仿真是由嵌入式设备停止和启动的——由于硬件的设计,这种行为是必需的。
我试过盲目添加<legacyUnhandledExceptionPolicy enabled="1"/>
到我的 app.exe.config 文件,它似乎什么也没做。
根据对类似问题的回答,我尝试将对 Close() 的调用移至单独的线程,还尝试使用异步等待调用关闭。该方法因系统反射错误终止了我的应用程序。
如何让我的应用程序在端口消失并重新出现时处理它的连接(或以其他方式让开),以便正确刷新注册表以匹配设备管理器?
或者我有什么办法可以在不终止和重新启动应用程序的情况下从模拟串行端口的瞬间丢失中恢复过来?
框架 System.IO.Ports.SerialPort
class 当然不会让这一切变得简单。
您需要一些 p/invoke 或 C++/CLI 来获取断开连接和重新连接事件...RegisterDeviceNotification
函数是关键。
在那之后,你应该避免使用工作线程,因为 System.IO.Ports.SerialPort
会在设备消失或端口被处置时抛出异常,你将没有机会安装适当的异常处理程序在完成端口工作线程上。
如果您取消 DataReceived
事件并使用 port.BaseStream.ReadAsync
进行事件驱动的重叠接收,您可以在 ReadAsync
调用周围放置一个 try/catch 并且能够通过处理旧端口对象来恢复。作为额外的好处,传入的数据随后通过主线程上的同步上下文传送,因此您不必自己担心线程同步。