/* 
* Author: Marcus Lorentzon, 2001 
* d98malor@dtek.chalmers.se 

* 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)), 00, 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(
00000); 


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, nullnull); 


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(00, 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, nullnull); 


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(00, 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, nullnull)); 


public override void Write(byte[] buffer, int offset, int count) { 
EndWrite(BeginWrite(buffer, offset, count, 
nullnull)); 


public int WriteEx(byte[] buffer, int offset, int count) { 
return EndWriteEx(BeginWrite(buffer, offset, count, nullnull)); 


public int Read(byte[] buffer) { 
return EndRead(BeginRead(buffer, 0, buffer.Length, nullnull)); 


public int Write(byte[] buffer) { 
return EndOperation(BeginWrite(buffer, 0, buffer.Length, nullnull), 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 

public uint fBinary { get { return Flags&0x0001; } 
set { Flags = Flags & ~1U | value; } } 
public uint fParity { get { return (Flags>>1)&1; } 
set { Flags = Flags & ~(1U >2)&1; } 
set { Flags = Flags & ~(1U >3)&1; } 
set { Flags = Flags & ~(1U >4)&3; } 
set { Flags = Flags & ~(3U >6)&1; } 
set { Flags = Flags & ~(1U >7)&1; } 
set { Flags = Flags & ~(1U >8)&1; } 
set { Flags = Flags & ~(1U >9)&1; } 
set { Flags = Flags & ~(1U >10)&1; } 
set { Flags = Flags & ~(1U >11)&1; } 
set { Flags = Flags & ~(1U >12)&3; } 
set { Flags = Flags & ~(3U >14)&1; } 
set { Flags = Flags & ~(1U << 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(200000); 

// 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) { 





posted on 2007-11-22 17:05  蚂蚁  阅读(579)  评论(0)    收藏  举报