Can anyone point me to a working example that uses the .net 4.5 Async API (async, await, task<>, ReadAsync, etc) to do serial communications?
I've tried to adapt an existing event driven serial sample, and am getting all kinds of horrible behavior - "port in use by other application" errors, VS2013 debugger throwing exceptions and locking up - which usually require a PC reboot to recover from.
edit
I've written my own sample from scratch. It's a simple Winforms project that writes to the Output window. Three buttons on the form - Open Port, Close Port, and Read Data. The ReadDataAsync method calls SerialPort.BaseStream.ReadAsync.
As of now, it will read data from the port, but I'm running into problems making it robust.
For example, if I unplug the serial cable, open the port, and click Read Data twice, I will get an System.IO.IOException (which I kind of expect), but my app stops responding.
Worse, when I try to stop my program, VS2013 throws up a "Stop Debugging in Progress" dialog, which never completes, and I can't even kill VS from Task Manager. Have to reboot my PC every time this happens.
Not good.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private SerialPort _serialPort;
public Form1()
{
InitializeComponent();
}
private void openPortbutton_Click(object sender, EventArgs e)
{
try
{
if (_serialPort == null )
_serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
if (!_serialPort.IsOpen)
_serialPort.Open();
Console.Write("Open...");
}
catch(Exception ex)
{
ClosePort();
MessageBox.Show(ex.ToString());
}
}
private void closePortButton_Click(object sender, EventArgs e)
{
ClosePort();
}
private async void ReadDataButton_Click(object sender, EventArgs e)
{
try
{
await ReadDataAsync();
}
catch (Exception ex)
{
ClosePort();
MessageBox.Show(ex.ToString(), "ReadDataButton_Click");
}
}
private async Task ReadDataAsync()
{
byte[] buffer = new byte[4096];
Task<int> readStringTask = _serialPort.BaseStream.ReadAsync(buffer, 0, 100);
if (!readStringTask.IsCompleted)
Console.WriteLine("Waiting...");
int bytesRead = await readStringTask;
string data = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine(data);
}
private void ClosePort()
{
if (_serialPort == null) return;
if (_serialPort.IsOpen)
_serialPort.Close();
_serialPort.Dispose();
_serialPort = null;
Console.WriteLine("Close");
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
ClosePort();
}
}
}
BaseStream.ReadAsync
is exactly the right way to use serial ports in .NET, if you are forced to use the BCL SerialPort class. (Even better is to use the Win32 API.) I have a blog post coming out on this exact topic, after my supervisor approved it. – Ben Voigt