1. 程式人生 > >C API方式串列埠讀寫

C API方式串列埠讀寫

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

 

在除錯ICU通訊裝置的時候,由於串列埠通訊老出現故障,所以就懷疑CF實現的SerialPort類是否有問題,所以最後決定用純 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字元    &nb