C# SerialPort自定義串口DCB



譯自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, 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();

常量:

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;

最后,擴展類

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   保留,未啟用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM