C#串口编程

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

你可能感兴趣的:(C#串口编程)