分享

C#中如何安全的关闭串口(By?Kiseigo)

 goodwangLib 2012-02-17

VC#中如果涉及到多线程,特别是大量的数据处理和界面更新时,如果简单强制的关闭串口,很可能会造成串口死掉,我1年来一直有个想法,今天终于真正找到了原因和解决的办法。

 C#中如何安全的关闭串口(By <wbr>Kiseigo)

串口无法关闭的原因是:要关闭串口的时候,有其它线程还在读取数据或者更新界面。
关键是:在准备关闭串口的时候,看看是否在接收和处理数据,如果是就等它处理完为止;在事件处理的最前面,判断如果是准备关闭串口的bool类型值,就不再进入数据接收和处理。

 

2010.03.27 更新 Help By Wyz. I appreciate it !!!

 

using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Timers;


namespace PortTesting
{
    /// <summary>
    /// 定义了一个委托类型
    /// </summary>
    public delegate void WhenGetNew();

    /// <summary>
    /// 串口封装类,Help By Wyz
    /// </summary>
    public class PortDataDisplay
    {
        /// <summary>
        /// 系统串口类
        /// </summary>
        public SerialPort serialPort = new SerialPort("COM1", 19200);
        /// <summary>
        /// 解析得到数据后触发事件
        /// </summary>
        public event WhenGetNew whenGetNew;
        /// <summary>
        /// 处理线程
        /// </summary>
        private SerialDataReceivedEventHandler threadCallHandler;
        /// <summary>
        /// 对外的数据类型定义
        /// </summary>
        public string dataSrc = "";
        /// <summary>
        /// 准备关闭串口=true
        /// </summary>
        private bool m_IsTryToClosePort = false;
        /// <summary>
        /// true表示正在接收数据
        /// </summary>
        private bool m_IsReceiving = false;
       

        /// <summary>
        /// 初始化
        /// </summary>
        public PortDataDisplay()
        {
        }

        /// <summary>
        /// 有参数的构造函数
        /// </summary>
        /// <param name="PortName">串口号,如"COM1"</param>
        /// <param name="BaudRate">波特率,如19200</param>
        public PortDataDisplay(string PortName, int BaudRate)
        {
            serialPort = new SerialPort(PortName, BaudRate);
        }

        /// <summary>
        /// 开始工作
        /// </summary>
        public void ConnectDeveice()
        {
            //0.注册事件
            serialPort.DataReceived -= OnSerialPortDataCome;
            serialPort.DataReceived += OnSerialPortDataCome;
            //1.再设置一下串口参数
            if (this.serialPort.IsOpen == false)
            {
                this.serialPort.ReadBufferSize = 1000;
                this.serialPort.ReceivedBytesThreshold = 1;//数据达到120的时候才就要触发事件,不行!!应该是数据来就触发
                //2.打开串口开始工作
                m_IsTryToClosePort = false;
                this.serialPort.Open();
            }
        }

        /// <summary>
        /// 结束工作
        /// </summary>
        public void DisconnectDeveice() // 关键和核心!!!
        {
            m_IsTryToClosePort = true;
            while (m_IsReceiving)
            {
                System.Windows.Forms.Application.DoEvents();
            }
            serialPort.Close();
        }

        /// <summary>
        /// 当通知到有数据达到120时处理(读取,与分析)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSerialPortDataCome(object sender, SerialDataReceivedEventArgs e)
        {
            if (m_IsTryToClosePort) // 关键!!!
            {
                return;
            }

            m_IsReceiving = true; // 关键!!!

            try
            {
                if (threadCallHandler == null)
                {
                    threadCallHandler = new SerialDataReceivedEventHandler(OnSerialPortDataCome);
                }

                //read
                dataSrc = serialPort.ReadExisting();//读出缓冲区所有数据
                if (dataSrc != "" && this.whenGetNew != null)
                {
                    this.whenGetNew();
                }
            }
            finally // 放在finally里面比较好。
            {
                m_IsReceiving = false; // 关键!!!
            }
        }
    }
}

 

 

使用的时候:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace PortTesting
{
    public partial class FrmMain : Form
    {
        /// <summary>
        /// 封装好的串口类
        /// </summary>
        private PortDataDisplay m_portDispl = new PortDataDisplay("COM1", 19200);

        public FrmMain()
        {
            InitializeComponent();
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            if (btnOpen.Text == "打开串口")
            {
                m_portDispl.whenGetNew -= portDispl_whenGetNew;
                m_portDispl.whenGetNew += new WhenGetNew(portDispl_whenGetNew);
                m_portDispl.ConnectDeveice();
                btnOpen.Text = "关闭串口";
            }
            else if (btnOpen.Text == "关闭串口")
            {
                m_portDispl.DisconnectDeveice();
                btnOpen.Text = "打开串口";
            }
        }

        /// <summary>
        /// 事件
        /// </summary>
        private void portDispl_whenGetNew()
        {
            WhenGetNew ehan = delegate
            {
                txtDisplay.AppendText(m_portDispl.dataSrc);
            };

            try
            {
                if (InvokeRequired)
                {
                    this.Invoke(ehan);
                }
            }
            catch
            {
            }
        }


        private void btnClear_Click(object sender, EventArgs e)
        {
            txtDisplay.Clear();
        }
    }
}

 

 

 

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多