以前串口通信一直用AxInterop.MSCommLib第三方串口通信类,但是发现那个东西用着很麻烦,因为需要设置ActiveX控件的持久状态OcxState,这个东西又蛋疼的一般在Winform的Designer里面,随意调整一下界面的布局,那个OcxState的设定就不见了。。。还有个蛋疼的地方就是打开和关闭串口需要比较久的时间。
于是我就想要自己写一个串口通信类,就叫做SerialCommunication。
首先需要一些DllImport
[DllImport("kernel32.dll")]
private static extern int CreateFile(
string lpFileName, // file name
uint dwDesiredAccess, // access mode
int dwShareMode, // share mode
int lpSecurityAttributes, // SD
int dwCreationDisposition, // how to create
int dwFlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
);
[DllImport("kernel32.dll")]
private static extern bool GetCommState(
int hFile, // handle to communications device
ref Dcb lpDcb // device-control block
);
//[DllImport("kernel32.dll")]
//private static extern bool BuildCommDCB(
// string lpDef, // device-control string
// ref DCB lpDcb // device-control block
// );
[DllImport("kernel32.dll")]
private static extern bool SetCommState(
int hFile, // handle to communications device
ref Dcb lpDcb // device-control block
);
//[DllImport("kernel32.dll")]
//private static extern bool GetCommTimeouts(
// int hFile, // handle to comm device
// ref CommTimeouts lpCommTimeouts // time-out values
// );
[DllImport("kernel32.dll")]
private static extern bool SetCommTimeouts(
int hFile, // handle to comm device
ref CommTimeouts lpCommTimeouts // time-out values
);
[DllImport("kernel32.dll")]
private static extern bool ReadFile(
int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
ref Overlapped lpOverlapped // overlapped buffer
);
[DllImport("kernel32.dll")]
private static extern bool WriteFile(
int hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToWrite, // number of bytes to write
ref int lpNumberOfBytesWritten, // number of bytes written
ref Overlapped lpOverlapped // overlapped buffer
);
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(
int hObject // handle to object
);
//[DllImport("kernel32.dll")]
//private static extern uint GetLastError();
然后需要一些struct
[StructLayout(LayoutKind.Sequential)]
public struct CommTimeouts
{
public UInt32 ReadIntervalTimeout;
public UInt32 ReadTotalTimeoutMultiplier;
public UInt32 ReadTotalTimeoutConstant;
public UInt32 WriteTotalTimeoutMultiplier;
public UInt32 WriteTotalTimeoutConstant;
}
[StructLayout(LayoutKind.Sequential)]
public struct Dcb
{
public Int32 DCBlength;
public Int32 BaudRate;
public Int32 PackedValues;
public Int16 wReserved;
public Int16 XonLim;
public Int16 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 Int16 wReserved1;
}
[StructLayout(LayoutKind.Sequential)]
public struct Overlapped
{
public UIntPtr Internal;
public UIntPtr InternalHigh;
public UInt32 Offset;
public UInt32 OffsetHigh;
public IntPtr hEvent;
}
最后实现开关读写串口即可。开串口只需打开串口,设置串口两步操作,打开串口可能会失败,原因一般是该串口没有设备连接或者正在被其他设备使用中,设置串口也有可能会失败,原因一般是设置的参数不正确或者不够。关串口只需要执行CloseHandle即可
public void Open()
{
// OPEN THE COMM PORT.
_hComm = CreateFile("COM" + PortNum, GenericRead | GenericWrite, 0, 0, OpenExisting, 0, 0);
// IF THE PORT CANNOT BE OPENED, BAIL OUT.
if (_hComm == InvalidHandleValue)
{
throw (new Exception("Port Open Failure"));
}
CommTimeouts ctoCommPort = new CommTimeouts { ReadTotalTimeoutConstant = ReadTimeout };
if (!SetCommTimeouts(_hComm, ref ctoCommPort))
{
throw (new Exception("Bad Timeout Settings"));
}
Dcb dcb = new Dcb();
GetCommState(_hComm, ref dcb);
dcb.BaudRate = BaudRate;
dcb.Parity = Parity;
dcb.ByteSize = ByteSize;
dcb.StopBits = StopBits;
if (!SetCommState(_hComm, ref dcb))
{
throw (new Exception("Bad Com Settings"));
}
}
public void Close()
{
if (_hComm != InvalidHandleValue)
{
CloseHandle(_hComm);
}
}
读串口执行ReadFile即可,可以在调用的地方开一个线程不断的读串口,不过需要考虑效率问题。写串口执行WriteFile即可,每次读、写串口不需要先关闭再打开串口,那样的话也会浪费效率,但是若串口号或者串口配置修改了,则需要关闭并重新打开串口。