在调试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机使用):
-
-
-
-
public class CommPort
- {
-
-
-
-
public string Port = "COM1:";
-
-
-
-
public int BaudRate = 9600;
-
-
-
-
public byte ByteSize = 8;
-
-
-
-
public byte Parity = 0;
-
-
-
-
public byte StopBits = 0;
-
-
-
-
public int ReadTimeout = 200;
-
-
-
-
public bool Opened = false;
-
-
-
-
private int hComm = -1;
-
- #region "API相关定义"
-
private const string DLLPATH = "\\windows\\coredll.dll";
-
-
-
-
-
private const uint GENERIC_READ = 0x80000000;
-
-
-
-
private const uint GENERIC_WRITE = 0x40000000;
-
-
-
-
private const int OPEN_EXISTING = 3;
-
-
-
-
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;
-
-
-
-
- [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;
- }
-
-
-
-
- [StructLayout(LayoutKind.Sequential)]
-
private struct COMMTIMEOUTS
- {
-
public int ReadIntervalTimeout;
-
public int ReadTotalTimeoutMultiplier;
-
public int ReadTotalTimeoutConstant;
-
public int WriteTotalTimeoutMultiplier;
-
public int WriteTotalTimeoutConstant;
- }
-
-
-
-
- [StructLayout(LayoutKind.Sequential)]
-
private struct OVERLAPPED
- {
-
public int Internal;
-
public int InternalHigh;
-
public int Offset;
-
public int OffsetHigh;
-
public int hEvent;
- }
-
-
-
-
-
-
-
-
-
-
-
- [DllImport(DLLPATH)]
-
private static extern int CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode,
-
int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);
-
-
-
-
-
-
- [DllImport(DLLPATH)]
-
private static extern bool GetCommState(int hFile, ref DCB lpDCB);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [DllImport(DLLPATH)]
-
private static extern bool SetCommState(int hFile, ref DCB lpDCB);
-
-
-
-
-
-
- [DllImport(DLLPATH)]
-
private static extern bool GetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
-
-
-
-
-
-
- [DllImport(DLLPATH)]
-
private static extern bool SetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
-
-
-
-
-
-
-
-
-
- [DllImport(DLLPATH)]
-
private static extern bool ReadFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToRead,
-
ref int lpNumberOfBytesRead, ref OVERLAPPED lpOverlapped);
-
-
-
-
-
-
-
-
-
- [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);
-
-
-
-
-
- [DllImport(DLLPATH)]
-
private static extern bool CloseHandle(int hObject);
-
-
-
-
- [DllImport(DLLPATH)]
-
private static extern uint GetLastError();
- #endregion
-
-
-
-
-
-
-
-
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;
- }
-
-
-
-
-
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);
-
SetDcbFlag(3, 0, dcb);
-
SetDcbFlag(4, 0, dcb);
-
SetDcbFlag(6, 0, dcb);
-
SetDcbFlag(9, 1, dcb);
-
SetDcbFlag(8, 0, dcb);
-
SetDcbFlag(10, 0, dcb);
-
SetDcbFlag(11, 0, dcb);
-
SetDcbFlag(12, 0, dcb);
-
SetDcbFlag(14, 0, dcb);
-
-
dcb.wReserved = 0;
-
dcb.XonLim = 0;
-
dcb.XoffLim = 0;
-
dcb.XonChar = 0;
-
dcb.XoffChar = 0;
-
dcb.ErrorChar = 0;
-
dcb.EofChar = 0;
-
dcb.EvtChar = 0;
-
dcb.wReserved1 = 0;
-
-
if (!SetCommState(hComm, ref dcb))
- {
-
return -2;
- }
-
Opened = true;
-
return 0;
- }
-
-
-
-
public void Close()
- {
-
if (hComm != INVALID_HANDLE_VALUE)
- {
- CloseHandle(hComm);
- }
- }
-
-
-
-
-
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;
- }
- }
-
-
-
-
-
-
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;
- }
- }
-
-
-
-
-
-
public void ClearReceiveBuf()
- {
-
if (hComm != INVALID_HANDLE_VALUE)
- {
- PurgeComm(hComm, PURGE_RXABORT | PURGE_RXCLEAR);
- }
- }
-
-
-
-
-
public void ClearSendBuf()
- {
-
if (hComm != INVALID_HANDLE_VALUE)
- {
- PurgeComm(hComm, PURGE_TXABORT | PURGE_TXCLEAR);
- }
- }
- }
后记:我的串口程序修改为API方式后,实际发现与SerialPort类遇到同样的问题,所以SerialPort类还是值得信任的。该API方式的代码在WinCE平台和PC平台都调试通过。
本文转自yefanqiu51CTO博客,原文链接:http://blog.51cto.com/yfsoft/323424,如需转载请自行联系原作者