我正在编写一个Windows服务,用于与串行磁条读取器和中继板(访问控制系统)进行通信。
在另一个程序通过打开与我的服务相同的串口“中断”进程后,我遇到了代码停止工作的问题(我得到了IOExceptions)。
部分代码如下:
public partial class Service : ServiceBase
{
Thread threadDoorOpener;
public Service()
{
threadDoorOpener = new Thread(DoorOpener);
}
public void DoorOpener()
{
while (true)
{
SerialPort serialPort = new SerialPort();
Thread.Sleep(1000);
string[] ports = SerialPort.GetPortNames();
serialPort.PortName = "COM1";
serialPort.BaudRate = 9600;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
serialPort.Parity = Parity.None;
if (serialPort.IsOpen) serialPort.Close();
serialPort.Open();
serialPort.DtrEnable = true;
Thread.Sleep(1000);
serialPort.Close();
}
}
public void DoStart()
{
threadDoorOpener.Start();
}
public void DoStop()
{
threadDoorOpener.Abort();
}
protected override void OnStart(string[] args)
{
DoStart();
}
protected override void OnStop()
{
DoStop();
}
}
我的示例程序成功启动了工作线程,DTR的打开/关闭和升高使我的Mag-stripe读取器上电(等待1秒),关闭(等待1秒)等等。
如果我启动HyperTerminal并连接到同一个COM端口,HyperTerminal告诉我该端口当前正在使用中。如果我在HyperTerminal中反复按ENTER,尝试重新打开 几次重试后它将成功的端口。
这会导致我的工作线程中出现IOExceptions,这是预期的。但是,即使我关闭HyperTerminal,我的工作线程仍然会得到相同的IOException。唯一的办法就是重启计算机。
其他程序(不使用.NET库进行端口访问)似乎在这一点上正常工作。
关于导致这种情况的任何想法?
答案 0 :(得分:24)
@thomask
是的,Hyperterminal确实在SetCommState的DCB中启用了fAbortOnError,这解释了SerialPort对象抛出的大多数IOExceptions。有些PC /手持设备也有UART,默认情况下会打开错误标志中止 - 所以串口的init例程必须清除它(微软忽略了这一点)。我最近写了一篇长篇文章来更详细地解释这一点(如果你有兴趣的话,请参阅this。)
答案 1 :(得分:4)
您无法关闭某人与某个端口的连接,以下代码将无法使用:
if (serialPort.IsOpen) serialPort.Close();
因为您的对象没有打开端口,所以无法关闭它。
即使发生异常,您也应该关闭并处理串口
try
{
//do serial port stuff
}
finally
{
if(serialPort != null)
{
if(serialPort.IsOpen)
{
serialPort.Close();
}
serialPort.Dispose();
}
}
如果您希望该过程可以中断,那么您应该检查端口是否已打开然后退回一段时间然后再试一次,例如。
while(serialPort.IsOpen)
{
Thread.Sleep(200);
}
答案 2 :(得分:2)
您是否尝试在应用程序中打开端口,只打开/关闭DtrEnable,然后在应用程序关闭时关闭端口?即:
using (SerialPort serialPort = new SerialPort("COM1", 9600))
{
serialPort.Open();
while (true)
{
Thread.Sleep(1000);
serialPort.DtrEnable = true;
Thread.Sleep(1000);
serialPort.DtrEnable = false;
}
serialPort.Close();
}
我不熟悉DTR语义,所以我不知道这是否有用。
答案 3 :(得分:2)
不要使用阻塞方法,内部帮助程序类有一些微妙的错误。
将APM与会话状态类一起使用,其实例管理跨调用共享的缓冲区和缓冲区游标,以及在EndRead
中包装try...catch
的回调实现。在正常操作中,try
块应该执行的最后一件事是通过调用BeginRead()
来设置下一个重叠的I / O回调。
当出现问题时,catch
应异步调用重新启动方法的委托。回调实现应该在catch
块之后立即退出,以便重启逻辑可以销毁当前会话(会话状态几乎肯定已损坏)并创建新会话。重启方法必须不在会话状态类上实现,因为这样可以防止它破坏并重新创建会话。
当SerialPort对象关闭时(将在应用程序退出时发生),可能会有待处理的I / O操作。如果是这样,关闭SerialPort将触发回调,并且在这些条件下EndRead
将抛出一个与通用comms shitfit无法区分的异常。您应该在会话状态中设置一个标志,以禁止catch
块中的重新启动行为。这将阻止重启方法干扰自然关闭。
可以依赖此架构不要意外地保留SerialPort对象。
restart方法管理串口对象的关闭和重新打开。在Close()
对象上致电SerialPort
后,请致电Thread.Sleep(5)
,让它有机会放手。其他东西可以抓住端口,所以准备好在重新打开它时处理它。
答案 4 :(得分:1)
我尝试过改变这样的工作线程,结果完全相同。一旦HyperTerminal成功“捕获端口”(当我的线程正在休眠时),我的服务将无法再次打开端口。
public void DoorOpener()
{
while (true)
{
SerialPort serialPort = new SerialPort();
Thread.Sleep(1000);
serialPort.PortName = "COM1";
serialPort.BaudRate = 9600;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
serialPort.Parity = Parity.None;
try
{
serialPort.Open();
}
catch
{
}
if (serialPort.IsOpen)
{
serialPort.DtrEnable = true;
Thread.Sleep(1000);
serialPort.Close();
}
serialPort.Dispose();
}
}
答案 5 :(得分:1)
此代码似乎正常工作。我已经在我的本地机器上在控制台应用程序中测试了它,使用Procomm Plus来打开/关闭端口,程序一直在滴答作响。
using (SerialPort port = new SerialPort("COM1", 9600))
{
while (true)
{
Thread.Sleep(1000);
try
{
Console.Write("Open...");
port.Open();
port.DtrEnable = true;
Thread.Sleep(1000);
port.Close();
Console.WriteLine("Close");
}
catch
{
Console.WriteLine("Error opening serial port");
}
finally
{
if (port.IsOpen)
port.Close();
}
}
}
答案 6 :(得分:1)
我想我已经得出结论,超级终端不能很好地发挥作用。我进行了以下测试:
以“控制台模式”启动我的服务,它开始/关闭设备(我可以通过它的LED告诉)。
启动超级终端并连接到端口。 设备保持打开状态(超级终端提升DTR) 我的服务写入事件日志,它无法打开端口
停止超级终端,我使用任务管理器验证它已正确关闭
设备保持关闭状态(超级终端降低了DTR),我的应用程序继续写入事件日志,说它无法打开端口。
我启动第三个应用程序(我需要共存的应用程序),并告诉它连接到端口。我这样做了。这里没有错误。
我停止了上述申请。
VOILA,我的服务再次启动,端口成功打开,LED开启/关闭。
答案 7 :(得分:0)
这个答案很长时间才成为评论...
我相信当你的程序在Thread.Sleep(1000)中并打开超级终端连接时,HyperTerminal会控制串口。当您的程序唤醒并尝试打开串行端口时,将抛出IOException。
重新设计您的方法并尝试以不同的方式处理端口的打开。
编辑: 关于你必须在程序失败时重新启动计算机......
这可能是因为您的程序并未真正关闭,请打开您的任务管理器,看看您是否可以找到您的程序服务。在退出应用程序之前,请务必停止所有线程。
答案 8 :(得分:0)
是否有充分理由让您的服务“拥有”该端口?看看内置的UPS服务 - 一旦你告诉它有一个连接到COM1的UPS,你可以亲吻那个端口再见。我建议你这样做,除非有强大的操作要求来共享端口。