我通过事件打开SerialPort
并接收数据。DataReceived
有什么方法可以检测是否SerialPort
断开连接?
我尝试了ErrorReceived
和PinChanged
事件,但没有运气。
除此之外,SerialPort.IsOpen
在物理断开连接时返回 true。
USB 串行端口是一个巨大的痛苦。例如,参见这个问题。我不确定它是否真的被 .NET 4.0 修复了,但在那天我试图处理断开连接的问题,使整个程序崩溃,如下所示:
public class SafeSerialPort : SerialPort
{
private Stream theBaseStream;
public SafeSerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
: base(portName, baudRate, parity, dataBits, stopBits)
{
}
public new void Open()
{
try
{
base.Open();
theBaseStream = BaseStream;
GC.SuppressFinalize(BaseStream);
}
catch
{
}
}
public new void Dispose()
{
Dispose(true);
}
protected override void Dispose(bool disposing)
{
if (disposing && (base.Container != null))
{
base.Container.Dispose();
}
try
{
if (theBaseStream.CanRead)
{
theBaseStream.Close();
GC.ReRegisterForFinalize(theBaseStream);
}
}
catch
{
// ignore exception - bug with USB - serial adapters.
}
base.Dispose(disposing);
}
}
向我改编这个的人道歉,似乎我没有在我的代码中记下它。问题显然源于.NET 在串行端口消失的情况下如何处理底层流。断开串行端口后,您似乎无法关闭流。
我使用的另一种策略是创建一个只执行串行通信部分的小程序,并公开一个 WCF 服务供我的主程序连接。这样,当 USB 串行适配器脱落并使通信程序崩溃时,我可以从我的主程序中自动重新启动它。
最后,我不知道为什么没有人销售锁定 USB 端口来避免整个意外断开问题,尤其是使用 USB 串行适配器!
问题是该值仅在方法执行时IsOpen
被设置回。false
Close
您可以尝试捕获WM_DEVICECHANGE
消息并使用它。
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspx
一旦 USB 串行适配器断开连接,我还面临无法处理的异常问题(并且无法检测/规避代码中的问题)。我可以确认 Mattt Burland 的解决方案有效。
简化版:
class SafeSerialPort : SerialPort {
public new void Open() {
if (!base.IsOpen) {
base.Open();
GC.SuppressFinalize(this.BaseStream);
}
}
public new void Close() {
if (base.IsOpen) {
GC.ReRegisterForFinalize(this.BaseStream);
base.Close();
}
}
protected override void Dispose(bool disposing) {
try {
base.Dispose(disposing);
} catch (Exception ex) {
Debug.WriteLine(ex.Message);
}
}
}
如果您要连接的设备使用 CD 引脚,您可以观察它的变化(其他引脚可能适用于某些使用流量控制的设备)。如果没有,那么就没有明确的方法来做到这一点。
根据连接设备的预期行为,您可能希望实现超时或某种保持活动状态。
为那些仍然面临这个问题的人更新;几点...
SerialPort.GetPortNames()
yank,但 4.7 不会更新开放端口的列表。我们有一个后台线程来检查这个列表是否有变化(如上所述),它曾经工作得很好,但它不再工作了......SerialPort.IsOpen()
,在 4.7 中,当用户拉出设备时,它会立即报告设备关闭。我们确实会在 yank 上立即看到该消息,但对于我们的应用程序来说,在后台线程中WM_DEVICECHANGE
进行测试更容易。SerialPort.IsOpen()
使用 C# SerialPort.CDHolding 标志。
因此,在打开和读取循环之前,将布尔值保存到 lastCDHolding 标志。
在您阅读 test SerialPort.CDHolding != lastCDHolding 以捕获更改或仅测试 SerialPort.CDHoldin==false 以检测端口已关闭之后。
我已经通过 Jeb 的代码编辑得到了这个解决方案。在这种情况下,我总是有一个后台计时器(但您也可以在连接 Comport 时启动它)当我连接 Comport 时,我将布尔“canDisconnect”设置为 true,这就是我使工作始终完美的方式。
bool canDisconnect = false;
private void tmrBackgroundTick_Tick(object sender, EventArgs e)
{
if (canDisconnect == true ){
//Automatic disconnect program if Comport is removed
try
{
ComPort.WriteLine("test");
}
catch (Exception ex)
{
disconnect();
canDisconnect = false;
}
}
}
我在使用 RFID 设备时遇到了同样的问题。
可以通过发送虚拟数据来检测 USB 移除。
通常我只需要从 RFID 接收数据,但(在我的情况下)每秒发送一些数据并没有什么坏处。
当 USB 电缆断开时,serialPort.Write 会引发异常。
然后必须立即关闭端口,否则程序会挂起。
bool verify_connection()
{
try
{
if (serialport.isOpen)
{
serialport.WriteLine("Dummy");
}
}
catch (Exception ex)
{
serialport.Close();
}
return serialport.isOpen;
}
使用 .NET Core 3.1 测试
我用一个简单的后台工作者解决了这个问题。我不断检查/将所有当前端口设置到一个阵列中,如果新端口与我的端口阵列不同,我会从这两个阵列中获取更改后的端口。
例子:
// global variable
List<string> oldPorts = new List<string>(); // contains all connected serial ports
private void bgw_checkSerialPorts_DoWork(object seder, DoWorkEventArgs e)
{
while (true)
{
string[] currentPorts = SerialPort.GetPortNames(); // get all connected serial ports
string[] diffPorts = currentPorts.Except(oldPorts.ToArray()); // get the differences between currentPorts[] and oldPorts-list
if (diffPorts.Length > 0)
{
// iterate all changed ports
for (int i = 0; i < diff.Length; i++)
{
// check if changed port was in old list
if (oldPorts.Contains(diff[i]))
{
// port in diff[] was removed
}
else
{
// port in diff[] is a new device
}
}
}
oldPorts = currentPorts.ToList(); // update oldPortlist
// check every 100ms
Thread.Sleep(100);
}
}
我面临着同样的问题。经过几次尝试,我得到了我想要的东西SerialPort.DsrHolding
。
一旦 USB 电缆被拉出,它就会返回 false。我不知道这可能会失败的其他情况。但这对我有用。