SerialCommunication是我综合网上看到的代码稍作修改而成的串口通信类,而SerialPort则是C#的System类库的IO目录Ports子目录下的串口通信类。SerialCommunication只有区区的二百多行,而SerialPort则有几千行。下面我将介绍SerialPort相对于SerialCommunication好在哪里。

  首先是一些基本字段的对比。

/// <summary>
/// 波特率
/// </summary>
public int BaudRate;
/// <summary>
/// 数据位
/// </summary>
public byte ByteSize = 8;
/// <summary>
/// 奇偶校验,0-4=no,odd,even,mark,space
/// </summary>
public byte Parity = 1;
/// <summary>
/// 串口号
/// </summary>
public int PortNum;
/// <summary>
/// 读超时
/// </summary>
public UInt32 ReadTimeout = 1000;
/// <summary>
/// 停止位,0,1,2 = 1, 1.5, 2
/// </summary>
public byte StopBits;
public SerialPort()
{
this.baudRate = 0x2580;
this.dataBits = 8;
this.stopBits = System.IO.Ports.StopBits.One;
this.portName = "COM1";
this.encoding = System.Text.Encoding.ASCII;
this.decoder = System.Text.Encoding.ASCII.GetDecoder();
this.maxByteCountForSingleChar = System.Text.Encoding.ASCII.GetMaxByteCount(1);
this.readTimeout = -1;
this.writeTimeout = -1;
this.receivedBytesThreshold = 1;
this.parityReplace = 0x3f;
this.newLine = "\n";
this.readBufferSize = 0x1000;
this.writeBufferSize = 0x800;
this.inBuffer = new byte[0x400];
this.oneChar = new char[1];
}
public SerialPort(IContainer container)
{
this.baudRate = 0x2580;
this.dataBits = 8;
this.stopBits = System.IO.Ports.StopBits.One;
this.portName = "COM1";
this.encoding = System.Text.Encoding.ASCII;
this.decoder = System.Text.Encoding.ASCII.GetDecoder();
this.maxByteCountForSingleChar = System.Text.Encoding.ASCII.GetMaxByteCount(1);
this.readTimeout = -1;
this.writeTimeout = -1;
this.receivedBytesThreshold = 1;
this.parityReplace = 0x3f;
this.newLine = "\n";
this.readBufferSize = 0x1000;
this.writeBufferSize = 0x800;
this.inBuffer = new byte[0x400];
this.oneChar = new char[1];
container.Add(this);
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public SerialPort(string portName) : this(portName, 0x2580, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One)
{
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public SerialPort(string portName, int baudRate) : this(portName, baudRate, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One)
{
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public SerialPort(string portName, int baudRate, System.IO.Ports.Parity parity) : this(portName, baudRate, parity, 8, System.IO.Ports.StopBits.One)
{
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public SerialPort(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits) : this(portName, baudRate, parity, dataBits, System.IO.Ports.StopBits.One)
{
}
public SerialPort(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits, System.IO.Ports.StopBits stopBits)
{
this.baudRate = 0x2580;
this.dataBits = 8;
this.stopBits = System.IO.Ports.StopBits.One;
this.portName = "COM1";
this.encoding = System.Text.Encoding.ASCII;
this.decoder = System.Text.Encoding.ASCII.GetDecoder();
this.maxByteCountForSingleChar = System.Text.Encoding.ASCII.GetMaxByteCount(1);
this.readTimeout = -1;
this.writeTimeout = -1;
this.receivedBytesThreshold = 1;
this.parityReplace = 0x3f;
this.newLine = "\n";
this.readBufferSize = 0x1000;
this.writeBufferSize = 0x800;
this.inBuffer = new byte[0x400];
this.oneChar = new char[1];
this.PortName = portName;
this.BaudRate = baudRate;
this.Parity = parity;
this.DataBits = dataBits;
this.StopBits = stopBits;
}

  可以看到我有一个明显的不足,就是用了public的字段,这貌似是违反了面向对象的理念的,按照面向对象的理念,外部类应该是不能直接访问操作其他类的内部字段的,不过有些人就认为写private字段,public属性太麻烦,过度设计,能不用属性就不用属性。这里就不讨论用属性的优点了。除此之外还可以看到SerialPort有个明显的优点,使用了大量的重载方法,那些重载方法之间又用了继承,最终执行了参数最多的那个重载方法,该方法里面又将那些参数赋值给了对应的属性,各属性赋值时候又可以进行各种判断,这就是C#的经典套路,也是比较好的套路,只是这样写代码会长很多。

  然后看串口打开方法

public void Open()
{
// OPEN THE COMM PORT.
_hComm = CreateFile("COM" + PortNum, GenericRead | GenericWrite, 0, 0, OpenExisting, 0, 0);
// IF THE PORT CANNOT BE OPENED, BAIL OUT.
if (_hComm == InvalidHandleValue)
{
throw (new Exception("Port Open Failure"));
}
CommTimeouts ctoCommPort = new CommTimeouts { ReadTotalTimeoutConstant = ReadTimeout };
if (!SetCommTimeouts(_hComm, ref ctoCommPort))
{
throw (new Exception("Bad Timeout Settings"));
}
Dcb dcb = new Dcb();
GetCommState(_hComm, ref dcb);
dcb.BaudRate = BaudRate;
dcb.Parity = Parity;
dcb.ByteSize = ByteSize;
dcb.StopBits = StopBits;
if (!SetCommState(_hComm, ref dcb))
{
throw (new Exception("Bad Com Settings"));
}
}
public void Open()
{
if (this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_already_open"));
}
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
this.internalSerialStream = new SerialStream(this.portName, this.baudRate, this.parity, this.dataBits, this.stopBits, this.readTimeout, this.writeTimeout, this.handshake, this.dtrEnable, this.rtsEnable, this.discardNull, this.parityReplace);
this.internalSerialStream.SetBufferSizes(this.readBufferSize, this.writeBufferSize);
this.internalSerialStream.ErrorReceived += new SerialErrorReceivedEventHandler(this.CatchErrorEvents);
this.internalSerialStream.PinChanged += new SerialPinChangedEventHandler(this.CatchPinChangedEvents);
this.internalSerialStream.DataReceived += new SerialDataReceivedEventHandler(this.CatchReceivedEvents);
}
internal SerialStream(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits, System.IO.Ports.StopBits stopBits, int readTimeout, int writeTimeout, System.IO.Ports.Handshake handshake, bool dtrEnable, bool rtsEnable, bool discardNull, byte parityReplace)
{
int dwFlagsAndAttributes = 0x40000000;
if (Environment.OSVersion.Platform == PlatformID.Win32Windows)
{
dwFlagsAndAttributes = 0x80;
this.isAsync = false;
}
if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException(SR.GetString("Arg_InvalidSerialPort"), "portName");
}
SafeFileHandle hFile = Microsoft.Win32.UnsafeNativeMethods.CreateFile(@"\\.\" + portName, -1073741824, 0, IntPtr.Zero, 3, dwFlagsAndAttributes, IntPtr.Zero);
if (hFile.IsInvalid)
{
InternalResources.WinIOError(portName);
}
try
{
int fileType = Microsoft.Win32.UnsafeNativeMethods.GetFileType(hFile);
if ((fileType != 2) && (fileType != 0))
{
throw new ArgumentException(SR.GetString("Arg_InvalidSerialPort"), "portName");
}
this._handle = hFile;
this.portName = portName;
this.handshake = handshake;
this.parityReplace = parityReplace;
this.tempBuf = new byte[1];
this.commProp = new Microsoft.Win32.UnsafeNativeMethods.COMMPROP();
int lpModemStat = 0;
if (!Microsoft.Win32.UnsafeNativeMethods.GetCommProperties(this._handle, ref this.commProp) || !Microsoft.Win32.UnsafeNativeMethods.GetCommModemStatus(this._handle, ref lpModemStat))
{
int errorCode = Marshal.GetLastWin32Error();
switch (errorCode)
{
case 0x57:
case 6:
throw new ArgumentException(SR.GetString("Arg_InvalidSerialPortExtended"), "portName");
}
InternalResources.WinIOError(errorCode, string.Empty);
}
if ((this.commProp.dwMaxBaud != 0) && (baudRate > this.commProp.dwMaxBaud))
{
throw new ArgumentOutOfRangeException("baudRate", SR.GetString("Max_Baud", new object[] { this.commProp.dwMaxBaud }));
}
this.comStat = new Microsoft.Win32.UnsafeNativeMethods.COMSTAT();
this.dcb = new Microsoft.Win32.UnsafeNativeMethods.DCB();
this.InitializeDCB(baudRate, parity, dataBits, stopBits, discardNull);
this.DtrEnable = dtrEnable;
this.rtsEnable = this.GetDcbFlag(12) == 1;
if ((handshake != System.IO.Ports.Handshake.RequestToSend) && (handshake != System.IO.Ports.Handshake.RequestToSendXOnXOff))
{
this.RtsEnable = rtsEnable;
}
if (readTimeout == 0)
{
this.commTimeouts.ReadTotalTimeoutConstant = 0;
this.commTimeouts.ReadTotalTimeoutMultiplier = 0;
this.commTimeouts.ReadIntervalTimeout = -1;
}
else if (readTimeout == -1)
{
this.commTimeouts.ReadTotalTimeoutConstant = -2;
this.commTimeouts.ReadTotalTimeoutMultiplier = -1;
this.commTimeouts.ReadIntervalTimeout = -1;
}
else
{
this.commTimeouts.ReadTotalTimeoutConstant = readTimeout;
this.commTimeouts.ReadTotalTimeoutMultiplier = -1;
this.commTimeouts.ReadIntervalTimeout = -1;
}
this.commTimeouts.WriteTotalTimeoutMultiplier = 0;
this.commTimeouts.WriteTotalTimeoutConstant = (writeTimeout == -1) ? 0 : writeTimeout;
if (!Microsoft.Win32.UnsafeNativeMethods.SetCommTimeouts(this._handle, ref this.commTimeouts))
{
InternalResources.WinIOError();
}
if (this.isAsync && !ThreadPool.BindHandle(this._handle))
{
throw new IOException(SR.GetString("IO_BindHandleFailed"));
}
Microsoft.Win32.UnsafeNativeMethods.SetCommMask(this._handle, 0x1fb);
this.eventRunner = new EventLoopRunner(this);
new Thread(new ThreadStart(this.eventRunner.WaitForCommEvent)) { IsBackground = true }.Start();
}
catch
{
hFile.Close();
this._handle = null;
throw;
}
}

  打开方法SerialPort比我的复杂很多,我那个只是打开并设置了一下串口,SerialPort除此之外还开启了ErrorReceived、PinChanged和DataReceived事件。两个接收事件和一个貌似是串口端口改变事件吧。前两个不写是因为我觉得有些人串口通信只需要写不需要接收,那么那两个事件也就不需要了吧,如果需要接收的话调用我那个Read方法就可以接收消息了,不过需要调用者自己开线程监听。

  不说这个,就说代码方面的问题吧,我那里有个明显的不太好的地方就是,抛出的异常没有进行分类,异常是有格式的,也可以自定义异常,我那里没有对异常进行分类,全都是抛出Exception就完了,这样的话调用者就不能方便的根据异常的类型进行不同的处理了。除此之外抛出的异常也不够详细,SerialPort的Open方法一开始就有一个Port_already_open端口已打开异常,我那边就没有分的这么细了,只要端口打开失败就返回Port Open Failure,异常分的不够细也是不好的。

  除此之外就是SerialPort用了大量的枚举,如StopBits、Handshake、Parity、SerialData、SerialError和SerialPinChange,用枚举有个很大的好处就是调用者可以清楚的根据枚举来判断每个枚举值的意义,这样子调用者可以在不查文档的情况下就顺利的写完了代码,这可能会节省编码人员很多的时间,与用枚举损失的些须效率,这是完全值得的,我没有用枚举只是贪图方便省事,这是不够好的,应该注意一下。

  接着看串口关闭方法

public void Close()
{
if (_hComm != InvalidHandleValue)
{
CloseHandle(_hComm);
}
}
public void Close()
{
base.Dispose();
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
lock (this)
{
if ((this.site != null) && (this.site.Container != null))
{
this.site.Container.Remove(this);
}
if (this.events != null)
{
EventHandler handler = (EventHandler) this.events[EventDisposed];
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
}
}

  关闭方法就完全不同了,我那关闭方法只是把串口关闭,SerialPort则貌似是把整个类Dispose了,这两种做法萝卜青菜各有所爱吧。不过我那个类貌似没有继承自IDisposable貌似不太好,比较大的类应该要提供Dispose才好的吧。

  最后看看读写方法吧

public byte[] Read(int numBytes)
{
if (_hComm == InvalidHandleValue)
{
throw (new Exception("Comm Port Not Open"));
}
int bytesRead = 0;
byte[] bufBytes = new byte[numBytes];
ReadFile(_hComm, bufBytes, numBytes, ref bytesRead, ref _ovlCommPort);
byte[] outBytes = new byte[bytesRead];
Array.Copy(bufBytes, outBytes, bytesRead);
return outBytes;
}
public void Write(byte[] writeBytes)
{
if (_hComm == InvalidHandleValue)
{
throw (new Exception("Comm Port Not Open"));
}
WriteFile(_hComm, writeBytes, writeBytes.Length, ref _bytesCount, ref _ovlCommPort);
}
public int Read(byte[] buffer, int offset, int count)
{
if (!this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_not_open"));
}
if (buffer == null)
{
throw new ArgumentNullException("buffer", SR.GetString("ArgumentNull_Buffer"));
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if ((buffer.Length - offset) < count)
{
throw new ArgumentException(SR.GetString("Argument_InvalidOffLen"));
}
int num = 0;
if (this.CachedBytesToRead >= 1)
{
num = Math.Min(this.CachedBytesToRead, count);
Buffer.BlockCopy(this.inBuffer, this.readPos, buffer, offset, num);
this.readPos += num;
if (num == count)
{
if (this.readPos == this.readLen)
{
this.readPos = this.readLen = 0;
}
return count;
}
if (this.BytesToRead == 0)
{
return num;
}
}
this.readLen = this.readPos = 0;
int num2 = count - num;
num += this.internalSerialStream.Read(buffer, offset + num, num2);
this.decoder.Reset();
return num;
}
public int Read(char[] buffer, int offset, int count)
{
if (!this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_not_open"));
}
if (buffer == null)
{
throw new ArgumentNullException("buffer", SR.GetString("ArgumentNull_Buffer"));
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if ((buffer.Length - offset) < count)
{
throw new ArgumentException(SR.GetString("Argument_InvalidOffLen"));
}
return this.InternalRead(buffer, offset, count, this.readTimeout, false);
}
public void Write(string text)
{
if (!this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_not_open"));
}
if (text == null)
{
throw new ArgumentNullException("text");
}
if (text.Length != 0)
{
byte[] bytes = this.encoding.GetBytes(text);
this.internalSerialStream.Write(bytes, 0, bytes.Length, this.writeTimeout);
}
}
public void Write(byte[] buffer, int offset, int count)
{
if (!this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_not_open"));
}
if (buffer == null)
{
throw new ArgumentNullException("buffer", SR.GetString("ArgumentNull_Buffer"));
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if ((buffer.Length - offset) < count)
{
throw new ArgumentException(SR.GetString("Argument_InvalidOffLen"));
}
if (buffer.Length != 0)
{
this.internalSerialStream.Write(buffer, offset, count, this.writeTimeout);
}
}
public void Write(char[] buffer, int offset, int count)
{
if (!this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_not_open"));
}
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if ((buffer.Length - offset) < count)
{
throw new ArgumentException(SR.GetString("Argument_InvalidOffLen"));
}
if (buffer.Length != 0)
{
byte[] buffer2 = this.Encoding.GetBytes(buffer, offset, count);
this.Write(buffer2, 0, buffer2.Length);
}
}
public void WriteLine(string text)
{
this.Write(text + this.NewLine);
}

  对比可以得出我那个读写方法没有SerialPort那么多,我那里只提供了byte[]的读写,这是相对来说比较不够用的,不过这个不是问题。问题是我那里读写时的异常判断太少,只判断了串口关闭的情况,这样有些异常就不能在收发之前被判断出来了。

  总结来说就是自己写的代码面向对象得还不够,代码不够规范,考虑的不够全面等,希望以后可以学着向“SerialPort”看齐!

对比SerialCommunication和微软的SerialPort,向SerialPort看齐的更多相关文章

  1. [转]微软SerialPort秘籍[SerialPort为什么死锁程序的分析]

    既然是秘籍,显然是写一些大家不常找到的,MSDN里遗漏提示大家注意的东西. 用过.net 2.0中,自带SerialPort的人,大多都遇到过.莫名其妙的执行Close的时候会死掉的问题.而Wince ...

  2. SerialPort.h SerialPort.cpp

    SerialPort.h 1 #ifndef __SERIALPORT_H__ 2 #define __SERIALPORT_H__ 3 4 #define WM_COMM_BREAK_DETECTE ...

  3. 使用SerialPort 读取外置GPS信息和使用GeoCoordinateWatcher获取内置gps的信息

    简介最近工作中需要读取gps设备的信息,平板本身有内置的gps设备,但是精度不够,就又添加了一个外置的gps.对于外置的gps,我们主要通过SerialPort类来获得串口的信息,然后对接收到的内容进 ...

  4. SerialPort

    using System;   using System.Collections.Generic;   using System.ComponentModel;   using System.Data ...

  5. [转]How to display the data read in DataReceived event handler of serialport

    本文转自:https://stackoverflow.com/questions/11590945/how-to-display-the-data-read-in-datareceived-event ...

  6. [转]c# System.IO.Ports SerialPort Class

    本文转自:https://docs.microsoft.com/en-us/dotnet/api/system.io.ports.serialport?redirectedfrom=MSDN& ...

  7. C# 串口类SerialPort的使用方法

    序言:最近做了一个智能体育项目——跆拳道积分系统,硬件部分会向软件传入振动值等数据,链接方式为串口,所以用到SerialPort类. 值得注意的是: DataReceived 方法,当串口缓冲区有数据 ...

  8. C#串口SerialPort常用属性方法

    SerialPort(): //属性 .BaudRate;获取或设置波特率 .BytesToRead;得到 接收到数据的字节数 .BytesToWrites;得到送往串口的字节数 .DataBits; ...

  9. (转)C#串口SerialPort常用属性方法

    SerialPort(): //属性 .BaudRate;获取或设置波特率 .BytesToRead;得到 接收到数据的字节数 .BytesToWrites;得到送往串口的字节数 .DataBits; ...

随机推荐

  1. MYSQL(二)

    上一篇文章讲的是mysql的基本操作,这一篇会有一点难以理解,本节主要内容mysql视图,存储过程,函数,事务,触发器,以及动态执行sql 视图view 视图是一个虚拟表,其内容由查询定义.同真实的表 ...

  2. Java 网络编程之 Socket

    ========================UDP============================= UDP---用户数据报协议,是一个简单的面向数据报的运输层协议. UDP不提供可靠性, ...

  3. tornado 路由系统----扩展(include)

    在Tornado中实现Django分层路由 看了几个 Tornado 的 demo 发现都是一个py搞定所有,这样做做为例子来讲确实很直观,但如果在大型项目中这样做肯定不现实,所以博主考虑如何制定合理 ...

  4. 【python】isinstance可以接收多个类型,hasattr,getattr,setattr

    来源:廖雪峰 可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是str或者unicode: >>> isinstance('a', (str, unicode)) ...

  5. .NET Lambda

    Lambda概述      lambda 表达式是一个可用于创建委托或表达式树类型的匿名函数. 通过使用 lambda 表达式,可以可作为参数或返回编写本地函数,该函数调用的值. Lambda 表达式 ...

  6. NEFU 561 方块计算

    题目链接 简单搜索题 #include <cstdio> #include <iostream> #include <cstring> using namespac ...

  7. 电脑莫名其妙的被装上了流氓软件DNSunlocker的解决办法

    重大更新:之前说的这些方法只能删除流氓文件,不能解决其弹出机具诱惑性的广告,网络经常不好用 经过测试,使用Malwarebytes Anti-Malware可以彻底解决这个问题,还能发现更多隐藏在你电 ...

  8. iTextSharp简单生成pdf和操作pdf添加水印

    遇到需要导出页面到pdf,并添加pdf水印的业务.稍微研究了下,借阅网友的前车之鉴,经过使用可行之后的代码贴出来,做个记录,也供需要的网友借阅. public class PDFSetWaterMar ...

  9. 关闭英文拼写检查,关闭xml验证

    http://blog.sina.com.cn/s/blog_70b623e4010173ce.html eclipse里面的许多设置对于国内开发者日常使用不太适用,反而会成为干扰.既然是完全可配置的 ...

  10. 分享Kali Linux 2016.2第50周镜像文件

    分享Kali Linux 2016.2第50周镜像文件Kali Linux官方于12月11日发布Kali Linux 2016.2的第50周镜像.这次保持以往规律,仍然是11个镜像文件.默认的Gnom ...