译自Change DCB fields from SerialPort instance C#

C# SerialPort自定义串口DCB


DCB(Device Control Block)在C++ 里面是用bitfield(位域)表示的,C#没有bitfield,但有一个枚举位标志。C#有自己的方法来设置怎么存取DCB,而且“SerialStream”里确实也有“SetDcbFlag”方法(该方法接收2个int参数)。在研究了.Net源码后,我创建了一组常量用来等效原来的DCB int字段和他们的新int代码,我暂时还没有整理出所有位标志,不过你可以在DCB文档里面查阅它们。因为这是一个Internal方法只对.Net内部程序集可见,所以我在获取该方法时用到了System.Reflection(反射)。

下面就是代码,整体上是一个.Net 2.0+SerialPort的扩展类。扩展类中有三个方法,SetField(string name, object value)方法用来设置所有不是以'f'开头的位,SetFlag(int Flag, int Value)方法用来处理以'f'开头的位(我提供了一个针对Flag参数的const列表),最后UpdateComm()方法用来在你更改DCB后刷新串口连接,它本来是SetField方法的一部分,但是如果设置DCB时每次都调用它,那花的时间就有点长了。

注意:
确保开启串口连接后,再使用这些方法。

用法:

SerialPort COM = new SerialPort("COM7");
COM.Open();
COM.DiscardInBuffer();
COM.DiscardOutBuffer();
COM.SetFlag(FBINARY, );
COM.SetFlag(FPARITY, );
COM.SetFlag(FDTRCONTROL, 0x00);
COM.SetFlag(FRTSCONTROL, 0x01);
COM.SetField("BaudRate", (UInt32));
COM.SetField("StopBits", (byte));
COM.SetField("ByteSize", (byte));
COM.SetField("Parity", (byte));
COM.SetField("XonChar", (byte)0x11);
COM.SetField("XoffChar", (byte)0x13);
COM.SetField("EvtChar", (byte)0x1A);
COM.SetField("XonLim", (ushort));
COM.SetField("XoffLim", (ushort));
COM.UpdateComm();
/* Do Stuff */
COM.Close();

常量:

internal const int FBINARY = ;
internal const int FPARITY = ;
internal const int FOUTXCTSFLOW = ;
internal const int FOUTXDSRFLOW = ;
internal const int FDTRCONTROL = ;
internal const int FDSRSENSITIVITY = ;
internal const int FTXCONTINUEONXOFF = ;
internal const int FOUTX = ;
internal const int FINX = ;
internal const int FERRORCHAR = ;
internal const int FNULL = ;
internal const int FRTSCONTROL = ;
internal const int FABORTONOERROR = ;
internal const int FDUMMY2 = ;

最后,扩展类

internal static class SerialPortExtensions
{
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void SetField(this SerialPort port, string field, object value)
{
if (port == null)
throw new NullReferenceException();
if (port.BaseStream == null)
throw new InvalidOperationException("Cannot change fields until after the port has been opened.");
try
{
object baseStream = port.BaseStream;
Type baseStreamType = baseStream.GetType();
FieldInfo dcbFieldInfo = baseStreamType.GetField("dcb", BindingFlags.NonPublic | BindingFlags.Instance);
object dcbValue = dcbFieldInfo.GetValue(baseStream);
Type dcbType = dcbValue.GetType();
dcbType.GetField(field).SetValue(dcbValue, value);
dcbFieldInfo.SetValue(baseStream, dcbValue);
}
catch (SecurityException) { throw; }
catch (OutOfMemoryException) { throw; }
catch (Win32Exception) { throw; }
catch (Exception)
{
throw;
}
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void SetFlag(this SerialPort port, int flag, int value)
{
object BaseStream = port.BaseStream;
Type SerialStream = BaseStream.GetType();
SerialStream.GetMethod("SetDcbFlag", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(BaseStream, new object[] { flag, value });
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void UpdateComm(this SerialPort port)
{
object baseStream = port.BaseStream;
Type baseStreamType = baseStream.GetType();
FieldInfo dcbFieldInfo = baseStreamType.GetField("dcb", BindingFlags.NonPublic | BindingFlags.Instance);
object dcbValue = dcbFieldInfo.GetValue(baseStream);
SafeFileHandle portFileHandle = (SafeFileHandle)baseStreamType.GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(baseStream);
IntPtr hGlobal = Marshal.AllocHGlobal(Marshal.SizeOf(dcbValue));
try
{
Marshal.StructureToPtr(dcbValue, hGlobal, false);
if (!SetCommState(portFileHandle, hGlobal))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
if (hGlobal != IntPtr.Zero)
Marshal.FreeHGlobal(hGlobal);
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetCommState(SafeFileHandle hFile, IntPtr lpDCB);
}

Change DCB fields from SerialPort instance C


The DCB struct requires a C++ class known as a bitfield, which C# does not have, so instead they have a field called "Flags" stored as a UInt32.They have their own melarchy set up there as to how it's stored and read from, but they did put a method in the SerialStream called SetDcbFlag which accepts two ints.After digging through the .net source a bit, I managed to come up with a set of constants equating to the original DCB Fields and their new int code, I haven't yet made a list of values for these flags, but those can be easily found in the DCB Documentation.I used system.reflection to gain access to the method as it was an internal method only to be used by internal .NET source.

So here it is, the code, it's an extension class for the SerialPort class which is shipped stock with .NET 2.0+. My extension class adds three methods, SetField(string name, object value) which will set any of the fields that aren't prefixed with "f", SetFlag(int Flag, int Value) which will take care of those fields prefixed with "f" (I'll provide a list of constants for use with the Flag parameter), and finally UpdateComm() this will update the serial connection once you have changed all of your values, it was originally part of the SetField method, but it takes slightly longer to complete things if it's calling that every single time during initialization.

NOTE:
The serial port must be opened before using any of these methods!
Usage:

Constants:

And finally, the extension class:

PS:中文DCB结构详解表


MSDN的DCB文档和这个内容基本相同

成员 取值 说明
DCBlength   DCB结构大小,即sizeof(DCB),在调用SetCommState来更新DCB前必须作设置
BaudRate   指定当前采用的波特率,应与所连接的通讯设备相匹配
fBinary   指定是否允许二进制模式。Win32 API不支持非二进制模式传输,应设置为true
fParity   指定奇偶校验是否允许,在为true时具体采用何种校验看Parity 设置
Parity   指定端口数据传输的校验方法。以下是可取值及其意义:
  EVENPARITY 偶校验(2)
  MARKPARITY 标记校验,所发信息帧第9位恒为1(3)
  NOPARITY 无校验(0)
  ODDPARITY 奇校验(1)
StopBits   指定端口当前使用的停止位数,可取值:
  ONESTOPBIT 1停止位(0)
  ONE5STOPBITS 1.5停止位(1)
  TWOSTOPBITS 2停止位(2)
fErrorChar   该值为TRUE,则用ErrorChar指定的字符代替奇偶校验错误的接收字符
ErrorChar   指定ErrorChar字符(代替接收到的奇偶校验发生错误时的字节)
EvtChar   当接收到此字符时,会产生一个EV_RXFLAG事件,如果用SetCommMask函数中指定了EV_RXFLAG ,则可用WaitCommEvent 来监测该事件
EofChar   指定用于标示数据结束的字符
fNull   为TRUE时,接收时自动去掉空(0值)字节
fAbortOnError   读写操作发生错误时是否取消操作。若设置为true,则当发生读写错误时,将取消所有读写操作(错误状态置为ERROR_IO_ABORTED),直到调用ClearCommError函数后才能重新进行通讯操作
fOutxCtsFlow   是否监控CTS(clear-to-send)信号来做输出流控。当设置为true时:若CTS为低电平,则数据发送将被挂起,直至CTS变为高。CTS的信号一般由DCE(通常是一个Modem)来控制,而DTE(通常是计算机)发送数据时监测CTS信号。也就是说DCE通过把CTS置高来表明自己可以接收数据了
fRtsControl   设置RTS (request-to-send)流控,若为0则缺省取值 RTS_CONTROL_HANDSHAKE。以下是可取值及其意义:
  RTS_CONTROL_DISABLE 打开设备时置RTS信号为低电平,应用程序可通过调用EscapeCommFunction函数来改变RTS线电平状态
  RTS_CONTROL_ENABLE 打开设备时置RTS信号为高电平,应用程序可通过调用EscapeCommFunction函数来改变RTS线电平状态
  RTS_CONTROL_HANDSHAKE 允许RTS信号握手,此时应用程序不能调用EscapeCommFunction函数。当输入缓冲区已经有足够空间接收数据时,驱动程序置RTS为高以便允许DCE来发送;反之置RTS为低以阻止DCE发送数据。
  RTS_CONTROL_TOGGLE 有字节要发送时RTS变高,当所有缓冲字节已经被发送完毕后,RTS变低。此时应用程序不能调用EscapeCommFunction函数。该值在Windows 95系统被忽略
fOutxDsrFlow   是否监控DSR (data-set-ready) 信号来做输出流控。当设置为true时:若DSR为低电平,则数据发送将被挂起,直至DSR变为高。DSR的信号一般由DCE来控制
fDtrControl   DTR (data-terminal-ready)流控,可取值如下:
  DTR_CONTROL_DISABLE 打开设备时置DTR信号为低电平,应用程序可通过调用EscapeCommFunction函数来改变DTR线电平状态
  DTR_CONTROL_ENABLE 打开设备时置DTR信号为高电平,应用程序可通过调用EscapeCommFunction函数来改变DTR线电平状态
  DTR_CONTROL_HANDSHAKE 允许DTR信号握手,此时应用程序不能调用EscapeCommFunction函数
fDsrSensitivity   通讯设备是否对DSR信号敏感。若设置为TRUE,则当DSR为低时将会忽略所有接收的字节
fTXContinueOnXoff   当输入缓冲区满且驱动程序已发出XOFF字符时,是否停止发送。当为TRUE时,XOFF被发送后发送仍然会继续;为FALSE时,发送停止,直至输入缓冲区有XonLim字节的空余空间、驱动程序已发送XON字符之后发送继续。
fOutX   XON/XOFF 流量控制在发送时是否可用。如果为TRUE, 当 XOFF 值被收到的时候,发送停止;当 XON 值被收到的时候,发送继续
fInX   XON/XOFF 流量控制在接收时是否可用。如果为TRUE, 当 输入缓冲区已接收满XoffLim 字节时,发送XOFF字符;当输入缓冲区已经有XonLim 字节的空余容量时,发送XON字符
XonLim   在XON字符发送前接收缓冲区内可允许的最小字节数
XoffLim   在XOFF字符发送前接收缓冲区内可允许的最大字节数
XonChar   指定XON字符
XoffChar   指定XOFF字符
fDummy2   保留,未启用
wReserved   未启用,必须设置为0
wReserved1   保留,未启用

C# SerialPort自定义串口DCB的更多相关文章

  1. C#用SerialPort实现串口通讯

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

  2. Nodejs 使用 SerialPort 调用串口

    工作经常使用串口读写数据,electron 想要替代原来的客户端,串口成了必须要突破的障碍. get -->  https://github.com/EmergingTechnologyAdvi ...

  3. C#SerialPort实现串口控制继电器

    最近做了一个小系统,麻雀虽小五脏俱全呀,用到各种线程控制,串口控制等技术.其中串口控制最麻烦,因为继电器的响应很快,根据不同的转接口,返回的数据质量是不一样的,所以不能直接wirte,然后马上read ...

  4. C#SerialPort如何读取串口数据并显示在TextBox上

    SerialPort中串口数据的读取与写入有较大的不同.由于串口不知道数据何时到达,因此有两种方法可以实现串口数据的读取.一.线程实时读串口:二.事件触发方式实现. 由于线程实时读串口的效率不是十分高 ...

  5. c#实现串口操作 SerialPort

    命名空间:using System.IO.Ports;该类提供了同步 I/O 和事件驱动的 I/O.对管脚和中断状态的访问以及对串行驱动程序属性的访问. 操作类声明: SerialPort sp = ...

  6. System.IO.Ports.SerialPort串口通信接收完整数据

    C#中使用System.IO.Ports.SerialPort进行串口通信网上资料也很多,但都没有提及一些细节: 比如 串口有时候并不会一次性把你想要的数据全部传输给你,可能会分为1次,2次,3次分别 ...

  7. C#串口操作类,包括串口读写操作

    串口进行操作的类,其中包括写和读操作,类可设置串口参数.设置接收函数.打开串口资源.关闭串口资源,操作完成后,一定要关闭串口.接收串口数据事件.接收数据出错事件.获取当前全部串口.把字节型转换成十六进 ...

  8. .Net Core跨平台应用研究-CustomSerialPort(增强型跨平台串口类库)

    .Net Core跨平台应用研究-CustomSerialPort -增强型跨平台串口类库 摘要 在使用SerialPort进行串口协议解析过程中,经常遇到接收单帧协议数据串口接收事件多次触发,协议解 ...

  9. .Net Core 跨平台应用使用串口、串口通信 ,可能出现的问题、更简洁的实现方法

    前些天在学习在 .NET Core下,跨平台使用串口通讯,有一篇文章说到在Linux/物联网下,实现通讯. 主要问题出现在以下两个类库 SerialPortStream flyfire.CustomS ...

随机推荐

  1. 定制化WinPE

    1 .首先挂载wim Dism /Mount-WIM /WimFile:D:\install.wim /Index: /MountDir:D:\wimmount 2. 如何要修改WinPE的启动项,可 ...

  2. bzoj2876 [NOI2012]骑行川藏(拉格朗日乘数法)

    题目描述 蛋蛋非常热衷于挑战自我,今年暑假他准备沿川藏线骑着自行车从成都前往拉萨.川藏线的沿途有着非常美丽的风景,但在这一路上也有着很多的艰难险阻,路况变化多端,而蛋蛋的体力十分有限,因此在每天的骑行 ...

  3. shell脚本实现anisble客户端脚本分发和密钥授权配置

    ##############################Deploy ansible client shell######################## echo "start d ...

  4. linux shell 执行远程命令

    我在本地的shell脚本中,想要直接执行远程服务器的一个shell脚本: ssh -l root 192.168.1.1 "/data/t.sh" 记得提前给远程服务器的 /dat ...

  5. javascript函数大全

    JavaScript函数大全 1.document.write(""); 输出语句2.JS中的注释为//3.传统的HTML文档顺序是:document->html->( ...

  6. 启动MySql提示:The server quit without updating PID file(…)失败

    1.可能是/usr/local/mysql/data/rekfan.pid文件没有写的权限解决方法 :给予权限,执行 "chown -R mysql:mysql /var/data" ...

  7. 阿里云ECS服务器上搭建keepalived+mha+mysql5.6+gtid+一主两从+脚本判断架构踩的坑

    最近,公司项目搭建了一套后端数据库架构,不是在RDS,是在阿里云的ECS服务器上搭建keepalived.mha.mysql5.6.gtid.一主两从架构,目前还没有实现读写分离,以后架构升级,可能代 ...

  8. 抛砖引玉之~sftp

    跨平台系列汇总:http://www.cnblogs.com/dunitian/p/4822808.html#linux XFtp用惯了,没有它就不行了?Linux就不能文件传输了? 答案肯定是~No ...

  9. Python基础总结

      刚学习Python时,边学边总结的,采用思维导图的形式, 适合回顾使用.内容参考<Python:从入门到实践>一书.   再给出一张Datacamp网站上的一张关于Python基础的总 ...

  10. solr6.6教程-基础环境搭建(二)

    在上一篇文章中已经简单介绍了solr在windows的部署,今天我们来下如何新建一个自己core. 1,core理解 如果把solr理解为个数据库的话,那么core可以理解为数据库中的一张表,其实就是 ...