C#串口编程
我们在移至一个MFC的串口操作,在调用win32API时,要定义一个DCB数据结构,可C#中没有位操作的功能,这下可把我难倒了,难道就用C#实现不了这样的数据结构么,结构到网上GOOGLE,看到VB的例子,原来数据结构可以不用完全一样(这也行啊~~~),最后从网上找到下面的这个例子,我将代码格式调整了一下:
/*
* Author: Marcus Lorentzon, 2001
* [email protected]
*
* Freeware: Please do not remove this header
*
* File: SerialStream.cs
*
* Description: Implements a Stream for asynchronous
* transfers and COMM. Stream version.
*
* Version: 2.4
*
*/
#region Using
using System;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
using System.ComponentModel;
#endregion Using
namespace LoMaN.IO
{
public class SerialStream : Stream
{
#region Attributes
private IOCompletionCallback m_IOCompletionCallback;
private IntPtr m_hFile = IntPtr.Zero;
private string m_sPort;
private bool m_bRead;
private bool m_bWrite;
#endregion Attributes
#region Properties
public string Port
{
get
{
return m_sPort;
}
set
{
if (m_sPort != value)
{
Close();
Open(value);
}
}
}
public override bool CanRead
{
get
{
return m_bRead;
}
}
public override bool CanWrite
{
get
{
return m_bWrite;
}
}
public override bool CanSeek
{
get
{
return false;
}
}
public bool Closed
{
get
{
return m_hFile.ToInt32() 0;
}
}
public bool Dsr
{
get
{
uint status;
if (!GetCommModemStatus(m_hFile, out status))
{
throw new Win32Exception();
}
return (status & MS_DSR_ON) > 0;
}
}
public bool Ring
{
get
{
uint status;
if (!GetCommModemStatus(m_hFile, out status))
{
throw new Win32Exception();
}
return (status & MS_RING_ON) > 0;
}
}
public bool Rlsd
{
get
{
uint status;
if (!GetCommModemStatus(m_hFile, out status))
{
throw new Win32Exception();
}
return (status & MS_RLSD_ON) > 0;
}
}
#endregion Properties
#region Constructors
public SerialStream() : this(FileAccess.ReadWrite)
{
}
public SerialStream(FileAccess access)
{
m_bRead = ((int)access & (int)FileAccess.Read) != 0;
m_bWrite = ((int)access & (int)FileAccess.Write) != 0;
unsafe
{
m_IOCompletionCallback = new IOCompletionCallback(AsyncFSCallback);
}
}
public SerialStream(string port) : this(FileAccess.ReadWrite)
{
Open(port);
}
public SerialStream(string port, FileAccess access) : this(access)
{
Open(port);
}
#endregion Constructors
#region Methods
public void Open(string port)
{
if (m_hFile != IntPtr.Zero)
{
throw new IOException("Stream already opened.");
}
m_sPort = port;
m_hFile = CreateFile(port, (uint)((m_bRead?GENERIC_READ:0)|(m_bWrite?GENERIC_WRITE:0)), 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (m_hFile.ToInt32() == INVALID_HANDLE_VALUE)
{
m_hFile = IntPtr.Zero;
throw new FileNotFoundException("Unable to open " + port);
}
ThreadPool.BindHandle(m_hFile);
SetTimeouts(0, 0, 0, 0, 0);
}
public override void Close()
{
CloseHandle(m_hFile);
m_hFile = IntPtr.Zero;
m_sPort = null;
}
public IAsyncResult BeginRead(byte[] buffer)
{
return BeginRead(buffer, 0, buffer.Length, null, null);
}
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
GCHandle gchBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
SerialAsyncResult sar = new SerialAsyncResult(this, state, callback, true, gchBuffer);
Overlapped ov = new Overlapped(0, 0, sar.AsyncWaitHandle.Handle.ToInt32(), sar);
unsafe
{
NativeOverlapped* nov = ov.Pack(m_IOCompletionCallback);
byte* data = (byte*)((int)gchBuffer.AddrOfPinnedObject() + offset);
uint read = 0;
if (ReadFile(m_hFile, data, (uint)count, out read, nov))
{
sar.m_bCompletedSynchronously = true;
return sar;
}
else if (GetLastError() == ERROR_IO_PENDING)
{
return sar;
}
else
throw new Exception("Unable to initialize read. Errorcode: " + GetLastError().ToString());
}
}
public IAsyncResult BeginWrite(byte[] buffer)
{
return BeginWrite(buffer, 0, buffer.Length, null, null);
}
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
GCHandle gchBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
SerialAsyncResult sar = new SerialAsyncResult(this, state, callback, false, gchBuffer);
Overlapped ov = new Overlapped(0, 0, sar.AsyncWaitHandle.Handle.ToInt32(), sar);
unsafe
{
NativeOverlapped* nov = ov.Pack(m_IOCompletionCallback);
byte* data = (byte*)((int)gchBuffer.AddrOfPinnedObject() + offset);
uint written = 0;
if (WriteFile(m_hFile, data, (uint)count, out written, nov))
{
sar.m_bCompletedSynchronously = true;
return sar;
}
else if (GetLastError() == ERROR_IO_PENDING)
{
return sar;
}
else
throw new Exception("Unable to initialize write. Errorcode: " + GetLastError().ToString());
}
}
private int EndOperation(IAsyncResult asyncResult, bool isRead)
{
SerialAsyncResult sar = (SerialAsyncResult)asyncResult;
if (sar.m_bIsRead != isRead)
throw new IOException("Invalid parameter: IAsyncResult is not from a " + (isRead ? "read" : "write"));
if (sar.EndOperationCalled)
{
throw new IOException("End" + (isRead ? "Read" : "Write") + " called twice for the same operation.");
}
else
{
sar.m_bEndOperationCalled = true;
}
while (!sar.m_bCompleted)
{
sar.AsyncWaitHandle.WaitOne();
}
sar.Dispose();
if (sar.m_nErrorCode != ERROR_SUCCESS && sar.m_nErrorCode != ERROR_OPERATION_ABORTED)
{
throw new IOException("Operation finished with errorcode: " + sar.m_nErrorCode);
}
return sar.m_nReadWritten;
}
public override int EndRead(IAsyncResult asyncResult)
{
return EndOperation(asyncResult, true);
}
public override void EndWrite(IAsyncResult asyncResult)
{
EndOperation(asyncResult, false);
}
public int EndWriteEx(IAsyncResult asyncResult)
{
return EndOperation(asyncResult, false);
}
public override int Read(byte[] buffer, int offset, int count)
{
return EndRead(BeginRead(buffer, offset, count, null, null));
}
public override void Write(byte[] buffer, int offset, int count)
{
EndWrite(BeginWrite(buffer, offset, count, null, null));
}
public int WriteEx(byte[] buffer, int offset, int count)
{
return EndWriteEx(BeginWrite(buffer, offset, count, null, null));
}
public int Read(byte[] buffer)
{
return EndRead(BeginRead(buffer, 0, buffer.Length, null, null));
}
public int Write(byte[] buffer)
{
return EndOperation(BeginWrite(buffer, 0, buffer.Length, null, null), false);
}
public override void Flush()
{
FlushFileBuffers(m_hFile);
}
public bool PurgeRead()
{
return PurgeComm(m_hFile, PURGE_RXCLEAR);
}
public bool PurgeWrite()
{
return PurgeComm(m_hFile, PURGE_TXCLEAR);
}
public bool Purge()
{
return PurgeRead() && PurgeWrite();
}
public bool CancelRead()
{
return PurgeComm(m_hFile, PURGE_RXABORT);
}
public bool CancelWrite()
{
return PurgeComm(m_hFile, PURGE_TXABORT);
}
public bool CancelAll()
{
return CancelRead() && CancelWrite();
}
public override void SetLength(long nLength)
{
throw new NotSupportedException("SetLength isn't supported on serial ports.");
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException("Seek isn't supported on serial ports.");
}
public void SetTimeouts(int ReadIntervalTimeout,
int ReadTotalTimeoutMultiplier,
int ReadTotalTimeoutConstant,
int WriteTotalTimeoutMultiplier,
int WriteTotalTimeoutConstant)
{
SerialTimeouts Timeouts = new SerialTimeouts(ReadIntervalTimeout,
ReadTotalTimeoutMultiplier,
ReadTotalTimeoutConstant,
WriteTotalTimeoutMultiplier,
WriteTotalTimeoutConstant);
unsafe
{
SetCommTimeouts(m_hFile, ref Timeouts);
}
}
public bool SetPortSettings(uint baudrate)
{
return SetPortSettings(baudrate, FlowControl.Hardware);
}
public bool SetPortSettings(uint baudrate, FlowControl flowControl)
{
return SetPortSettings(baudrate, flowControl, Parity.None);
}
public bool SetPortSettings(uint baudrate, FlowControl flowControl, Parity parity)
{
return SetPortSettings(baudrate, flowControl, parity, 8, StopBits.One);
}
public bool SetPortSettings(uint baudrate, FlowControl flowControl, Parity parity, byte databits, StopBits stopbits)
{
unsafe
{
DCB dcb = new DCB();
dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = baudrate;
dcb.ByteSize = databits;
dcb.StopBits = (byte)stopbits;
dcb.Parity = (byte)parity;
dcb.fParity = (parity > 0)? 1U : 0U;
dcb.fBinary = dcb.fDtrControl = dcb.fTXContinueOnXoff = 1;
dcb.fOutxCtsFlow = dcb.fAbortOnError = (flowControl == FlowControl.Hardware)? 1U : 0U;
dcb.fOutX = dcb.fInX = (flowControl == FlowControl.XOnXOff)? 1U : 0U;
dcb.fRtsControl = (flowControl == FlowControl.Hardware)? 2U : 1U;
dcb.XonLim = 2048;
dcb.XoffLim = 512;
dcb.XonChar = 0x11; // Ctrl-Q
dcb.XoffChar = 0x13; // Ctrl-S
return SetCommState(m_hFile, ref dcb);
}
}
public bool SetPortSettings(DCB dcb)
{
return SetCommState(m_hFile, ref dcb);
}
public bool GetPortSettings(out DCB dcb)
{
unsafe
{
DCB dcb2 = new DCB();
dcb2.DCBlength = sizeof(DCB);
bool ret = GetCommState(m_hFile, ref dcb2);
dcb = dcb2;
return ret;
}
}
public bool SetXOn()
{
return EscapeCommFunction(m_hFile, SETXON);
}
public bool SetXOff()
{
return EscapeCommFunction(m_hFile, SETXOFF);
}
private unsafe void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped)
{
SerialAsyncResult sar = (SerialAsyncResult)Overlapped.Unpack(pOverlapped).AsyncResult;
sar.m_nErrorCode = errorCode;
sar.m_nReadWritten = (int)numBytes;
sar.m_bCompleted = true;
if (sar.Callback != null)
sar.Callback.Invoke(sar);
Overlapped.Free(pOverlapped);
}
#endregion Methods
#region Constants
private const uint PURGE_TXABORT = 0x0001; // Kill the pending/current writes to the comm port.
private const uint PURGE_RXABORT = 0x0002; // Kill the pending/current reads to the comm port.
private const uint PURGE_TXCLEAR = 0x0004; // Kill the transmit queue if there.
private const uint PURGE_RXCLEAR = 0x0008; // Kill the typeahead buffer if there.
private const uint SETXOFF = 1; // Simulate XOFF received
private const uint SETXON = 2; // Simulate XON received
private const uint SETRTS = 3; // Set RTS high
private const uint CLRRTS = 4; // Set RTS low
private const uint SETDTR = 5; // Set DTR high
private const uint CLRDTR = 6; // Set DTR low
private const uint SETBREAK = 8; // Set the device break line.
private const uint CLRBREAK = 9; // Clear the device break line.
private const uint MS_CTS_ON = 0x0010;
private const uint MS_DSR_ON = 0x0020;
private const uint MS_RING_ON = 0x0040;
private const uint MS_RLSD_ON = 0x0080;
private const uint FILE_FLAG_OVERLAPPED = 0x40000000;
private const uint OPEN_EXISTING = 3;
private const int INVALID_HANDLE_VALUE = -1;
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint ERROR_SUCCESS = 0;
private const uint ERROR_OPERATION_ABORTED = 995;
private const uint ERROR_IO_PENDING = 997;
#endregion Constants
#region Enums
public enum Parity
{
None,
Odd,
Even,
Mark,
Space
};
public enum StopBits
{
One,
OneAndHalf,
Two
};
public enum FlowControl
{
None,
XOnXOff,
Hardware
};
#endregion Enums
#region Classes
[StructLayout(LayoutKind.Sequential)]
public struct DCB
{
#region Attributes
public int DCBlength;
public uint BaudRate;
public uint Flags;
public ushort wReserved;
public ushort XonLim;
public ushort XoffLim;
public byte ByteSize;
public byte Parity;
public byte StopBits;
public sbyte XonChar;
public sbyte XoffChar;
public sbyte ErrorChar;
public sbyte EofChar;
public sbyte EvtChar;
public ushort wReserved1;
#endregion Attributes
#region Properties
internal uint binary
{
get
{
return flags&0x0001;
}
set
{
flags = flags & ~1U | value;
}
}
internal uint bitParity
{
set
{
flags = (flags & ~(1U << 1)) | ((value & 1U) << 1);
}
}
internal uint outxCtsFlow
{
set
{
flags = (flags & ~(1U << 2)) | ((value & 1U) << 2);
}
}
internal uint outxDsrFlow
{
set
{
flags = (flags & ~(1U << 3)) | ((value & 1U) << 3);
}
}
internal uint dtrControl
{
set
{
flags = (flags & ~(1U << 4)) | ((value & 1U) << 4);
}
}
internal uint dsrSensitivity
{
set
{
flags = (flags & ~(3U << 1)) | ((value & 3U) << 5);
}
}
internal uint continueOnXoff
{
set
{
flags = (flags & ~(1U << 7)) | ((value & 1U) << 7);
}
}
internal uint outX
{
set
{
flags = (flags & ~(1U << 8)) | ((value & 1U) << 8);
}
}
internal uint inX
{
set
{
flags = (flags & ~(1U << 9)) | ((value & 1U) << 9);
}
}
internal uint biterrorChar
{
set
{
flags = (flags & ~(1U << 10)) | ((value & 1U) << 10);
}
}
internal uint nullFlag
{
set
{
flags = (flags & ~(1U << 11)) | ((value & 1U) << 11);
}
}
internal uint rtsControl
{
set
{
flags = (flags & ~(1U << 12)) | ((value & 1U) << 12);
}
}
internal uint abortOnError
{
set
{
flags = (flags & ~(1U << 13)) | ((value & 1U) << 13);
}
}
internal uint dummy2
{
set
{
flags = flags & ~(131072U << 14) | (value << 14);
}
}
#endregion Properties
#region Methods
public override string ToString()
{
return "DCBlength: " + DCBlength + "/r/n" +
"BaudRate: " + BaudRate + "/r/n" +
"fBinary: " + fBinary + "/r/n" +
"fParity: " + fParity + "/r/n" +
"fOutxCtsFlow: " + fOutxCtsFlow + "/r/n" +
"fOutxDsrFlow: " + fOutxDsrFlow + "/r/n" +
"fDtrControl: " + fDtrControl + "/r/n" +
"fDsrSensitivity: " + fDsrSensitivity + "/r/n" +
"fTXContinueOnXoff: " + fTXContinueOnXoff + "/r/n" +
"fOutX: " + fOutX + "/r/n" +
"fInX: " + fInX + "/r/n" +
"fErrorChar: " + fErrorChar + "/r/n" +
"fNull: " + fNull + "/r/n" +
"fRtsControl: " + fRtsControl + "/r/n" +
"fAbortOnError: " + fAbortOnError + "/r/n" +
"XonLim: " + XonLim + "/r/n" +
"XoffLim: " + XoffLim + "/r/n" +
"ByteSize: " + ByteSize + "/r/n" +
"Parity: " + Parity + "/r/n" +
"StopBits: " + StopBits + "/r/n" +
"XonChar: " + XonChar + "/r/n" +
"XoffChar: " + XoffChar + "/r/n" +
"EofChar: " + EofChar + "/r/n" +
"EvtChar: " + EvtChar + "/r/n";
}
#endregion Methods
}
private class SerialAsyncResult : IAsyncResult, IDisposable
{
#region Attributes
internal bool m_bEndOperationCalled = false;
internal bool m_bIsRead;
internal int m_nReadWritten = 0;
internal bool m_bCompleted = false;
internal bool m_bCompletedSynchronously = false;
internal uint m_nErrorCode = ERROR_SUCCESS;
private object m_AsyncObject;
private object m_StateObject;
private ManualResetEvent m_WaitHandle = new ManualResetEvent(false);
private AsyncCallback m_Callback;
private GCHandle m_gchBuffer;
#endregion Attributes
#region Properties
internal bool EndOperationCalled
{
get
{
return m_bEndOperationCalled;
}
}
public bool IsCompleted
{
get
{
return m_bCompleted;
}
}
public bool CompletedSynchronously
{
get
{
return m_bCompletedSynchronously;
}
}
public object AsyncObject
{
get
{
return m_AsyncObject;
}
}
public object AsyncState
{
get
{
return m_StateObject;
}
}
public WaitHandle AsyncWaitHandle
{
get
{
return m_WaitHandle;
}
}
internal ManualResetEvent WaitHandle
{
get
{
return m_WaitHandle;
}
}
public AsyncCallback Callback
{
get
{
return m_Callback;
}
}
#endregion Properties
#region Constructors
public SerialAsyncResult(object asyncObject,
object stateObject,
AsyncCallback callback,
bool bIsRead,
GCHandle gchBuffer)
{
m_AsyncObject = asyncObject;
m_StateObject = stateObject;
m_Callback = callback;
m_bIsRead = bIsRead;
m_gchBuffer = gchBuffer;
}
#endregion Constructors
#region Methods
public void Dispose()
{
m_WaitHandle.Close();
m_gchBuffer.Free();
}
#endregion Methods
}
#endregion Classes
#region Imports
[DllImport("kernel32.dll", EntryPoint="CreateFileW", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true)]
static extern IntPtr CreateFile(string filename, uint access, uint sharemode, uint security_attributes, uint creation, uint flags, uint template);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll", SetLastError=true)]
static extern unsafe bool ReadFile(IntPtr hFile, byte* lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, NativeOverlapped* lpOverlapped);
[DllImport("kernel32.dll", SetLastError=true)]
static extern unsafe bool WriteFile(IntPtr hFile, byte* lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, NativeOverlapped* lpOverlapped);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool SetCommTimeouts(IntPtr hFile, ref SerialTimeouts lpCommTimeouts);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool SetCommState(IntPtr hFile, ref DCB lpDCB);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool GetCommState(IntPtr hFile, ref DCB lpDCB);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool BuildCommDCB(string def, ref DCB lpDCB);
[DllImport("kernel32.dll", SetLastError=true)]
static extern int GetLastError();
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool FlushFileBuffers(IntPtr hFile);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool PurgeComm(IntPtr hFile, uint dwFlags);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool EscapeCommFunction(IntPtr hFile, uint dwFunc);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool GetCommModemStatus(IntPtr hFile, out uint modemStat);
#endregion Imports
}
[StructLayout(LayoutKind.Sequential)]
public struct SerialTimeouts
{
#region Attributes
public int ReadIntervalTimeout;
public int ReadTotalTimeoutMultiplier;
public int ReadTotalTimeoutConstant;
public int WriteTotalTimeoutMultiplier;
public int WriteTotalTimeoutConstant;
#endregion Attributes
#region Constructors
public SerialTimeouts(int r1, int r2, int r3, int w1, int w2)
{
ReadIntervalTimeout = r1;
ReadTotalTimeoutMultiplier = r2;
ReadTotalTimeoutConstant = r3;
WriteTotalTimeoutMultiplier = w1;
WriteTotalTimeoutConstant = w2;
}
#endregion Constructors
#region Methods
public override string ToString()
{
return "ReadIntervalTimeout: " + ReadIntervalTimeout + "/r/n" +
"ReadTotalTimeoutMultiplier: " + ReadTotalTimeoutMultiplier + "/r/n" +
"ReadTotalTimeoutConstant: " + ReadTotalTimeoutConstant + "/r/n" +
"WriteTotalTimeoutMultiplier: " + WriteTotalTimeoutMultiplier + "/r/n" +
"WriteTotalTimeoutConstant: " + WriteTotalTimeoutConstant + "/r/n";
}
#endregion Methods
}
}
using System;
using System.IO;
using System.Threading;
using LoMaN.IO;
namespace SerialStreamReader
{
class App
{
// The main serial stream
static SerialStream ss;
[STAThread]
static void Main(string[] args)
{
// Create a serial port
ss = new SerialStream();
try
{
ss.Open("COM4"); /
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
return;
}
// Set port settings
ss.SetPortSettings(9600);
// Set timeout so read ends after 20ms of silence after a response
ss.SetTimeouts(20, 0, 0, 0, 0);
// Create the StreamWriter used to send commands
StreamWriter sw = new StreamWriter(ss, System.Text.Encoding.ASCII);
// Create the Thread used to read responses
Thread responseReaderThread = new Thread(new ThreadStart(ReadResponseThread));
responseReaderThread.Start();
// Read all returned lines
for (;;) {
// Read command from console
string command = Console.ReadLine();
// Check for exit command
if (command.Trim().ToLower() == "exit")
{
responseReaderThread.Abort();
break;
}
// Write command to modem
sw.WriteLine(command);
sw.Flush();
}
}
// Main loop for reading responses
static void ReadResponseThread()
{
StreamReader sr = new StreamReader(ss, System.Text.Encoding.ASCII);
try
{
for (;;)
{
// Read response from modem
string response = sr.ReadLine();
Console.WriteLine("Response: " + response);
}
}
catch (ThreadAbortException) {
}
}
}
}