【问题标题】:C# Handling a Serial Port when program is closingC#在程序关闭时处理串行端口
【发布时间】:2015-02-09 14:26:06
【问题描述】:

我正在用 C# 构建一个使用 Excel 运行的应用程序,最终游戏是允许用户单击 Excel 工作表上的按钮,然后通过串行端口发送一些命令,然后读回数据。

这已经完成并且工作正常。

我的问题是,我如何检查以确保在应用程序关闭之前没有在 serialport_DataReceived; 事件上运行任何事件/函数,就好像它们那样我会遇到异常崩溃 -

The I/O operation has been aborted because of either a thread exit or an application request.

所以,在某种程度上,我需要进行某种清理,说明串行端口仍在收集数据,等到完成后再关闭。

这是我目前的代码;

     while (!dataCollected)
     {
        if (data.Contains(carrier)) // If the read data doesn't contain end carriage return, then retry until it does
        {
             if (data.Contains(resultMessage)) // Check to make sure it has the result message - this guarantees a fully built read data with no missing bytes 
             {
                 ParseIncomingDataToExcel(data); // put the string into a specfic excel row
                 dataCollected = true; // exits out of the while loop
             }
        }
        else // Carriage not found, data loss - try again
        {
            data = wrench.ReadLine();
        }
}


if (dataCollected)
    SetControlPropertyValue(button, "Enabled", true); // Invoke to turn the button enabled to true

所以基本上,你通过串口发送一个命令,然后得到一个输出。如果你在这个过程中尝试退出,那么它会给出一个异常(我知道这会发生,我宁愿把代码放进去阻止它发生,而不是把它放在一个 try 块中

private void Sheet1_Shutdown(object sender, System.EventArgs e)
{
     dataCollected = true;
     wrench.Close();
}

这会使程序在尝试关闭串行端口上的连接时挂起...如果我删除 wrench.close - 则显示上述异常(如我所料)。

--编辑--

我尝试在我的 while 循环中将我的布尔值声明为全局 volatile 类型,但我对使用这样的东西有一种不好的感觉,似乎是一种 hack(并且仍然不起作用,只是挂起.. .)

【问题讨论】:

    标签: c# serial-port


    【解决方案1】:

    两个问题。第一个是您的 SetControlPropertyValue() 方法。这是一个隐藏 Control.Invoke() 调用的邪恶方法。这非常可能导致死锁。在所有事件处理程序停止运行之前,SerialPort.Close() 方法无法关闭端口。但在 UI 线程空闲之前,Invoke() 调用无法完成。它不是空闲的,它正忙于尝试关闭端口。无法完成事件处理程序,无法关闭端口,死锁城市。您必须改用 BeginInvoke()。

    第二个问题是您在串行端口忙于接收数据时猛拉地垫。 .NET 想让您知道这一点,ReadLine() 调用无法正常完成。它当然是通过抛出异常来实现的。通过相当随意地读取数据,您肯定会在这里失去优雅的点。一个良好实现的串行端口协议要求设备发送数据,设备以单个数据项进行响应。听起来您的设备只是在不断地发送数据。如果您无法对其进行适当的控制,例如关闭 RtsEnable 握手信号,那么异常可能是您最不反对的解决方案。

    请注意一个错误,// data loss - try again 评论看起来很不健康。您只需清除您使用的任何响应缓冲区即可重试,DataReceived 事件将在没有您帮助的情况下再次触发。使用 ReadLine() 通常是让 SerialPort 处理从设备获取完整响应的一种简单方法,但如果您不能确定它总是完成,那么您确实需要设置 ReadTimeout 属性。不要把它设置得太低,10 秒是一个安全的赌注。如何处理 TimeoutException 取决于您。

    【讨论】:

    • 破解!将邪恶的 Invoke 更改为 BeginInvoke 并删除了在碰巧发生某种数据丢失时尝试获取数据的代码位 - 这从未发生过,只需尝试为不太可能发生的情况进行编程。我今天也了解了死锁,谢谢哈哈!
    【解决方案2】:

    我认为您的错误源于您仍在等待来自您的 serial.ReadLine() 的更多数据。这个 serial.ReadLine() 的问题在于它的阻塞性质。如果您打算使用 serial.Close() 或 serial.Dispose() 而 serial.ReadLine() 仍处于挂起状态,您最终会遇到您指示的情况。

    毕竟缺少回车似乎发生了,因为您遇到了这种“找不到回车,数据丢失 - 重试”的情况。

    你最好做一个不阻塞的serial.Read(x-characters),让你控制什么时候停止阅读,这样你就可以干净地关闭了。

    【讨论】:

    • 好的,谢谢,没有意识到 ReadLine();本质上是阻塞的。我想我可以创建自己的 readline 函数,它从Read 中接收字符串,然后检查回车符。只是意味着我不能偷懒:')。我会实现这个想法,看看它是如何运作的,然后再看看其他人是否有其他想法!
    • 最后,您需要做的是检查您的数据是否有效。所以是的,作为验证过程的一部分,您需要这样做。同时,您可能应该将收到的数据连接到 StringBuilder 对象中(如果您收到的是字符串),以便在您认为准备就绪后处理整个缓冲区。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多