在后台工作程序中关闭COM端口时应用程序崩溃 - C#应用程序?

时间:2015-04-21 12:02:19

标签: c# serial-port thread-safety

我做了一个快速搜索,仍然无法找到我的问题的答案。

串口变量

int close;
SerialPort _serialPort = new SerialPort("COM1", 1200, Parity.None, 8, StopBits.One);

串口代理码

private void si_DataReceived(string data)
{
    if (close == 1)
    {
        string d1 = data.Trim().Replace("TT", "");
        d1 = d1.Replace("Tl3", "");
        txtCan.Text = d1;
    }
    else
    {
        return;
    }

}
private delegate void SetTextDeleg(string text);
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    string data = _serialPort.ReadLine();
    this.BeginInvoke(new SetTextDeleg(si_DataReceived), new object[] { data });
}

连接按钮代码

private void button4_Click(object sender, EventArgs e)
{
    if (button4.Text == "Close Connection")
    {
        progressBar1.Style = ProgressBarStyle.Continuous;
        progressBar1.MarqueeAnimationSpeed = 0;
        close=0;
        try
        {
            string d1 = txtCan.Text;
            double r1 = Convert.ToDouble(d1) * 10;
            txtCan.Text = Math.Round(r1, 3).ToString();
            button4.Text = "Open Connection";
            button1.Enabled = true;
            readOnly(true);
        }
        catch (Exception ex)
        {
            button4.Text = "Open Connection";
            button1.Enabled = true;
            progressBar1.Style = ProgressBarStyle.Continuous;
            progressBar1.MarqueeAnimationSpeed = 0;
            MessageBox.Show("Cant connect.");
        }
    }
    else
    {
        progressBar1.Style = ProgressBarStyle.Marquee;
        progressBar1.MarqueeAnimationSpeed = 30;
        close = 1;
        txtCan.Text = "";
        txtCan.Focus();
        readOnly(false);
        button1.Enabled = false;
        button4.Text = "Close Connection";
        try
        {
            if(!_serialPort.IsOpen)
            { 
                _serialPort.Handshake = Handshake.None;
                _serialPort.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
                _serialPort.Open();
            }
        }
        catch
        {
            button4.Text = "Open Connection";
            button1.Enabled = true;
            readOnly(true);
            progressBar1.Style = ProgressBarStyle.Continuous;
            progressBar1.MarqueeAnimationSpeed = 0;
            MessageBox.Show("Cant connect.");
        }
    }
}

我关闭com端口的解决方案,但如果我在可数次重新打开一个表单(如两次或三次,那么它会崩溃),它将会失败。

private void myForm_FormClosing(object sender, FormClosingEventArgs e)
{
    if (_serialPort.IsOpen)
    {
        e.Cancel = true; 
        Thread CloseDown = new Thread(new ThreadStart(CloseSerialOnExit)); 
        CloseDown.Start();
    }
}

private void CloseSerialOnExit()
{
    try
    {
        backgroundWorker1.RunWorkerAsync();
    }
    catch (Exception ex)
    {
    }
    this.BeginInvoke(new EventHandler(NowClose)); 
}

private void NowClose(object sender, EventArgs e)
{
    this.Close(); //now close the form
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    _serialPort.Close(); 
}

1 个答案:

答案 0 :(得分:1)

    string data = _serialPort.ReadLine();

此声明不存在潜在的问题。当这个电话没有完成时,你的手上有一个非常讨厌的失败模式。它没有必要,您可以在错误的时间关闭串行设备,猛拉电缆,使线路终结器损坏,您将会非常头疼。

在任何事件处理程序停止运行之前,SerialPort.Close()方法无法完成。这可以显着增加死锁的几率。使用ReadLine()方法很好,但是必须将ReadTimeout属性设置为10秒,这样您就可以确保事故不会变成不可解决的问题。让TimeoutException终止你的程序或设置一个"串口已经死了"状态变量。

通过将SerialPort的生命周期与表单的生命周期取消关联,进一步提高成功几率。一般来说,唯一明智的做法是在程序启动时创建实例,而不是关闭它直到程序终止。只需将它保存在一个单独的类中,它可以是 static ,以便于使用。

而且,毫不直观地,现在调用Close()已经不再重要了。终结者会照顾它。

如果您使用USB仿真器,请确保从不断开连接,而无需通过安全删除硬件托盘图标。 USB驱动程序通常很难处理意外删除。有些是如此糟糕,以至于即使你打开设备也会让设备消失。并试图关闭它总是失败,你不能再干净地退出你的程序。