譯自Change DCB fields from SerialPort instance C#html
DCB(Device Control Block)在C++ 裏面是用bitfield(位域)表示的,C#沒有bitfield,但有一個枚舉位標誌。C#有本身的方法來設置怎麼存取DCB,並且「SerialStream」裏確實也有「SetDcbFlag」方法(該方法接收2個int
參數)。在研究了.Net源碼後,我建立了一組常量用來等效原來的DCB int字段和他們的新int代碼,我暫時尚未整理出全部位標誌,不過你能夠在DCB文檔裏面查閱它們。由於這是一個Internal方法只對.Net內部程序集可見,因此我在獲取該方法時用到了System.Reflection(反射)。windows
下面就是代碼,總體上是一個.Net 2.0+SerialPort
的擴展類。擴展類中有三個方法,SetField(string name, object value)
方法用來設置全部不是以'f'開頭的位,SetFlag(int Flag, int Value)
方法用來處理以'f'開頭的位(我提供了一個針對Flag
參數的const列表),最後UpdateComm()
方法用來在你更改DCB後刷新串口鏈接,它原本是SetField
方法的一部分,可是若是設置DCB時每次都調用它,那花的時間就有點長了。ide
注意:
確保開啓串口鏈接後,再使用這些方法。函數
用法:ui
SerialPort COM = new SerialPort("COM7"); COM.Open(); COM.DiscardInBuffer(); COM.DiscardOutBuffer(); COM.SetFlag(FBINARY, 1); COM.SetFlag(FPARITY, 0); COM.SetFlag(FDTRCONTROL, 0x00); COM.SetFlag(FRTSCONTROL, 0x01); COM.SetField("BaudRate", (UInt32)115200); COM.SetField("StopBits", (byte)0); COM.SetField("ByteSize", (byte)8); COM.SetField("Parity", (byte)0); COM.SetField("XonChar", (byte)0x11); COM.SetField("XoffChar", (byte)0x13); COM.SetField("EvtChar", (byte)0x1A); COM.SetField("XonLim", (ushort)256); COM.SetField("XoffLim", (ushort)256); COM.UpdateComm(); /* Do Stuff */ COM.Close();
常量:this
internal const int FBINARY = 0; internal const int FPARITY = 1; internal const int FOUTXCTSFLOW = 2; internal const int FOUTXDSRFLOW = 3; internal const int FDTRCONTROL = 4; internal const int FDSRSENSITIVITY = 6; internal const int FTXCONTINUEONXOFF = 7; internal const int FOUTX = 8; internal const int FINX = 9; internal const int FERRORCHAR = 10; internal const int FNULL = 11; internal const int FRTSCONTROL = 12; internal const int FABORTONOERROR = 14; internal const int FDUMMY2 = 15;
最後,擴展類spa
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); }
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 int
s.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..net
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.code
NOTE:
The serial port must be opened before using any of these methods!
Usage:
略
Constants:
略
And finally, the extension class:
略htm
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 | 保留,未啓用 |