以最后決定用純API函數實現串口讀寫。
先從網上搜索相關代碼(關鍵字:C# API 串口),發現網上相關的資料大約來源于一個版本,那就是所謂的msdn提供的樣例代碼(msdn的具體出處,我沒有考證),其它的代碼大都是它的變種。
其實這個示例代碼是有問題的,也就是說DCB結構體聲明的有問題,雖然該代碼可以正常通信,不過如果你設置了奇偶校驗的話,你會發現奇偶校驗無效。
VC中的DCB結構聲明如下:
typedef struct _DCB {
??? DWORD DCBlength;????? /* sizeof(DCB)???????????????????? */
??? DWORD BaudRate;?????? /* Baudrate at which running?????? */
??? DWORD fBinary: 1;???? /* Binary Mode (skip EOF check)??? */
??? DWORD fParity: 1;???? /* Enable parity checking????????? */
??? DWORD fOutxCtsFlow:1; /* CTS handshaking on output?????? */
??? DWORD fOutxDsrFlow:1; /* DSR handshaking on output?????? */
??? DWORD fDtrControl:2; /* DTR Flow control??????????????? */
??? DWORD fDsrSensitivity:1; /* DSR Sensitivity????????????? */
??? DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */
??? DWORD fOutX: 1;?????? /* Enable output X-ON/X-OFF??????? */
??? DWORD fInX: 1;??????? /* Enable input X-ON/X-OFF???????? */
??? DWORD fErrorChar: 1; /* Enable Err Replacement????????? */
??? DWORD fNull: 1;?????? /* Enable Null stripping?????????? */
??? DWORD fRtsControl:2; /* Rts Flow control??????????????? */
??? DWORD fAbortOnError:1; /* Abort all reads and writes on Error */
??? DWORD fDummy2:17;???? /* Reserved??????????????????????? */
??? WORD wReserved;?????? /* Not currently used????????????? */
??? WORD XonLim;????????? /* Transmit X-ON threshold???????? */
??? WORD XoffLim;???????? /* Transmit X-OFF threshold??????? */
??? BYTE ByteSize;??????? /* Number of bits/byte, 4-8??????? */
??? BYTE Parity;????????? /* 0-4=None,Odd,Even,Mark,Space??? */
??? BYTE StopBits;??????? /* 0,1,2 = 1, 1.5, 2?????????????? */
??? char XonChar;???????? /* Tx and Rx X-ON character??????? */
??? char XoffChar;??????? /* Tx and Rx X-OFF character?????? */
??? char ErrorChar;?????? /* Error replacement char????????? */
??? char EofChar;???????? /* End of Input character????????? */
??? char EvtChar;???????? /* Received Event character??????? */
??? WORD wReserved1;????? /* Fill for now.?????????????????? */
} DCB, *LPDCB;
?
有問題的代碼DCB結構聲明如下:
[StructLayout(LayoutKind.Sequential)]
??????? public struct DCB
??????? {
??????????? public int DCBlength;
??????????? public int BaudRate;
??????????? public int fBinary;
??????????? public int fParity;
??????????? public int fOutxCtsFlow;
??????????? public int fOutxDsrFlow;
??????????? public int fDtrControl;
??????????? public int fDsrSensitivity;
??????????? public int fTXContinueOnXoff;
??????????? public int fOutX;
??????????? public int fInX;
??????????? public int fErrorChar;
??????????? public int fNull;
??????????? public int fRtsControl;
??????????? public int fAbortOnError;
??????????? public int fDummy2;
??????????? public uint flags;
??????????? public ushort wReserved;
??????????? public ushort XonLim;
??????????? public ushort XoffLim;
??????????? public byte ByteSize;
??????????? public byte Parity;
??????????? public byte StopBits;
??????????? public byte XonChar;
??????????? public byte XoffChar;
??????????? public byte ErrorChar;
??????????? public byte EofChar;
??????????? public byte EvtChar;
??????????? public ushort wReserved1;
??????? }
對C++比較熟悉網友應該知道,結構體中這種格式的聲明,如DWORD fBinary: 1;是以位為單位進行變量設置的,DCB中相關位一共占4個字節,也就是相當于C#中的一個int變量所占的空間。很明顯上面的DCB結構會有問題,實際上后面你設置的串口參數,如奇偶校驗由于偏移有問題,雖然你設置了,其實都沒有設置成功。
其實也不是我說人家的DCB聲明錯了就錯了,在SerialPort類中你就可以找到微軟官方自己的DCB聲明(需要反編譯SerialPort類),聲明如下:
[StructLayout(LayoutKind.Sequential)]
??????? public struct DCB
??????? {
??????????? public int DCBlength;
??????????? public int BaudRate;
??????????? public uint Flags;
??????????? public ushort wReserved;
??????????? public ushort XonLim;
??????????? public ushort XoffLim;
??????????? public byte ByteSize;
??????????? public byte Parity;
??????????? public byte StopBits;
??????????? public byte XonChar;
??????????? public byte XoffChar;
??????????? public byte ErrorChar;
??????????? public byte EofChar;
??????????? public byte EvtChar;
??????????? public ushort wReserved1;
??????? }
并且專門有一個設置位標志的函數,如下:
internal void SetDcbFlag(int whichFlag, int setting)
??????? {
??????????? uint num;
??????????? setting = setting << whichFlag;
??????????? if ((whichFlag == 4) || (whichFlag == 12))
??????????? {
??????????????? num = 3;
??????????? }
??????????? else if (whichFlag == 15)
??????????? {
??????????????? num = 0x1ffff;
??????????? }
??????????? else
??????????? {
??????????????? num = 1;
??????????? }
??????????? dcb.flags &= ~(num << whichFlag);
??????????? dcb.flags |= (uint)setting;
??????? }
經過修改能正確運行的API代碼如下(注意,由于我是在WinCE平臺上運行,所以DLL的路徑為" //windows//coredll.dll",你修改為"kernel32"后即可在PC機使用):
///<summary>
??? /// API串口類 葉帆修改 http://blog.csdn.net/yefanqiu
??? ///</summary>
??? public class CommPort
??? {
??????? ///<summary>
??????? ///端口名稱(COM1,COM2...COM4...)
??????? ///</summary>
??????? public string Port = "COM1:";
??????? ///<summary>
??????? ///波特率9600
??????? ///</summary>
??????? public int BaudRate = 9600;
??????? ///<summary>
??????? ///數據位4-8
??????? ///</summary>
??????? public byte ByteSize = 8; //4-8
??????? ///<summary>
??????? ///奇偶校驗0-4=no,odd,even,mark,space
??????? ///</summary>
??????? public byte Parity = 0;?? //0-4=no,odd,even,mark,space
??????? ///<summary>
??????? ///停止位
??????? ///</summary>
??????? public byte StopBits = 0;?? //0,1,2 = 1, 1.5, 2
??????? ///<summary>
??????? ///超時長
??????? ///</summary>
??????? public int ReadTimeout = 200;
??????? ///<summary>
??????? ///串口是否已經打開
??????? ///</summary>
??????? public bool Opened = false;
??????? ///<summary>
??????? /// COM口句柄
??????? ///</summary>
??????? private int hComm = -1;
?
??????? #region "API相關定義"
??????? private const string DLLPATH = " //windows//coredll.dll"; // "kernel32";
?
??????? ///<summary>
??????? /// WINAPI常量,寫標志
??????? ///</summary>
??????? private const uint GENERIC_READ = 0x80000000;
??????? ///<summary>
??????? /// WINAPI常量,讀標志
??????? ///</summary>
??????? private const uint GENERIC_WRITE = 0x40000000;
??????? ///<summary>
??????? /// WINAPI常量,打開已存在
??????? ///</summary>
??????? private const int OPEN_EXISTING = 3;
??????? ///<summary>
??????? /// WINAPI常量,無效句柄
??????? ///</summary>
??????? private const int INVALID_HANDLE_VALUE = -1;
?
??????? private const int PURGE_RXABORT = 0x2;
??????? private const int PURGE_RXCLEAR = 0x8;
??????? private const int PURGE_TXABORT = 0x1;
??????? private const int PURGE_TXCLEAR = 0x4;
?
??????? ///<summary>
??????? ///設備控制塊結構體類型
??????? ///</summary>
??????? [StructLayout(LayoutKind.Sequential)]
??????? public struct DCB
??????? {
?????????? ///<summary>
??????????? /// DCB長度
??????????? ///</summary>
??????????? public int DCBlength;
??????????? ///<summary>
??????????? ///指定當前波特率
??????????? ///</summary>
??????????? public int BaudRate;
??????????? ///<summary>
??????????? ///標志位
??????????? ///</summary>
??????????? public uint flags;
??????????? ///<summary>
??????????? ///未使用,必須為0
??????????? ///</summary>
??????????? public ushort wReserved;
??????????? ///<summary>
??????????? ///指定在XON字符發送這前接收緩沖區中可允許的最小字節數
??????????? ///</summary>
??????????? public ushort XonLim;
??????????? ///<summary>
??????????? ///指定在XOFF字符發送這前接收緩沖區中可允許的最小字節數
??????????? ///</summary>
??????????? public ushort XoffLim;
??????????? ///<summary>
??????????? ///指定端口當前使用的數據位
??????????? ///</summary>
??????????? public byte ByteSize;
??????????? ///<summary>
??????????? ///指定端口當前使用的奇偶校驗方法,可能為:EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY 0-4=no,odd,even,mark,space
??????????? ///</summary>
??????????? public byte Parity;
??????????? ///<summary>
??????????? ///指定端口當前使用的停止位數,可能為:ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS 0,1,2 = 1, 1.5, 2
??????????? ///</summary>
??????????? public byte StopBits;
??????????? ///<summary>
??????????? ///指定用于發送和接收字符XON的值 Tx and Rx XON character
??????????? ///</summary>
??????????? public byte XonChar;
??????????? ///<summary>
??????????? ///指定用于發送和接收字符XOFF值 Tx and Rx XOFF character
??????????? ///</summary>
??????????? public byte XoffChar;
??????????? ///<summary>
??????????? ///本字符用來代替接收到的奇偶校驗發生錯誤時的值
??????????? ///</summary>
??????????? public byte ErrorChar;
??????????? ///<summary>
??????????? ///當沒有使用二進制模式時,本字符可用來指示數據的結束
??????????? ///</summary>
??????????? public byte EofChar;
??????????? ///<summary>
??????????? ///當接收到此字符時,會產生一個事件
??????????? ///</summary>
??????????? public byte EvtChar;
??????????? ///<summary>
??????????? ///未使用
??????????? ///</summary>
??????????? public ushort wReserved1;
??????? }
?
??????? ///<summary>
??????? ///串口超時時間結構體類型
??????? ///</summary>
??????? [StructLayout(LayoutKind.Sequential)]
??????? private struct COMMTIMEOUTS
??????? {
??????????? public int ReadIntervalTimeout;
??????????? public int ReadTotalTimeoutMultiplier;
??????????? public int ReadTotalTimeoutConstant;
??????????? public int WriteTotalTimeoutMultiplier;
??????????? public int WriteTotalTimeoutConstant;
??????? }
?
??????? ///<summary>
??????? ///溢出緩沖區結構體類型
??????? ///</summary>
??????? [StructLayout(LayoutKind.Sequential)]
??????? private struct OVERLAPPED
??????? {
??????????? public int Internal;
??????????? public int InternalHigh;
??????????? public int Offset;
??????????? public int OffsetHigh;
??????????? public int hEvent;
??????? }
?
??????? ///<summary>
??????? ///打開串口
??????? ///</summary>
??????? ///<param name="lpFileName">要打開的串口名稱</param>
??????? ///<param name="dwDesiredAccess">指定串口的訪問方式,一般設置為可讀可寫方式</param>
??????? ///<param name="dwShareMode">指定串口的共享模式,串口不能共享,所以設置為0</param>
??????? ///<param name="lpSecurityAttributes">設置串口的安全屬性,WIN9X下不支持,應設為NULL</param>
??????? ///<param name="dwCreationDisposition">對于串口通信,創建方式只能為OPEN_EXISTING</param>
??????? ///<param name="dwFlagsAndAttributes">指定串口屬性與標志,設置為FILE_FLAG_OVERLAPPED(重疊I/O操作),指定串口以異步方式通信</param>
??????? ///<param name="hTemplateFile">對于串口通信必須設置為NULL</param>
??????? [DllImport(DLLPATH)]
??????? private static extern int CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode,
??????? int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);
?
??????? ///<summary>
??????? ///得到串口狀態
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpDCB">設備控制塊DCB</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool GetCommState(int hFile, ref DCB lpDCB);
?
??????? ///<summary>
??????? ///建立串口設備控制塊(嵌入版沒有)
??????? ///</summary>
??????? ///<param name="lpDef">設備控制字符串</param>
??????? ///<param name="lpDCB">設備控制塊</param>
??????? //[DllImport(DLLPATH)]
??????? //private static extern bool BuildCommDCB(string lpDef, ref DCB lpDCB);
?
??????? ///<summary>
??????? ///設置串口狀態
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpDCB">設備控制塊</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool SetCommState(int hFile, ref DCB lpDCB);
?
??????? ///<summary>
??????? ///讀取串口超時時間
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpCommTimeouts">超時時間</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool GetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
?
??????? ///<summary>
??????? ///設置串口超時時間
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpCommTimeouts">超時時間</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool SetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
?
??????? ///<summary>
??????? ///讀取串口數據
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpBuffer">數據緩沖區</param>
??????? ///<param name="nNumberOfBytesToRead">多少字節等待讀取</param>
??????? ///<param name="lpNumberOfBytesRead">讀取多少字節</param>
??????? ///<param name="lpOverlapped">溢出緩沖區</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool ReadFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToRead,
??????? ref int lpNumberOfBytesRead, ref OVERLAPPED lpOverlapped);
?
??????? ///<summary>
??????? ///寫串口數據
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpBuffer">數據緩沖區</param>
??????? ///<param name="nNumberOfBytesToWrite">多少字節等待寫入</param>
??????? ///<param name="lpNumberOfBytesWritten">已經寫入多少字節</param>
??????? ///<param name="lpOverlapped">溢出緩沖區</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool WriteFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToWrite,
??????? ref int lpNumberOfBytesWritten, ref OVERLAPPED lpOverlapped);
?
??????? [DllImport(DLLPATH, SetLastError = true)]
??????? private static extern bool FlushFileBuffers(int hFile);
?
??????? [DllImport(DLLPATH, SetLastError = true)]
??????? private static extern bool PurgeComm(int hFile, uint dwFlags);
?
??????? ///<summary>
??????? ///關閉串口
??????? ///</summary>
??????? ///<param name="hObject">通信設備句柄</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool CloseHandle(int hObject);
?
??????? ///<summary>
??????? ///得到串口最后一次返回的錯誤
??????? ///</summary>
??????? [DllImport(DLLPATH)]
??????? private static extern uint GetLastError();
??????? #endregion
?
??????? ///<summary>
??????? ///設置DCB標志位
??????? ///</summary>
??????? ///<param name="whichFlag"></param>
??????? ///<param name="setting"></param>
??????? ///<param name="dcb"></param>
??????? internal void SetDcbFlag(int whichFlag, int setting, DCB dcb)
??????? {
??????????? uint num;
??????????? setting = setting << whichFlag;
??????????? if ((whichFlag == 4) || (whichFlag == 12))
??????????? {
??????????????? num = 3;
??????????? }
??????????? else if (whichFlag == 15)
??????????? {
??????????????? num = 0x1ffff;
??????????? }
??????????? else
??????????? {
??????????????? num = 1;
??????????? }
??????????? dcb.flags &= ~(num << whichFlag);
??????????? dcb.flags |= (uint)setting;
??????? }
?
??????? ///<summary>
??????? ///建立與串口的連接
??????? ///</summary>
??????? public int Open()
??????? {
??????????? DCB dcb = new DCB();
??????????? COMMTIMEOUTS ctoCommPort = new COMMTIMEOUTS();
?
??????????? // 打開串口
??????????? hComm = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
??????????? if (hComm == INVALID_HANDLE_VALUE)
??????????? {
??????????????? return -1;
??????????? }
??????????? // 設置通信超時時間
??????????? GetCommTimeouts(hComm, ref ctoCommPort);
??????????? ctoCommPort.ReadTotalTimeoutConstant = ReadTimeout;
??????????? ctoCommPort.ReadTotalTimeoutMultiplier = 0;
??????????? ctoCommPort.WriteTotalTimeoutMultiplier = 0;
??????????? ctoCommPort.WriteTotalTimeoutConstant = 0;
??????????? SetCommTimeouts(hComm, ref ctoCommPort);
?
??????????? //設置串口參數
??????????? GetCommState(hComm, ref dcb);
??????????? dcb.DCBlength = Marshal.SizeOf(dcb);
??????????? dcb.BaudRate = BaudRate;
??????????? dcb.flags = 0;
??????????? dcb.ByteSize = (byte)ByteSize;
??????????? dcb.StopBits = StopBits;
??????????? dcb.Parity = (byte)Parity;
?
??????????? //------------------------------
??????????? SetDcbFlag(0, 1, dcb);??????????? //二進制方式
??????????? SetDcbFlag(1, (Parity == 0) ? 0 : 1, dcb);
??????????? SetDcbFlag(2, 0, dcb);??????????? //不用CTS檢測發送流控制
??????????? SetDcbFlag(3, 0, dcb);??????????? //不用DSR檢測發送流控制
??????????? SetDcbFlag(4, 0, dcb);??????????? //禁止DTR流量控制
??????????? SetDcbFlag(6, 0, dcb);??????????? //對DTR信號線不敏感
??????????? SetDcbFlag(9, 1, dcb);??????????? //檢測接收緩沖區
??????????? SetDcbFlag(8, 0, dcb);??????????? //不做發送字符控制
??????????? SetDcbFlag(10, 0, dcb);?????????? //是否用指定字符替換校驗錯的字符
??????????? SetDcbFlag(11, 0, dcb);?????????? //保留NULL字符
??????????? SetDcbFlag(12, 0, dcb);?????????? //允許RTS流量控制
??????????? SetDcbFlag(14, 0, dcb);?????????? //發送錯誤后,繼續進行下面的讀寫操作
??????????? //--------------------------------
??????????? dcb.wReserved = 0;?????????????????????? //沒有使用,必須為0??????
??????????? dcb.XonLim = 0;????????????????????????? //指定在XOFF字符發送之前接收到緩沖區中可允許的最小字節數
??????????? dcb.XoffLim = 0;???????????????????????? //指定在XOFF字符發送之前緩沖區中可允許的最小可用字節數
??????????? dcb.XonChar = 0;???????????????????????? //發送和接收的XON字符
??????????? dcb.XoffChar = 0;??????????????????????? //發送和接收的XOFF字符
??????????? dcb.ErrorChar = 0;?????????????????????? //代替接收到奇偶校驗錯誤的字符
??????????? dcb.EofChar = 0;???????????????????????? //用來表示數據的結束?????
??????????? dcb.EvtChar = 0;???????????????????????? //事件字符,接收到此字符時,會產生一個事件???????
??????????? dcb.wReserved1 = 0;????????????????????? //沒有使用
?
??????????? if (!SetCommState(hComm, ref dcb))
??????????? {
??????????????? return -2;
??????????? }
??????????? Opened = true;
??????????? return 0;
??????? }
??????? ///<summary>
??????? ///關閉串口,結束通訊
??????? ///</summary>
??????? public void Close()
??????? {
??????????? if (hComm != INVALID_HANDLE_VALUE)
??????????? {
??????????????? CloseHandle(hComm);
??????????? }
??????? }
??????? ///<summary>
??????? ///讀取串口返回的數據
??????? ///</summary>
??????? ///<param name="NumBytes">數據長度</param>
??????? public int Read(ref byte[] bytData, int NumBytes)
??????? {
??????????? if (hComm != INVALID_HANDLE_VALUE)
??????????? {
??????????????? OVERLAPPED ovlCommPort = new OVERLAPPED();
??????????????? int BytesRead = 0;
??????????????? ReadFile(hComm, bytData, NumBytes, ref BytesRead, ref ovlCommPort);
??????????????? return BytesRead;
??????????? }
??????????? else
??????????? {
??????????????? return -1;
??????????? }
??????? }
?
??????? ///<summary>
??????? ///向串口寫數據
??????? ///</summary>
??????? ///<param name="WriteBytes">數據數組</param>
??????? public int Write(byte[] WriteBytes, int intSize)
??????? {
??????????? if (hComm != INVALID_HANDLE_VALUE)
??????????? {
??????????????? OVERLAPPED ovlCommPort = new OVERLAPPED();
??????????????? int BytesWritten = 0;
??????????????? WriteFile(hComm, WriteBytes, intSize, ref BytesWritten, ref ovlCommPort);
??????????????? return BytesWritten;
??????????? }
??????????? else
??????????? {
??????????????? return -1;
??????????? }
??????? }
?
??????? ///<summary>
??????? ///清除接收緩沖區
??????? ///</summary>
??????? ///<returns></returns>
??????? public void ClearReceiveBuf()
??????? {
??????????? if (hComm != INVALID_HANDLE_VALUE)
??????????? {
??????????????? PurgeComm(hComm, PURGE_RXABORT | PURGE_RXCLEAR);
??????????? }
??????? }
?
??????? ///<summary>
??????? ///清除發送緩沖區
??????? ///</summary>
??????? public void ClearSendBuf()
??????? {
??????????? if (hComm != INVALID_HANDLE_VALUE)
??????????? {
??????????????? PurgeComm(hComm, PURGE_TXABORT | PURGE_TXCLEAR);
??????????? }
??????? }
?}
后記:我的串口程序修改為API方式后,實際發現與SerialPort類遇到同樣的問題,所以SerialPort類還是值得信任的。該API方式的代碼在WinCE平臺和PC平臺都調試通過。?
先從網上搜索相關代碼(關鍵字:C# API 串口),發現網上相關的資料大約來源于一個版本,那就是所謂的msdn提供的樣例代碼(msdn的具體出處,我沒有考證),其它的代碼大都是它的變種。
其實這個示例代碼是有問題的,也就是說DCB結構體聲明的有問題,雖然該代碼可以正常通信,不過如果你設置了奇偶校驗的話,你會發現奇偶校驗無效。
VC中的DCB結構聲明如下:
typedef struct _DCB {
??? DWORD DCBlength;????? /* sizeof(DCB)???????????????????? */
??? DWORD BaudRate;?????? /* Baudrate at which running?????? */
??? DWORD fBinary: 1;???? /* Binary Mode (skip EOF check)??? */
??? DWORD fParity: 1;???? /* Enable parity checking????????? */
??? DWORD fOutxCtsFlow:1; /* CTS handshaking on output?????? */
??? DWORD fOutxDsrFlow:1; /* DSR handshaking on output?????? */
??? DWORD fDtrControl:2; /* DTR Flow control??????????????? */
??? DWORD fDsrSensitivity:1; /* DSR Sensitivity????????????? */
??? DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */
??? DWORD fOutX: 1;?????? /* Enable output X-ON/X-OFF??????? */
??? DWORD fInX: 1;??????? /* Enable input X-ON/X-OFF???????? */
??? DWORD fErrorChar: 1; /* Enable Err Replacement????????? */
??? DWORD fNull: 1;?????? /* Enable Null stripping?????????? */
??? DWORD fRtsControl:2; /* Rts Flow control??????????????? */
??? DWORD fAbortOnError:1; /* Abort all reads and writes on Error */
??? DWORD fDummy2:17;???? /* Reserved??????????????????????? */
??? WORD wReserved;?????? /* Not currently used????????????? */
??? WORD XonLim;????????? /* Transmit X-ON threshold???????? */
??? WORD XoffLim;???????? /* Transmit X-OFF threshold??????? */
??? BYTE ByteSize;??????? /* Number of bits/byte, 4-8??????? */
??? BYTE Parity;????????? /* 0-4=None,Odd,Even,Mark,Space??? */
??? BYTE StopBits;??????? /* 0,1,2 = 1, 1.5, 2?????????????? */
??? char XonChar;???????? /* Tx and Rx X-ON character??????? */
??? char XoffChar;??????? /* Tx and Rx X-OFF character?????? */
??? char ErrorChar;?????? /* Error replacement char????????? */
??? char EofChar;???????? /* End of Input character????????? */
??? char EvtChar;???????? /* Received Event character??????? */
??? WORD wReserved1;????? /* Fill for now.?????????????????? */
} DCB, *LPDCB;
?
有問題的代碼DCB結構聲明如下:
[StructLayout(LayoutKind.Sequential)]
??????? public struct DCB
??????? {
??????????? public int DCBlength;
??????????? public int BaudRate;
??????????? public int fBinary;
??????????? public int fParity;
??????????? public int fOutxCtsFlow;
??????????? public int fOutxDsrFlow;
??????????? public int fDtrControl;
??????????? public int fDsrSensitivity;
??????????? public int fTXContinueOnXoff;
??????????? public int fOutX;
??????????? public int fInX;
??????????? public int fErrorChar;
??????????? public int fNull;
??????????? public int fRtsControl;
??????????? public int fAbortOnError;
??????????? public int fDummy2;
??????????? public uint flags;
??????????? public ushort wReserved;
??????????? public ushort XonLim;
??????????? public ushort XoffLim;
??????????? public byte ByteSize;
??????????? public byte Parity;
??????????? public byte StopBits;
??????????? public byte XonChar;
??????????? public byte XoffChar;
??????????? public byte ErrorChar;
??????????? public byte EofChar;
??????????? public byte EvtChar;
??????????? public ushort wReserved1;
??????? }
對C++比較熟悉網友應該知道,結構體中這種格式的聲明,如DWORD fBinary: 1;是以位為單位進行變量設置的,DCB中相關位一共占4個字節,也就是相當于C#中的一個int變量所占的空間。很明顯上面的DCB結構會有問題,實際上后面你設置的串口參數,如奇偶校驗由于偏移有問題,雖然你設置了,其實都沒有設置成功。
其實也不是我說人家的DCB聲明錯了就錯了,在SerialPort類中你就可以找到微軟官方自己的DCB聲明(需要反編譯SerialPort類),聲明如下:
[StructLayout(LayoutKind.Sequential)]
??????? public struct DCB
??????? {
??????????? public int DCBlength;
??????????? public int BaudRate;
??????????? public uint Flags;
??????????? public ushort wReserved;
??????????? public ushort XonLim;
??????????? public ushort XoffLim;
??????????? public byte ByteSize;
??????????? public byte Parity;
??????????? public byte StopBits;
??????????? public byte XonChar;
??????????? public byte XoffChar;
??????????? public byte ErrorChar;
??????????? public byte EofChar;
??????????? public byte EvtChar;
??????????? public ushort wReserved1;
??????? }
并且專門有一個設置位標志的函數,如下:
internal void SetDcbFlag(int whichFlag, int setting)
??????? {
??????????? uint num;
??????????? setting = setting << whichFlag;
??????????? if ((whichFlag == 4) || (whichFlag == 12))
??????????? {
??????????????? num = 3;
??????????? }
??????????? else if (whichFlag == 15)
??????????? {
??????????????? num = 0x1ffff;
??????????? }
??????????? else
??????????? {
??????????????? num = 1;
??????????? }
??????????? dcb.flags &= ~(num << whichFlag);
??????????? dcb.flags |= (uint)setting;
??????? }
經過修改能正確運行的API代碼如下(注意,由于我是在WinCE平臺上運行,所以DLL的路徑為" //windows//coredll.dll",你修改為"kernel32"后即可在PC機使用):
///<summary>
??? /// API串口類 葉帆修改 http://blog.csdn.net/yefanqiu
??? ///</summary>
??? public class CommPort
??? {
??????? ///<summary>
??????? ///端口名稱(COM1,COM2...COM4...)
??????? ///</summary>
??????? public string Port = "COM1:";
??????? ///<summary>
??????? ///波特率9600
??????? ///</summary>
??????? public int BaudRate = 9600;
??????? ///<summary>
??????? ///數據位4-8
??????? ///</summary>
??????? public byte ByteSize = 8; //4-8
??????? ///<summary>
??????? ///奇偶校驗0-4=no,odd,even,mark,space
??????? ///</summary>
??????? public byte Parity = 0;?? //0-4=no,odd,even,mark,space
??????? ///<summary>
??????? ///停止位
??????? ///</summary>
??????? public byte StopBits = 0;?? //0,1,2 = 1, 1.5, 2
??????? ///<summary>
??????? ///超時長
??????? ///</summary>
??????? public int ReadTimeout = 200;
??????? ///<summary>
??????? ///串口是否已經打開
??????? ///</summary>
??????? public bool Opened = false;
??????? ///<summary>
??????? /// COM口句柄
??????? ///</summary>
??????? private int hComm = -1;
?
??????? #region "API相關定義"
??????? private const string DLLPATH = " //windows//coredll.dll"; // "kernel32";
?
??????? ///<summary>
??????? /// WINAPI常量,寫標志
??????? ///</summary>
??????? private const uint GENERIC_READ = 0x80000000;
??????? ///<summary>
??????? /// WINAPI常量,讀標志
??????? ///</summary>
??????? private const uint GENERIC_WRITE = 0x40000000;
??????? ///<summary>
??????? /// WINAPI常量,打開已存在
??????? ///</summary>
??????? private const int OPEN_EXISTING = 3;
??????? ///<summary>
??????? /// WINAPI常量,無效句柄
??????? ///</summary>
??????? private const int INVALID_HANDLE_VALUE = -1;
?
??????? private const int PURGE_RXABORT = 0x2;
??????? private const int PURGE_RXCLEAR = 0x8;
??????? private const int PURGE_TXABORT = 0x1;
??????? private const int PURGE_TXCLEAR = 0x4;
?
??????? ///<summary>
??????? ///設備控制塊結構體類型
??????? ///</summary>
??????? [StructLayout(LayoutKind.Sequential)]
??????? public struct DCB
??????? {
?????????? ///<summary>
??????????? /// DCB長度
??????????? ///</summary>
??????????? public int DCBlength;
??????????? ///<summary>
??????????? ///指定當前波特率
??????????? ///</summary>
??????????? public int BaudRate;
??????????? ///<summary>
??????????? ///標志位
??????????? ///</summary>
??????????? public uint flags;
??????????? ///<summary>
??????????? ///未使用,必須為0
??????????? ///</summary>
??????????? public ushort wReserved;
??????????? ///<summary>
??????????? ///指定在XON字符發送這前接收緩沖區中可允許的最小字節數
??????????? ///</summary>
??????????? public ushort XonLim;
??????????? ///<summary>
??????????? ///指定在XOFF字符發送這前接收緩沖區中可允許的最小字節數
??????????? ///</summary>
??????????? public ushort XoffLim;
??????????? ///<summary>
??????????? ///指定端口當前使用的數據位
??????????? ///</summary>
??????????? public byte ByteSize;
??????????? ///<summary>
??????????? ///指定端口當前使用的奇偶校驗方法,可能為:EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY 0-4=no,odd,even,mark,space
??????????? ///</summary>
??????????? public byte Parity;
??????????? ///<summary>
??????????? ///指定端口當前使用的停止位數,可能為:ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS 0,1,2 = 1, 1.5, 2
??????????? ///</summary>
??????????? public byte StopBits;
??????????? ///<summary>
??????????? ///指定用于發送和接收字符XON的值 Tx and Rx XON character
??????????? ///</summary>
??????????? public byte XonChar;
??????????? ///<summary>
??????????? ///指定用于發送和接收字符XOFF值 Tx and Rx XOFF character
??????????? ///</summary>
??????????? public byte XoffChar;
??????????? ///<summary>
??????????? ///本字符用來代替接收到的奇偶校驗發生錯誤時的值
??????????? ///</summary>
??????????? public byte ErrorChar;
??????????? ///<summary>
??????????? ///當沒有使用二進制模式時,本字符可用來指示數據的結束
??????????? ///</summary>
??????????? public byte EofChar;
??????????? ///<summary>
??????????? ///當接收到此字符時,會產生一個事件
??????????? ///</summary>
??????????? public byte EvtChar;
??????????? ///<summary>
??????????? ///未使用
??????????? ///</summary>
??????????? public ushort wReserved1;
??????? }
?
??????? ///<summary>
??????? ///串口超時時間結構體類型
??????? ///</summary>
??????? [StructLayout(LayoutKind.Sequential)]
??????? private struct COMMTIMEOUTS
??????? {
??????????? public int ReadIntervalTimeout;
??????????? public int ReadTotalTimeoutMultiplier;
??????????? public int ReadTotalTimeoutConstant;
??????????? public int WriteTotalTimeoutMultiplier;
??????????? public int WriteTotalTimeoutConstant;
??????? }
?
??????? ///<summary>
??????? ///溢出緩沖區結構體類型
??????? ///</summary>
??????? [StructLayout(LayoutKind.Sequential)]
??????? private struct OVERLAPPED
??????? {
??????????? public int Internal;
??????????? public int InternalHigh;
??????????? public int Offset;
??????????? public int OffsetHigh;
??????????? public int hEvent;
??????? }
?
??????? ///<summary>
??????? ///打開串口
??????? ///</summary>
??????? ///<param name="lpFileName">要打開的串口名稱</param>
??????? ///<param name="dwDesiredAccess">指定串口的訪問方式,一般設置為可讀可寫方式</param>
??????? ///<param name="dwShareMode">指定串口的共享模式,串口不能共享,所以設置為0</param>
??????? ///<param name="lpSecurityAttributes">設置串口的安全屬性,WIN9X下不支持,應設為NULL</param>
??????? ///<param name="dwCreationDisposition">對于串口通信,創建方式只能為OPEN_EXISTING</param>
??????? ///<param name="dwFlagsAndAttributes">指定串口屬性與標志,設置為FILE_FLAG_OVERLAPPED(重疊I/O操作),指定串口以異步方式通信</param>
??????? ///<param name="hTemplateFile">對于串口通信必須設置為NULL</param>
??????? [DllImport(DLLPATH)]
??????? private static extern int CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode,
??????? int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);
?
??????? ///<summary>
??????? ///得到串口狀態
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpDCB">設備控制塊DCB</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool GetCommState(int hFile, ref DCB lpDCB);
?
??????? ///<summary>
??????? ///建立串口設備控制塊(嵌入版沒有)
??????? ///</summary>
??????? ///<param name="lpDef">設備控制字符串</param>
??????? ///<param name="lpDCB">設備控制塊</param>
??????? //[DllImport(DLLPATH)]
??????? //private static extern bool BuildCommDCB(string lpDef, ref DCB lpDCB);
?
??????? ///<summary>
??????? ///設置串口狀態
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpDCB">設備控制塊</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool SetCommState(int hFile, ref DCB lpDCB);
?
??????? ///<summary>
??????? ///讀取串口超時時間
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpCommTimeouts">超時時間</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool GetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
?
??????? ///<summary>
??????? ///設置串口超時時間
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpCommTimeouts">超時時間</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool SetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
?
??????? ///<summary>
??????? ///讀取串口數據
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpBuffer">數據緩沖區</param>
??????? ///<param name="nNumberOfBytesToRead">多少字節等待讀取</param>
??????? ///<param name="lpNumberOfBytesRead">讀取多少字節</param>
??????? ///<param name="lpOverlapped">溢出緩沖區</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool ReadFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToRead,
??????? ref int lpNumberOfBytesRead, ref OVERLAPPED lpOverlapped);
?
??????? ///<summary>
??????? ///寫串口數據
??????? ///</summary>
??????? ///<param name="hFile">通信設備句柄</param>
??????? ///<param name="lpBuffer">數據緩沖區</param>
??????? ///<param name="nNumberOfBytesToWrite">多少字節等待寫入</param>
??????? ///<param name="lpNumberOfBytesWritten">已經寫入多少字節</param>
??????? ///<param name="lpOverlapped">溢出緩沖區</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool WriteFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToWrite,
??????? ref int lpNumberOfBytesWritten, ref OVERLAPPED lpOverlapped);
?
??????? [DllImport(DLLPATH, SetLastError = true)]
??????? private static extern bool FlushFileBuffers(int hFile);
?
??????? [DllImport(DLLPATH, SetLastError = true)]
??????? private static extern bool PurgeComm(int hFile, uint dwFlags);
?
??????? ///<summary>
??????? ///關閉串口
??????? ///</summary>
??????? ///<param name="hObject">通信設備句柄</param>
??????? [DllImport(DLLPATH)]
??????? private static extern bool CloseHandle(int hObject);
?
??????? ///<summary>
??????? ///得到串口最后一次返回的錯誤
??????? ///</summary>
??????? [DllImport(DLLPATH)]
??????? private static extern uint GetLastError();
??????? #endregion
?
??????? ///<summary>
??????? ///設置DCB標志位
??????? ///</summary>
??????? ///<param name="whichFlag"></param>
??????? ///<param name="setting"></param>
??????? ///<param name="dcb"></param>
??????? internal void SetDcbFlag(int whichFlag, int setting, DCB dcb)
??????? {
??????????? uint num;
??????????? setting = setting << whichFlag;
??????????? if ((whichFlag == 4) || (whichFlag == 12))
??????????? {
??????????????? num = 3;
??????????? }
??????????? else if (whichFlag == 15)
??????????? {
??????????????? num = 0x1ffff;
??????????? }
??????????? else
??????????? {
??????????????? num = 1;
??????????? }
??????????? dcb.flags &= ~(num << whichFlag);
??????????? dcb.flags |= (uint)setting;
??????? }
?
??????? ///<summary>
??????? ///建立與串口的連接
??????? ///</summary>
??????? public int Open()
??????? {
??????????? DCB dcb = new DCB();
??????????? COMMTIMEOUTS ctoCommPort = new COMMTIMEOUTS();
?
??????????? // 打開串口
??????????? hComm = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
??????????? if (hComm == INVALID_HANDLE_VALUE)
??????????? {
??????????????? return -1;
??????????? }
??????????? // 設置通信超時時間
??????????? GetCommTimeouts(hComm, ref ctoCommPort);
??????????? ctoCommPort.ReadTotalTimeoutConstant = ReadTimeout;
??????????? ctoCommPort.ReadTotalTimeoutMultiplier = 0;
??????????? ctoCommPort.WriteTotalTimeoutMultiplier = 0;
??????????? ctoCommPort.WriteTotalTimeoutConstant = 0;
??????????? SetCommTimeouts(hComm, ref ctoCommPort);
?
??????????? //設置串口參數
??????????? GetCommState(hComm, ref dcb);
??????????? dcb.DCBlength = Marshal.SizeOf(dcb);
??????????? dcb.BaudRate = BaudRate;
??????????? dcb.flags = 0;
??????????? dcb.ByteSize = (byte)ByteSize;
??????????? dcb.StopBits = StopBits;
??????????? dcb.Parity = (byte)Parity;
?
??????????? //------------------------------
??????????? SetDcbFlag(0, 1, dcb);??????????? //二進制方式
??????????? SetDcbFlag(1, (Parity == 0) ? 0 : 1, dcb);
??????????? SetDcbFlag(2, 0, dcb);??????????? //不用CTS檢測發送流控制
??????????? SetDcbFlag(3, 0, dcb);??????????? //不用DSR檢測發送流控制
??????????? SetDcbFlag(4, 0, dcb);??????????? //禁止DTR流量控制
??????????? SetDcbFlag(6, 0, dcb);??????????? //對DTR信號線不敏感
??????????? SetDcbFlag(9, 1, dcb);??????????? //檢測接收緩沖區
??????????? SetDcbFlag(8, 0, dcb);??????????? //不做發送字符控制
??????????? SetDcbFlag(10, 0, dcb);?????????? //是否用指定字符替換校驗錯的字符
??????????? SetDcbFlag(11, 0, dcb);?????????? //保留NULL字符
??????????? SetDcbFlag(12, 0, dcb);?????????? //允許RTS流量控制
??????????? SetDcbFlag(14, 0, dcb);?????????? //發送錯誤后,繼續進行下面的讀寫操作
??????????? //--------------------------------
??????????? dcb.wReserved = 0;?????????????????????? //沒有使用,必須為0??????
??????????? dcb.XonLim = 0;????????????????????????? //指定在XOFF字符發送之前接收到緩沖區中可允許的最小字節數
??????????? dcb.XoffLim = 0;???????????????????????? //指定在XOFF字符發送之前緩沖區中可允許的最小可用字節數
??????????? dcb.XonChar = 0;???????????????????????? //發送和接收的XON字符
??????????? dcb.XoffChar = 0;??????????????????????? //發送和接收的XOFF字符
??????????? dcb.ErrorChar = 0;?????????????????????? //代替接收到奇偶校驗錯誤的字符
??????????? dcb.EofChar = 0;???????????????????????? //用來表示數據的結束?????
??????????? dcb.EvtChar = 0;???????????????????????? //事件字符,接收到此字符時,會產生一個事件???????
??????????? dcb.wReserved1 = 0;????????????????????? //沒有使用
?
??????????? if (!SetCommState(hComm, ref dcb))
??????????? {
??????????????? return -2;
??????????? }
??????????? Opened = true;
??????????? return 0;
??????? }
??????? ///<summary>
??????? ///關閉串口,結束通訊
??????? ///</summary>
??????? public void Close()
??????? {
??????????? if (hComm != INVALID_HANDLE_VALUE)
??????????? {
??????????????? CloseHandle(hComm);
??????????? }
??????? }
??????? ///<summary>
??????? ///讀取串口返回的數據
??????? ///</summary>
??????? ///<param name="NumBytes">數據長度</param>
??????? public int Read(ref byte[] bytData, int NumBytes)
??????? {
??????????? if (hComm != INVALID_HANDLE_VALUE)
??????????? {
??????????????? OVERLAPPED ovlCommPort = new OVERLAPPED();
??????????????? int BytesRead = 0;
??????????????? ReadFile(hComm, bytData, NumBytes, ref BytesRead, ref ovlCommPort);
??????????????? return BytesRead;
??????????? }
??????????? else
??????????? {
??????????????? return -1;
??????????? }
??????? }
?
??????? ///<summary>
??????? ///向串口寫數據
??????? ///</summary>
??????? ///<param name="WriteBytes">數據數組</param>
??????? public int Write(byte[] WriteBytes, int intSize)
??????? {
??????????? if (hComm != INVALID_HANDLE_VALUE)
??????????? {
??????????????? OVERLAPPED ovlCommPort = new OVERLAPPED();
??????????????? int BytesWritten = 0;
??????????????? WriteFile(hComm, WriteBytes, intSize, ref BytesWritten, ref ovlCommPort);
??????????????? return BytesWritten;
??????????? }
??????????? else
??????????? {
??????????????? return -1;
??????????? }
??????? }
?
??????? ///<summary>
??????? ///清除接收緩沖區
??????? ///</summary>
??????? ///<returns></returns>
??????? public void ClearReceiveBuf()
??????? {
??????????? if (hComm != INVALID_HANDLE_VALUE)
??????????? {
??????????????? PurgeComm(hComm, PURGE_RXABORT | PURGE_RXCLEAR);
??????????? }
??????? }
?
??????? ///<summary>
??????? ///清除發送緩沖區
??????? ///</summary>
??????? public void ClearSendBuf()
??????? {
??????????? if (hComm != INVALID_HANDLE_VALUE)
??????????? {
??????????????? PurgeComm(hComm, PURGE_TXABORT | PURGE_TXCLEAR);
??????????? }
??????? }
?}
后記:我的串口程序修改為API方式后,實際發現與SerialPort類遇到同樣的問題,所以SerialPort類還是值得信任的。該API方式的代碼在WinCE平臺和PC平臺都調試通過。?