I just started learning how to send and receive data from my hardware through the C# GUI.
Can anyone please write a detail how to read data from the serial port?
SerialPort (RS-232 Serial COM Port) in C# .NET
This article explains how to use the SerialPort class in .NET to read and write data, determine what serial ports are available on your machine, and how to send files. It even covers the pin assignments on the port itself.
Example Code:
using System;
using System.IO.Ports;
using System.Windows.Forms;
namespace SerialPortExample
{
class SerialPortProgram
{
// Create the serial port with basic settings
private SerialPort port = new SerialPort("COM1",
9600, Parity.None, 8, StopBits.One);
[STAThread]
static void Main(string[] args)
{
// Instatiate this class
new SerialPortProgram();
}
private SerialPortProgram()
{
Console.WriteLine("Incoming Data:");
// Attach a method to be called when there
// is data waiting in the port's buffer
port.DataReceived += new
SerialDataReceivedEventHandler(port_DataReceived);
// Begin communications
port.Open();
// Enter an application loop to keep this thread alive
Application.Run();
}
private void port_DataReceived(object sender,
SerialDataReceivedEventArgs e)
{
// Show all the incoming data in the port's buffer
Console.WriteLine(port.ReadExisting());
}
}
}
I spent a lot of time to use SerialPort class and has concluded to use SerialPort.BaseStream class instead. You can see source code: SerialPort-source and
SerialPort.BaseStream-source for deep understanding.
I created and use code that shown below.
The core function
public int Recv(byte[] buffer, int maxLen)
has name and works like "well known" socket's recv().
It means that
in one hand it has timeout for no any data and throws TimeoutException.
In other hand, when any data has received,
it receives data either until maxLen bytes
or short timeout (theoretical 6 ms) in UART data flow
.
public class Uart : SerialPort
{
private int _receiveTimeout;
public int ReceiveTimeout { get => _receiveTimeout; set => _receiveTimeout = value; }
static private string ComPortName = "";
/// <summary>
/// It builds PortName using ComPortNum parameter and opens SerialPort.
/// </summary>
/// <param name="ComPortNum"></param>
public Uart(int ComPortNum) : base()
{
base.BaudRate = 115200; // default value
_receiveTimeout = 2000;
ComPortName = "COM" + ComPortNum;
try
{
base.PortName = ComPortName;
base.Open();
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine("Error: Port {0} is in use", ComPortName);
}
catch (Exception ex)
{
Console.WriteLine("Uart exception: " + ex);
}
} //Uart()
/// <summary>
/// Private property returning positive only Environment.TickCount
/// </summary>
private int _tickCount { get => Environment.TickCount & Int32.MaxValue; }
/// <summary>
/// It uses SerialPort.BaseStream rather SerialPort functionality .
/// It Receives up to maxLen number bytes of data,
/// Or throws TimeoutException if no any data arrived during ReceiveTimeout.
/// It works likes socket-recv routine (explanation in body).
/// Returns:
/// totalReceived - bytes,
/// TimeoutException,
/// -1 in non-ComPortNum Exception
/// </summary>
/// <param name="buffer"></param>
/// <param name="maxLen"></param>
/// <returns></returns>
public int Recv(byte[] buffer, int maxLen)
{
/// The routine works in "pseudo-blocking" mode. It cycles up to first
/// data received using BaseStream.ReadTimeout = TimeOutSpan (2 ms).
/// If no any message received during ReceiveTimeout property,
/// the routine throws TimeoutException
/// In other hand, if any data has received, first no-data cycle
/// causes to exit from routine.
int TimeOutSpan = 2;
// counts delay in TimeOutSpan-s after end of data to break receive
int EndOfDataCnt;
// pseudo-blocking timeout counter
int TimeOutCnt = _tickCount + _receiveTimeout;
//number of currently received data bytes
int justReceived = 0;
//number of total received data bytes
int totalReceived = 0;
BaseStream.ReadTimeout = TimeOutSpan;
//causes (2+1)*TimeOutSpan delay after end of data in UART stream
EndOfDataCnt = 2;
while (_tickCount < TimeOutCnt && EndOfDataCnt > 0)
{
try
{
justReceived = 0;
justReceived = base.BaseStream.Read(buffer, totalReceived, maxLen - totalReceived);
totalReceived += justReceived;
if (totalReceived >= maxLen)
break;
}
catch (TimeoutException)
{
if (totalReceived > 0)
EndOfDataCnt--;
}
catch (Exception ex)
{
totalReceived = -1;
base.Close();
Console.WriteLine("Recv exception: " + ex);
break;
}
} //while
if (totalReceived == 0)
{
throw new TimeoutException();
}
else
{
return totalReceived;
}
} // Recv()
} // Uart
Note that usage of a SerialPort.DataReceived event is optional. You can set proper timeout using SerialPort.ReadTimeout and continuously call SerialPort.Read() after you wrote something to a port until you get a full response.
Moreover, you can use SerialPort.BaseStream property to extract an underlying Stream instance. The benefit of using a Stream is that you can easily utilize various decorators with it:
var port = new SerialPort();
// LoggingStream inherits Stream, implements IDisposable, needed abstract methods and
// overrides needed virtual methods.
Stream portStream = new LoggingStream(port.BaseStream);
portStream.Write(...); // Logs write buffer.
portStream.Read(...); // Logs read buffer.
For more information:
Top 5 SerialPort Tips article by Kim Hamilton, BCL Team Blog
C# await event and timeout in serial port communication discussion on StackOverflow
Related
I am trying to create a server that can accept both secure SSL and insecure plain text connection (for backwards compatibility). My code is almost working except the first transmitted data received from an insecure client loses the first 5 bytes (chars) at the server. More specificially if I transmit 30 bytes on an insecure connection, when the server gets to the OnClientDataReceived() function, the line "int iRx = nwStream.EndRead(asyn);", then iRx = 25. Any subsequent messages transmitted from the client contain all sent bytes/chars as expected. I suspect that the initial assumption of the connection being an SSLStream may be stripping the first 5 bytes and then when it fails, those 5 bytes have already been extracted from the buffer and are no longer available. Does any body know of another approach I could take to write the code so that the server automatically can switch on the fly?
I am trying to avoid doing the following:
Require that a client connect using a plain text NetworkStream and then request to upgrade to an SSL stream
Setting up two TcpListeners on two different ports (one for secure, one for insecure)
Here is my code:
/// Each client that connects gets an instance of the ConnectedClient class.
Class Pseudo_ConnectedClient
{
//Properties
byte[] Buffer; //Holds temporary buffer of read bytes from BeginRead()
TcpClient TCPClient; //Reference to the connected client
Socket ClientSocket; //The outer Socket Reference of the connected client
StringBuilder CurrentMessage; //concatenated chunks of data in buffer until we have a complete message (ends with <ETX>
Stream Stream; //SSLStream or NetworkStream depending on client
ArrayList MessageQueue; //Array of complete messages received from client that need to be processed
}
/// When a new client connects (OnClientConnection callback is executed), the server creates the ConnectedClient object and stores its
/// reference in a local dictionary, then configures the callbacks for incoming data (WaitForClientData)
void OnClientConnection(IAsyncResult result)
{
TcpListener listener = result.AsyncState as TcpListener;
TcpClient clnt = null;
try
{
if (!IsRunning) //then stop was called, so don't call EndAcceptTcpClient because it will throw and ObjectDisposedException
return;
//Start accepting the next connection...
listener.BeginAcceptTcpClient(this.onClientConnection, listener);
//Get reference to client and set flag to indicate connection accepted.
clnt = listener.EndAcceptTcpClient(result);
//Add the reference to our ArrayList of Connected Clients
ConnectedClient conClnt = new ConnectedClient(clnt);
_clientList.Add(conClnt);
//Configure client to listen for incoming data
WaitForClientData(conClnt);
}
catch (Exception ex)
{
Trace.WriteLine("Server:OnClientConnection: Exception - " + ex.ToString());
}
}
/// WaitForClientData registers the AsyncCallback to handle incoming data from a client (OnClientDataReceieved).
/// If a certificate has been provided, then it listens for clients to connect on an SSLStream and configures the
/// BeginAuthenticateAsServer callback. If no certificate is provided, then it only sets up a NetworkStream
/// and prepares for the BeginRead callback.
private void WaitForClientData(ConnectedClient clnt)
{
if (!IsRunning) return; //Then stop was called, so don't do anything
SslStream sslStream = null;
try
{
if (_pfnClientDataCallBack == null) //then define the call back function to invoke when data is received from a connected client
_pfnClientDataCallBack = new AsyncCallback(OnClientDataReceived);
NetworkStream nwStream = clnt.TCPClient.GetStream();
//Check if we can establish a secure connection
if (this.SSLCertificate != null) //Then we have the ability to make an SSL connection (SSLCertificate is a X509Certificate2 object)
{
if (this.certValidationCallback != null)
sslStream = new SslStream(nwStream, true, this.certValidationCallback);
else
sslStream = new SslStream(nwStream, true);
clnt.Stream = sslStream;
//Start Listening for incoming (secure) data
sslStream.BeginAuthenticateAsServer(this.SSLCertificate, false, SslProtocols.Default, false, onAuthenticateAsServer, clnt);
}
else //No certificate available to make a secure connection, so use insecure (unless not allowed)
{
if (this.RequireSecureConnection == false) //Then we can try to read from the insecure stream
{
clnt.Stream = nwStream;
//Start Listening for incoming (unsecure) data
nwStream.BeginRead(clnt.Buffer, 0, clnt.Buffer.Length, _pfnClientDataCallBack, clnt);
}
else //we can't do anything - report config problem
{
throw new InvalidOperationException("A PFX certificate is not loaded and the server is configured to require a secure connection");
}
}
}
catch (Exception ex)
{
DisconnectClient(clnt);
}
}
/// OnAuthenticateAsServer first checks if the stream is authenticated, if it isn't it gets the TCPClient's reference
/// to the outer NetworkStream (client.TCPClient.GetStream()) - the insecure stream and calls the BeginRead on that.
/// If the stream is authenticated, then it keeps the reference to the SSLStream and calls BeginRead on it.
private void OnAuthenticateAsServer(IAsyncResult result)
{
ConnectedClient clnt = null;
SslStream sslStream = null;
if (this.IsRunning == false) return;
try
{
clnt = result.AsyncState as ConnectedClient;
sslStream = clnt.Stream as SslStream;
if (sslStream.IsAuthenticated)
sslStream.EndAuthenticateAsServer(result);
else //Try and switch to an insecure connections
{
if (this.RequireSecureConnection == false) //Then we are allowed to accept insecure connections
{
if (clnt.TCPClient.Connected)
clnt.Stream = clnt.TCPClient.GetStream();
}
else //Insecure connections are not allowed, close the connection
{
DisconnectClient(clnt);
}
}
}
catch (Exception ex)
{
DisconnectClient(clnt);
}
if( clnt.Stream != null) //Then we have a stream to read, start Async read
clnt.Stream.BeginRead(clnt.Buffer, 0, clnt.Buffer.Length, _pfnClientDataCallBack, clnt);
}
/// OnClientDataReceived callback is triggered by the BeginRead async when data is available from a client.
/// It determines if the stream (as assigned by OnAuthenticateAsServer) is an SSLStream or a NetworkStream
/// and then reads the data out of the stream accordingly. The logic to parse and process the message has
/// been removed because it isn't relevant to the question.
private void OnClientDataReceived(IAsyncResult asyn)
{
try
{
ConnectedClient connectClnt = asyn.AsyncState as ConnectedClient;
if (!connectClnt.TCPClient.Connected) //Then the client is no longer connected >> clean up
{
DisconnectClient(connectClnt);
return;
}
Stream nwStream = null;
if( connectClnt.Stream is SslStream) //Then this client is connected via a secure stream
nwStream = connectClnt.Stream as SslStream;
else //this is a plain text stream
nwStream = connectClnt.Stream as NetworkStream;
// Complete the BeginReceive() asynchronous call by EndReceive() method which
// will return the number of characters written to the stream by the client
int iRx = nwStream.EndRead(asyn); //Returns the numbers of bytes in the read buffer
char[] chars = new char[iRx];
// Extract the characters as a buffer and create a String
Decoder d = ASCIIEncoding.UTF8.GetDecoder();
d.GetChars(connectClnt.Buffer, 0, iRx, chars, 0);
//string data = ASCIIEncoding.ASCII.GetString(buff, 0, buff.Length);
string data = new string(chars);
if (iRx > 0) //Then there was data in the buffer
{
//Append the current packet with any additional data that was already received
connectClnt.CurrentMessage.Append(data);
//Do work here to check for a complete message
//Make sure two complete messages didn't get concatenated in one transmission (mobile devices)
//Add each message to the client's messageQueue
//Clear the currentMessage
//Any partial messsage at the end of the buffer needs to be added to the currentMessage
//Start reading again
nwStream.BeginRead(connectClnt.Buffer, 0, connectClnt.Buffer.Length, OnClientDataReceived, connectClnt);
}
else //zero-length packet received - Disconnecting socket
{
DisconnectClient(connectClnt);
}
}
catch (Exception ex)
{
return;
}
}
What works:
If the server doesn't have a certificate, a NetworkStream is only used, and all bytes are received from the client for all messages.
If the server does have a certificate (an SSLStream is setup) and a secure connection can be established (web-browser using https://) and the full message is received for all messages.
What doesn't work:
If the server does have a certificate (an SSLStream is setup) and an insecure connection is made from a client, when the first message is received from that client, the code does correctly detect the SSLStream is not authenticated and switches to the NetworkStream of the TCPClient. However, when EndRead is called on that NetworkStream for the first message, the first 5 chars (bytes) are missing from the message that was sent, but only for the first message. All remaining messages are complete as long as the TCPClient is connected. If the client disconnects and then reconnects, the first message is clipped, then all subsequent messages are good again.
What is causing those first 5 bytes to be clipped, and how can I avoid it?
My project is currently using .NET v3.5... I would like to remain at this version and not step up to 4.0 if I can avoid it.
Follow-up Question
Damien's answer below does allow me to retain those missing 5 bytes, however, I would prefer to stick with the BeginRead and EndRead methods in my code to avoid blocking. Are there any good tutorials showing a 'best practices' when override(ing) these? More specifically, how to work with the IAsyncResult object. I get that I would need to add any content that is stored in the RestartableStream buffers, then fall through to the inner stream (base) to get the rest and return the toral. But since the IAsyncResult object is a custom class, I can't figure out the generic way that I can combine the buffs of RestartableStream with those of the inner stream before returning. Do I need to also implement BeginRead() so that I know the buffers the user wants the content stored into? I guess the other solution is, since the dropped bytes problem is only with the first message from the client (after that I know whether to use it as a SSLStream or a NetworkStream), would be to handle that first message by directly calling the Read() method of RestartableStream (temporarily blocking the code), then for all future messages use the Async callbacks to Read the contents as I do now.
Okay, I think the best you can do is place your own class in between SslStream and NetworkStream where you implement some customized buffering. I've done a few tests on the below, but I'd recommend a few more before you put in in production (and probably some more robust error handling). I think I've avoided any 4.0 or 4.5isms:
public sealed class RestartableReadStream : Stream
{
private Stream _inner;
private List<byte[]> _buffers;
private bool _buffering;
private int? _currentBuffer = null;
private int? _currentBufferPosition = null;
public RestartableReadStream(Stream inner)
{
if (!inner.CanRead) throw new NotSupportedException(); //Don't know what else is being expected of us
if (inner.CanSeek) throw new NotSupportedException(); //Just use the underlying streams ability to seek, no need for this class
_inner = inner;
_buffering = true;
_buffers = new List<byte[]>();
}
public void StopBuffering()
{
_buffering = false;
if (!_currentBuffer.HasValue)
{
//We aren't currently using the buffers
_buffers = null;
_currentBufferPosition = null;
}
}
public void Restart()
{
if (!_buffering) throw new NotSupportedException(); //Buffering got turned off already
if (_buffers.Count == 0) return;
_currentBuffer = 0;
_currentBufferPosition = 0;
}
public override int Read(byte[] buffer, int offset, int count)
{
if (_currentBuffer.HasValue)
{
//Try to satisfy the read request from the current buffer
byte[] rbuffer = _buffers[_currentBuffer.Value];
int roffset = _currentBufferPosition.Value;
if ((rbuffer.Length - roffset) <= count)
{
//Just give them what we have in the current buffer (exhausting it)
count = (rbuffer.Length - roffset);
for (int i = 0; i < count; i++)
{
buffer[offset + i] = rbuffer[roffset + i];
}
_currentBuffer++;
if (_currentBuffer.Value == _buffers.Count)
{
//We've stopped reading from the buffers
if (!_buffering)
_buffers = null;
_currentBuffer = null;
_currentBufferPosition = null;
}
return count;
}
else
{
for (int i = 0; i < count; i++)
{
buffer[offset + i] = rbuffer[roffset + i];
}
_currentBufferPosition += count;
return count;
}
}
//If we reach here, we're currently using the inner stream. But may be buffering the results
int ncount = _inner.Read(buffer, offset, count);
if (_buffering)
{
byte[] rbuffer = new byte[ncount];
for (int i = 0; i < ncount; i++)
{
rbuffer[i] = buffer[offset + i];
}
_buffers.Add(rbuffer);
}
return ncount;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
//No more interesting code below here
public override void Flush()
{
throw new NotSupportedException();
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
Usage:
Construct a RestartableReadStream around your NetworkStream. Pass that instance to SslStream. If you decide that SSL was the wrong way to do things, call Restart() and then use it again however you want to. You can even try more than two strategies (calling Restart() between each one).
Once you've settled on which strategy (e.g. SSL or non-SSL) is correct, call StopBuffering(). Once it's finished replaying any buffers it had available, it will revert to just calling Read on its inner stream. If you don't call StopBuffering, then the entire history of reads from the stream will be kept in the _buffers list, which could add quite a bit of memory pressure.
Note that none of the above particularly accounts for multi-threaded access. But if you've got multiple threads calling Read() on a single stream (especially one that's network based), I wouldn't expect any sanity anyway.
I spent hours searching to not write a stream wrapper around NetworkStream and finally came across this and it worked for me.
MSDN SocketFlag.Peek
I kept finding suggestions to just write a wrapper or use separate ports, but I have a problem listening to authority or reason.
Here's my code. NLOLOL (No laughing out loud or lectures)
I haven't completely figured out if I need to Peek at more than the first byte for all scenarios.
Private Async Sub ProcessTcpClient(__TcpClient As Net.Sockets.TcpClient)
If __TcpClient Is Nothing OrElse Not __TcpClient.Connected Then Return
Dim __RequestBuffer(0) As Byte
Dim __BytesRead As Integer
Using __NetworkStream As Net.Sockets.NetworkStream = __TcpClient.GetStream
__BytesRead = __TcpClient.Client.Receive(__RequestBuffer, 0, 1, SocketFlags.Peek)
If __BytesRead = 1 AndAlso __RequestBuffer(0) = 22 Then
Await Me.ProcessTcpClientSsl(__NetworkStream)
Else
Await Me.ProcessTcpClientNonSsl(__NetworkStream)
End If
End Using
__TcpClient.Close()
End Sub
When I select a bunch of files in Explorer and right click open them (or press enter) I want all the files to be passed to a single instance of my application. I've used named pipes before to pass arguments from secondary instances to an existing global instance, but it seems like doing this for hundreds of program instances simultaneously (not to mention actually loading the application hundreds of times) is far from optimal. Is there a way to get explorer to concatenate the arguments on its own?
edit: I found a copy of the Paint.net 3.36 source code and it uses a memory mapped file to communicate between instances. That seems even more bloated than named pipes though (although it's not as likely to open hundreds of images for editing).
You can do it with shell extension.
Check out Creating Shortcut Menu Handlers
and
Create Namespace Extensions for Windows Explorer with the .NET Framework
I couldn't figure out the shell extension so I went with the named pipes. It seems to perform reasonably well, especially since there seems to be a limit in Windows 7 of how many files you can open with multi-select (although this will solve that: open-more-than-15-files-at-once-on-windows-7)
It took me forever to figure out how to get the timeout behavior to work (had to use a manual signal instead of AsyncWaitHandle). Hopefully this will save somebody some time:
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// The Original Code is the IDPicker project.
//
// The Initial Developer of the Original Code is Matt Chambers.
//
// Copyright 2011 Vanderbilt University
//
// Contributor(s):
//
using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.ComponentModel;
using System.Collections.Generic;
/// <summary>
/// A handler for an application to consolidate arguments from multiple instances
/// (within a Timeout period) into a single instance.
/// </summary>
public sealed class SingleInstanceHandler
{
/// <summary>
/// Occurs when the Timeout period has elapsed: the single instance is launched with the consolidated argument list.
/// </summary>
public event EventHandler<SingleInstanceEventArgs> Launching;
/// <summary>
/// Time to wait in milliseconds for additional instances to add their arguments to the first instance's.
/// </summary>
public int Timeout { get; set; }
/// <summary>
/// Constructs a handler for an application to consolidate arguments from multiple instances
/// (within a Timeout period) into a single instance.
/// </summary>
/// <param name="uniqueID">A unique string for the application.</param>
public SingleInstanceHandler (string uniqueID)
{
var rng = new Random(uniqueID.GetHashCode());
byte[] ipcMutexGuidBytes = new byte[16];
byte[] ipcNamedPipeGuidBytes = new byte[16];
rng.NextBytes(ipcMutexGuidBytes);
rng.NextBytes(ipcNamedPipeGuidBytes);
ipcMutexGuid = new Guid(ipcMutexGuidBytes).ToString().Trim('{', '}');
ipcNamedPipeGuid = new Guid(ipcNamedPipeGuidBytes).ToString().Trim('{', '}');
Timeout = 500;
}
/// <summary>
/// Launch a new instance using 'args' or consolidate 'args' into a recent instance.
/// </summary>
public void Connect (string[] args)
{
if (Launching == null)
return; // nothing to do
// create global named mutex
using (ipcMutex = new Mutex(false, ipcMutexGuid))
{
// if the global mutex is not locked, wait for args from additional instances
if (ipcMutex.WaitOne(0))
waitForAdditionalInstances(args);
else
sendArgsToExistingInstance(args);
}
}
private void waitForAdditionalInstances (string[] args)
{
var accumulatedArgs = new List<string>(args);
while (true)
{
var signal = new ManualResetEvent(false);
using (var pipeServer = new NamedPipeServerStream(ipcNamedPipeGuid, PipeDirection.In, -1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous))
{
pipeServer.BeginWaitForConnection(x =>
{
// if timed out, stop waiting for a connection
if (signal.WaitOne(0))
{
signal.Close();
return;
}
pipeServer.EndWaitForConnection(x);
signal.Set();
}, null);
// no client connected to the pipe within the Timeout period
if (!signal.WaitOne(Timeout, true))
{
signal.Set();
break;
}
using (var sr = new StreamReader(pipeServer))
{
int length = Convert.ToInt32(sr.ReadLine());
for (int i = 0; i < length; ++i)
accumulatedArgs.Add(sr.ReadLine());
}
}
// new args have been added to accumulatedArgs, continue loop to listen for another client
}
Launching(this, new SingleInstanceEventArgs(accumulatedArgs.ToArray()));
}
private void sendArgsToExistingInstance (string[] args)
{
var pipeClient = new NamedPipeClientStream(".", ipcNamedPipeGuid, PipeDirection.Out);
// try to connect to the pipe server for the Timeout period
try
{
var sb = new StringBuilder();
sb.AppendLine(args.Length.ToString());
foreach (string arg in args)
sb.AppendLine(arg);
byte[] buffer = Encoding.ASCII.GetBytes(sb.ToString());
pipeClient.Connect(Timeout);
// can this ever happen? if it does, don't handle it like a timeout exception
if (!pipeClient.IsConnected)
throw new Exception("did not throw exception");
pipeClient.Write(buffer, 0, buffer.Length);
}
catch (Exception e)
{
if (!e.Message.ToLower().Contains("time"))
throw;
// no server was running; launch a new instance
Launching(this, new SingleInstanceEventArgs(args));
}
}
private string ipcMutexGuid;
private string ipcNamedPipeGuid;
private Mutex ipcMutex;
}
/// <summary>
/// Stores the consolidated argument list from one or more instances of an application.
/// </summary>
public sealed class SingleInstanceEventArgs : EventArgs
{
public SingleInstanceEventArgs (string[] args) { Args = args; }
/// <summary>
/// The consolidated argument list from one or more instances of an application.
/// </summary>
public string[] Args { get; private set; }
}
How to use it:
static void Main (string[] args)
{
var singleInstanceHandler = new SingleInstanceHandler(Application.ExecutablePath) { Timeout = 200 };
singleInstanceHandler.Launching += (sender, e) =>
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm(e.Args));
};
singleInstanceHandler.Connect(args);
}
How can I check for an internet connection constantly in my application and respond if the connection is not available?
Currently I am using:
while(true) {
if(HasConnection()) {
//doSomething..
}
//stop app by 1sec
}
but it seems rather inelegant.
The accepted answer to this question on superuser describes the way the Windows determines if it has network access. You could use a similar method, but I would spawn a separate thread when your application starts that is responsible for doing the check. Have the separate thread perform the check in whatever manner you feel is the best and raise an event if the connection status changes.
You're looking for the NetworkAvailabilityChanged event.
To check for internet connectivity, you can ping a reliable website, such as Google.com.
Note that it is not possible to be notified of every change in internet connectivity (such as an ISP outage).
Use the following code:
public static class LocalSystemConnection
{
[DllImport("wininet.dll", SetLastError=true, CallingConvention = CallingConvention.ThisCall)]
extern static bool InternetGetConnectedState(out ConnectionStates lpdwFlags, long dwReserved);
/// <summary>
/// Retrieves the connected state of the local system.
/// </summary>
/// <param name="connectionStates">A <see cref="ConnectionStates"/> value that receives the connection description.</param>
/// <returns>
/// A return value of true indicates that either the modem connection is active, or a LAN connection is active and a proxy is properly configured for the LAN.
/// A return value of false indicates that neither the modem nor the LAN is connected.
/// If false is returned, the <see cref="ConnectionStates.Configured"/> flag may be set to indicate that autodial is configured to "always dial" but is not currently active.
/// If autodial is not configured, the function returns false.
/// </returns>
public static bool IsConnectedToInternet(out ConnectionStates connectionStates)
{
connectionStates = ConnectionStates.Unknown;
return InternetGetConnectedState(out connectionStates, 0);
}
/// <summary>
/// Retrieves the connected state of the local system.
/// </summary>
/// <returns>
/// A return value of true indicates that either the modem connection is active, or a LAN connection is active and a proxy is properly configured for the LAN.
/// A return value of false indicates that neither the modem nor the LAN is connected.
/// If false is returned, the <see cref="ConnectionStates.Configured"/> flag may be set to indicate that autodial is configured to "always dial" but is not currently active.
/// If autodial is not configured, the function returns false.
/// </returns>
public static bool IsConnectedToInternet()
{
ConnectionStates state = ConnectionStates.Unknown;
return IsConnectedToInternet(out state);
}
}
[Flags]
public enum ConnectionStates
{
/// <summary>
/// Unknown state.
/// </summary>
Unknown = 0,
/// <summary>
/// Local system uses a modem to connect to the Internet.
/// </summary>
Modem = 0x1,
/// <summary>
/// Local system uses a local area network to connect to the Internet.
/// </summary>
LAN = 0x2,
/// <summary>
/// Local system uses a proxy server to connect to the Internet.
/// </summary>
Proxy = 0x4,
/// <summary>
/// Local system has RAS (Remote Access Services) installed.
/// </summary>
RasInstalled = 0x10,
/// <summary>
/// Local system is in offline mode.
/// </summary>
Offline = 0x20,
/// <summary>
/// Local system has a valid connection to the Internet, but it might or might not be currently connected.
/// </summary>
Configured = 0x40,
}
if you only need to know if at least one connection is available you can try this:
InternetGetConnectedStateEx()
http://msdn.microsoft.com/en-us/library/aa384705%28v=vs.85%29.aspx
if you want to check continuously then use timer
private Timer timer1;
public void InitTimer()
{
timer1 = new Timer();
timer1.Tick += new EventHandler(timerEvent);
timer1.Interval = 2000; // in miliseconds
timer1.Start();
}
private void timerEvent(object sender, EventArgs e)
{
DoSomeThingWithInternet();
}
private void DoSomeThingWithInternet()
{
if (isConnected())
{
// inform user that "you're connected to internet"
}
else
{
// inform user that "you're not connected to internet"
}
}
public static bool isConnected()
{
try
{
using (var client = new WebClient())
using (client.OpenRead("http://clients3.google.com/generate_204"))
{
return true;
}
}
catch
{
return false;
}
}
I know this is an old question but this works great for me.
System.Net.NetworkInformation.NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
private async void NetworkChange_NetworkAvailabilityChanged(object sender, System.Net.NetworkInformation.NetworkAvailabilityEventArgs e)
{
//code to execute...
}
I subscribe to the event to a listener and it constantly checks for connection. this you can add a If statement such as:
if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
//Send Ping...
}
else
{
//other code....
}
How will you know if you have an Internet Connection? Is it enough that you can route packets to a nearby router? Maybe the machine has only a single NIC, a single gateway, and perhaps that Gateway's connection goes down but the machine can still route to the gateway and local network?
Maybe the machine has a single NIC and a dozen gateways; maybe they come and go all the time, but one of them is always up?
What if the machine has multiple NICs, but only a single gateway? Perhaps it can route to some subset of the Internet, but still has an excellent connection to a local network not connected to the Internet?
What if the machine has muliple NICs, multiple gateways, but for administrative policy reasons, still only portions of the Internet are routeble?
Do you really only care if clients have connectivity to your servers?
What kind of latency between packets is acceptable? (30ms is good, 300ms is pushing the limits of human endurance, 3000ms is intolerable long time, 960000ms is what would be required for a connection to a solar probe.) What kind of packet loss is acceptable?
What are you really trying to measure?
This would be a start but as sarnold has mentioned there are a lot of things you need to consider
You can test internet connectivity by pinging to some website like:
public bool IsConnectedToInternet
{
try
{
using (System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping())
{
string address = #"http://www.google.com";// System.Net.NetworkInformation.PingReply pingReplay = ping.Send(address);//you can specify timeout.
if (pingReplay.Status == System.Net.NetworkInformation.IPStatus.Success)
{
return true;
}
}
}
catch
{
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif//DEBUG
}
return false;
}
This code will life-saving for you.. it not only checks real internet connection but also handle the exception with indication on the console window...
after every 2 seconds
using System;
using System.Net;
using System.Threading;
using System.Net.Http;
bool check() //Checking for Internet Connection
{
while (true)
{
try
{ var i = new Ping().Send("www.google.com").Status;
if (i == IPStatus.Success)
{ Console.WriteLine("connected");
return true;
}
else { return false; }
}
catch (Exception)
{
Console.WriteLine("Not Connected");
Thread.Sleep(2000);
continue;
}
}
};
check();
using the NetworkChange.NetworkAvailabilityChanged is the most misleading answer. It check the network availablity change not the internet connection change.
we can monitor the internet connection using Windows NLM API.
using System;
using System.Runtime.InteropServices.ComTypes;
using NETWORKLIST;
namespace Components.Network.Helpers
{
public class InternetConnectionChecker : INetworkListManagerEvents, IDisposable
{
private int _cookie;
private IConnectionPoint _connectionPoint;
private readonly INetworkListManager _networkListManager;
public InternetConnectionChecker()
{
_networkListManager = new NetworkListManager();
}
public bool IsConnected()
{
return _networkListManager.IsConnectedToInternet;
}
public void StartMonitoringConnection()
{
try
{
var container = _networkListManager as IConnectionPointContainer;
if (container == null)
throw new Exception("connection container is null");
var riid = typeof(INetworkListManagerEvents).GUID;
container.FindConnectionPoint(ref riid, out _connectionPoint);
_connectionPoint.Advise(this, out _cookie);
}
catch (Exception e)
{
}
}
public void ConnectivityChanged(NLM_CONNECTIVITY newConnectivity)
{
if (_networkListManager.IsConnectedToInternet)
{
// do something based on internet connectivity
}
}
public void Dispose()
{
_connectionPoint.Unadvise(_cookie);
}
}
}
I'm using an Arduino with the Firmata library for communication to a C# application, and I want to eliminate a COM port configuration component since it can change from machine to machine...
Is it possible to:
Enumerate list of COM ports in the system? (In my googling I've seen some fairly ugly Win32 API code, hoping there's maybe a cleaner version now)
Auto-detect which COM port(s) are connected to an Arduino?
This little bit of code has performed very well for this (returns the COM port string, i.e. "COM12" if Arduino is detected):
private string AutodetectArduinoPort()
{
ManagementScope connectionScope = new ManagementScope();
SelectQuery serialQuery = new SelectQuery("SELECT * FROM Win32_SerialPort");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(connectionScope, serialQuery);
try
{
foreach (ManagementObject item in searcher.Get())
{
string desc = item["Description"].ToString();
string deviceId = item["DeviceID"].ToString();
if (desc.Contains("Arduino"))
{
return deviceId;
}
}
}
catch (ManagementException e)
{
/* Do Nothing */
}
return null;
}
You can use SerialPort.GetPortNames() to return an array of string COM port names.
I dont think you can auto detect the ports, youd have to ping the device in order to see if the device is connected.
Taking the WMI Management route a bit further, I've come up with a wrapper class which hooks on to the Win32_SerialPorts events and dynamically populated a list of SerialPorts for Arduino and Digi International (X-Bee) devices, complete with PortNames and BaudRates.
For now, I've used the devices Description field in the Win32_SerialPorts entry as the Key for the Dictionary, but this can easily be changed.
It has been tested to a limited capacity with an Arduino UNO and it seems to be stable.
// -------------------------------------------------------------------------
// <copyright file="ArduinoDeviceManager.cs" company="ApacheTech Consultancy">
// Copyright (c) ApacheTech Consultancy. All rights reserved.
// </copyright>
// <license type="GNU General Public License" version="3">
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses
// <license>
// -------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Runtime.CompilerServices;
// Automatically imported by Jetbeans Resharper
using ArduinoLibrary.Annotations;
namespace ArduinoLibrary
{
/// <summary>
/// Provides automated detection and initiation of Arduino devices. This class cannot be inherited.
/// </summary>
public sealed class ArduinoDeviceManager : IDisposable, INotifyPropertyChanged
{
/// <summary>
/// A System Watcher to hook events from the WMI tree.
/// </summary>
private readonly ManagementEventWatcher _deviceWatcher = new ManagementEventWatcher(new WqlEventQuery(
"SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2 OR EventType = 3"));
/// <summary>
/// A list of all dynamically found SerialPorts.
/// </summary>
private Dictionary<string, SerialPort> _serialPorts = new Dictionary<string, SerialPort>();
/// <summary>
/// Initialises a new instance of the <see cref="ArduinoDeviceManager"/> class.
/// </summary>
public ArduinoDeviceManager()
{
// Attach an event listener to the device watcher.
_deviceWatcher.EventArrived += _deviceWatcher_EventArrived;
// Start monitoring the WMI tree for changes in SerialPort devices.
_deviceWatcher.Start();
// Initially populate the devices list.
DiscoverArduinoDevices();
}
/// <summary>
/// Gets a list of all dynamically found SerialPorts.
/// </summary>
/// <value>A list of all dynamically found SerialPorts.</value>
public Dictionary<string, SerialPort> SerialPorts
{
get { return _serialPorts; }
private set
{
_serialPorts = value;
OnPropertyChanged();
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
// Stop the WMI monitors when this instance is disposed.
_deviceWatcher.Stop();
}
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Handles the EventArrived event of the _deviceWatcher control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArrivedEventArgs"/> instance containing the event data.</param>
private void _deviceWatcher_EventArrived(object sender, EventArrivedEventArgs e)
{
DiscoverArduinoDevices();
}
/// <summary>
/// Dynamically populates the SerialPorts property with relevant devices discovered from the WMI Win32_SerialPorts class.
/// </summary>
private void DiscoverArduinoDevices()
{
// Create a temporary dictionary to superimpose onto the SerialPorts property.
var dict = new Dictionary<string, SerialPort>();
try
{
// Scan through each SerialPort registered in the WMI.
foreach (ManagementObject device in
new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_SerialPort").Get())
{
// Ignore all devices that do not have a relevant VendorID.
if (!device["PNPDeviceID"].ToString().Contains("VID_2341") && // Arduino
!device["PNPDeviceID"].ToString().Contains("VID_04d0")) continue; // Digi International (X-Bee)
// Create a SerialPort to add to the collection.
var port = new SerialPort();
// Gather related configuration details for the Arduino Device.
var config = device.GetRelated("Win32_SerialPortConfiguration")
.Cast<ManagementObject>().ToList().FirstOrDefault();
// Set the SerialPort's PortName property.
port.PortName = device["DeviceID"].ToString();
// Set the SerialPort's BaudRate property. Use the devices maximum BaudRate as a fallback.
port.BaudRate = (config != null)
? int.Parse(config["BaudRate"].ToString())
: int.Parse(device["MaxBaudRate"].ToString());
// Add the SerialPort to the dictionary. Key = Arduino device description.
dict.Add(device["Description"].ToString(), port);
}
// Return the dictionary.
SerialPorts = dict;
}
catch (ManagementException mex)
{
// Send a message to debug.
Debug.WriteLine(#"An error occurred while querying for WMI data: " + mex.Message);
}
}
/// <summary>
/// Called when a property is set.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
[NotifyPropertyChangedInvocator]
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Try this, I'm working on a very similar project, also anyone please feel free to edit this!
In the setup portion of the Arduino code, I have it call a setupComms() method, this method simply prints an "A" until it receives an "a". Once "a" is received it jumps to the main loop() function. So the C# portion checks each available port for "A" and if "A" is found we know that we have opened the port to the Arduino!
Again, this may not be very clean but it does work, I am open to any comments and suggestions!
foreach (string s in SerialPort.GetPortNames())
{
com.Close(); // To handle the exception, in case the port isn't found and then they try again...
bool portfound = false;
com.PortName = s;
com.BaudRate = 38400;
try
{
com.Open();
status.Clear();
status.Text += "Trying port: " + s+"\r";
}
catch (IOException c)
{
status.Clear();
status.Text += "Invalid Port"+"\r";
return;
}
catch (InvalidOperationException c1)
{
status.Clear();
status.Text += "Invalid Port" + "\r";
return;
}
catch (ArgumentNullException c2)
{
// System.Windows.Forms.MessageBox.Show("Sorry, Exception Occured - " + c2);
status.Clear();
status.Text += "Invalid Port" + "\r";
return;
}
catch (TimeoutException c3)
{
// System.Windows.Forms.MessageBox.Show("Sorry, Exception Occured - " + c3);
status.Clear();
status.Text += "Invalid Port" + "\r";
return;
}
catch (UnauthorizedAccessException c4)
{
//System.Windows.Forms.MessageBox.Show("Sorry, Exception Occured - " + c);
status.Clear();
status.Text += "Invalid Port" + "\r";
return;
}
catch (ArgumentOutOfRangeException c5)
{
//System.Windows.Forms.MessageBox.Show("Sorry, Exception Occured - " + c5);
status.Clear();
status.Text += "Invalid Port" + "\r";
return;
}
catch (ArgumentException c2)
{
//System.Windows.Forms.MessageBox.Show("Sorry, Exception Occured - " + c2);
status.Clear();
status.Text += "Invalid Port" + "\r";
return;
}
if (!portfound)
{
if (com.IsOpen) // Port has been opened properly...
{
com.ReadTimeout = 500; // 500 millisecond timeout...
sent.Text += "Attemption to open port " + com.PortName + "\r";
try
{
sent.Text += "Waiting for a response from controller: " + com.PortName + "\r";
string comms = com.ReadLine();
sent.Text += "Reading From Port " + com.PortName+"\r";
if (comms.Substring(0,1) == "A") // We have found the arduino!
{
status.Clear();
status.Text += s + com.PortName+" Opened Successfully!" + "\r";
//com.Write("a"); // Sends 0x74 to the arduino letting it know that we are connected!
com.ReadTimeout = 200;
com.Write("a");
sent.Text += "Port " + com.PortName + " Opened Successfully!"+"\r";
brbox.Text += com.BaudRate;
comboBox1.Text = com.PortName;
}
else
{
sent.Text += "Port Not Found! Please cycle controller power and try again" + "\r";
com.Close();
}
}
catch (Exception e1)
{
status.Clear();
status.Text += "Incorrect Port! Trying again...";
com.Close();
}
}
}
}
All of the Try Catch statements are in there from when I was originally testing, this has worked for me so far. Good luck!
I just had a similar challenge with a Teensyduino communicating with a PC based processing language program. It may be useful for someone working with something Java or processing language rather than C#.
The basic idea of this solution was to send a handshake request ("!sh\n") to each Serial port and then listen for a response ("$h\n") from each device until the correct handshake response was received. Thereby showing which of the ports was the device I was seeking.
Also, I'm quite new to StackOverflow so please forgive and educate me if I'm breaking any StackOverflow etiquette in this answer.
Processing Language Code:
import processing.serial.*;
int ellipticalWalkerTeensyIndex; /* Represents Elliptical Walker Serial Port index in the Serial.list() String array. */
boolean latch;
String[] serialPortList = Serial.list();
int serialPortCount = serialPortList.length;
Serial[] ports = new Serial[serialPortCount];
int[] serialConnected = new int[serialPortCount];
void setup(){
for (int z = 0; z < serialPortCount; ++z) { /* Initialise serialConnected array to 0; Anything not marked to 1 later will be ignored. */
serialConnected[z] = 0;
}
ellipticalWalkerTeensyIndex = -1; /* Initialise ellipticalWalkerTeensyIndex to -1, as the correct serial port is not yet known. */
latch = false;
for (int z = 0; z < serialPortCount; ++z) {
try {
ports[z] = new Serial(this, serialPortList[z], 9600);
serialConnected[z] = 1; /* Mark this index as connected. */
ports[z].write("!sh"); /* Send handshake request; Expected response is "$h\n" */
}catch (Exception e){
println("Could not connect to "+Integer.toString(z)+" exception details: "+e);
}
}
}
void draw(){
if (ellipticalWalkerTeensyIndex < 0) {
for (int z = 0; z < serialPortCount; ++z) {
if(serialConnected[z]>0){ /* Only attempt communication if we have marked this serial port as connected during the setup routine. */
if (ports[z].available()>0) { /* Read from serial port 'z' if data is available. */
String lineOfData = ports[z].readStringUntil('\n');
if(lineOfData.charAt(0)=='$' && lineOfData.charAt(1)=='h'){ /* Check if received response matches expected handshake response */
ellipticalWalkerTeensyIndex = z; /* Note the correct serial port for the teensy. */
}
}
}
}
}else{
if (!latch) {
println("The teensyduino is on serial port: "+serialPortList[ellipticalWalkerTeensyIndex]);
latch = true;
exit();
}
}
}
Runtime results:
PS C:\repos\elliptical_walker> processing-java --sketch=c:\repos\elliptical_walker\EW0 --run
The teensyduino is on serial port: COM3
Finished.
I've noticed that my Chinese clone of Arduino nano shows up the COM port correctly in Devices Manager, but it doesn't show on the C# app dorp down list when you try and get all ports using this command:
using (var searcher = new ManagementObjectSearcher("SELECT * FROM WIN32_SerialPort"));
However, it is shown correctly when executing:
foreach (string z in SerialPort.GetPortNames())
So I have 2 lists: one with output of 1st code, and one with output from 2nd code. When comparing both, it will find the correct COM port. Obviously, when using Original Andurino Uno both commands displays port correctly so this solution will only work for Chinese clones which, for some odd reason, are invisible to using (var searcher = new ManagementObjectSearcher("SELECT * FROM WIN32_SerialPort"));
This method does not help you find out which port your arduino is connected to your computer
SerialPort.GetPortNames Method ()
// Get a list of serial port names.
string[] ports = SerialPort.GetPortNames();
Console.WriteLine("The following serial ports were found:");
Console.WriteLine("Aşşağıda Seri Bağlantı Noktaları Bulundu:");//For Turkish
// Display each port name to the console.
foreach(string port in ports)
{
Console.WriteLine(port);
}
Console.ReadLine();
With this php script you can transmit and receive data from arduino
<?php
/**
* Remember to go to Device Manager> Ports (COM & LPT)>Arduino XXX (COMXX)>right
* click>Properties>
* Port Settings>Advanced>uncheck "use FIFO buffers ........."
* In other hand, remeber that the Tx speed has to be the same in PhpConnect.php, in
* Arduino sketch and in the COM
* properties in Device manager, I selected 115200 b/s.
*
*/
// RX form PC**************
$t = $_POST['text1'];
include 'PruebaBatchCOM.php';
$puerto = escapeshellarg($usbCOM);
$dato = escapeshellarg($t);
exec("node C:\\xampp\\htdocs\\DisenoWEBTerminados\\BatteryTester\\Scripts\\writeandread.js {$puerto} {$dato} 2>&1", $output1);
$str = implode($output1);
$str1 = explode(",",$str);
$myJSON = json_encode($str1);// this is the response to AJAX
echo $myJSON;
?>
PruebaBatchCOM.php is
<?php
$puerto = array();
$file111 = "PruebaCOMRetrieve.bat";
exec($file111, $puerto);
$usbCOM = implode(",",$puerto);
?>
PruebaCOMRetrieve.bat
#echo off
setlocal
for /f "tokens=1* delims==" %%I in ('wmic path win32_pnpentity get caption
/format:list ^| find "Arduino Uno"') do (
call :setCOM "%%~J"
)
:: end main batch
goto :EOF
:setCOM <WMIC_output_line>
:: sets _COM#=line
setlocal
set "str=%~1"
set "num=%str:*(COM=%"
set "num=%num:)=%"
set port=COM%num%
echo %port%
I am attempting to use the .BaseStream property of the .NET2.0 SerialPort to do asynchronous reads and writes (BeginWrite/EndWrite, BeginRead/EndRead).
I am having some success in this, but after a time, I notice (using Process Explorer) a very gradual increase in the Handles the app is using, and occasionally an extra thread, which also increases the Handle count.
The context switch rate also increases each time a new thread appears.
The app constantly sends 3 bytes to a PLC device, and gets 800 or so bytes in return, and does so at a baud rate of 57600.
The initial CSwitch Delta (again, from Process Explorer) is around 2500, which seems very high anyway. Each time a new thread appears, this value increases, and the CPU load increases accordingly.
I'm hoping that somebody might have done something similar, and can help me out, or even say 'In God's name, don't do it that way.'
In the code below, 'this._stream' is obtained from SerialPort.BaseStream, and CommsResponse is a class I use as the IAsyncresult state object.
This code is common to a TCP connection I make as an alternative to using the serial port, (I have a CommsChannel base class, with a serial and TCP channel derived from it) and it has none of these problems so I'm reasonably hopeful that there is nothing wrong with the CommsResponse class.
Any comments gratefully received.
/// <summary>
/// Write byte data to the channel.
/// </summary>
/// <param name="bytes">The byte array to write.</param>
private void Write(byte[] bytes)
{
try
{
// Write the data to the port asynchronously.
this._stream.BeginWrite(bytes, 0, bytes.Length, new AsyncCallback(this.WriteCallback), null);
}
catch (IOException ex)
{
// Do stuff.
}
catch (ObjectDisposedException ex)
{
// Do stuff.
}
}
/// <summary>
/// Asynchronous write callback operation.
/// </summary>
private void WriteCallback(IAsyncResult ar)
{
bool writeSuccess = false;
try
{
this._stream.EndWrite(ar);
writeSuccess = true;
}
catch (IOException ex)
{
// Do stuff.
}
// If the write operation completed sucessfully, start the read process.
if (writeSuccess) { this.Read(); }
}
/// <summary>
/// Read byte data from the channel.
/// </summary>
private void Read()
{
try
{
// Create new comms response state object.
CommsResponse response = new CommsResponse();
// Begin the asynchronous read process to get response.
this._stream.BeginRead(this._readBuffer, 0, this._readBuffer.Length, new AsyncCallback(this.ReadCallback), response);
}
catch (IOException ex)
{
// Do stuff.
}
catch (ObjectDisposedException ex)
{
// Do stuff.
}
}
/// <summary>
/// Asynchronous read callback operation.
/// </summary>
private void ReadCallback(IAsyncResult ar)
{
// Retrieve the comms response object.
CommsResponse response = (CommsResponse)ar.AsyncState;
try
{
// Call EndRead to complete call made by BeginRead.
// At this point, new data will be in this._readbuffer.
int numBytesRead = this._stream.EndRead(ar);
if (numBytesRead > 0)
{
// Create byte array to hold newly received bytes.
byte[] rcvdBytes = new byte[numBytesRead];
// Copy received bytes from read buffer to temp byte array
Buffer.BlockCopy(this._readBuffer, 0, rcvdBytes, 0, numBytesRead);
// Append received bytes to the response data byte list.
response.AppendBytes(rcvdBytes);
// Check received bytes for a correct response.
CheckResult result = response.CheckBytes();
switch (result)
{
case CheckResult.Incomplete: // Correct response not yet received.
if (!this._cancelComm)
{
this._stream.BeginRead(this._readBuffer, 0, this._readBuffer.Length,
new AsyncCallback(this.ReadCallback), response);
}
break;
case CheckResult.Correct: // Raise event if complete response received.
this.OnCommResponseEvent(response);
break;
case CheckResult.Invalid: // Incorrect response
// Do stuff.
break;
default: // Unknown response
// Do stuff.
break;
}
}
else
{
// Do stuff.
}
}
catch (IOException ex)
{
// Do stuff.
}
catch (ObjectDisposedException ex)
{
// Do stuff.
}
}
Some suggestions:
Since you are only sending 3 bytes, you could have a synchronous Write Operation. The delay wouldn't be much of an issue.
Also don't create a new AsyncCallback all the time. Create one Read and one Write AsyncCallback and use that in every begin call.
No need at all for BeginWrite. You only send 3 bytes, they'll easily fit in the transmit buffer, and you're always sure that the buffer is empty when you send the next set.
Keep in mind that serial ports are much slower than TCP/IP connections. It is pretty likely that you end up calling BeginRead() for every single byte that you receive. That gives the thread pool a good workout, you'd definitely see a lot of context switches. Not so sure about handle consumption. Be sure to test that without the debugger attached.
Trying DataReceived instead of BeginRead() is definitely something you should try. Pull instead of push, you'll use a threadpool thread when there's something happening instead of always having one active.
Is it possible to take the data comming in from the serial port and directly send it to a file? At high a baud rates (1 MegaBaud) it is difficult to handle this amount of non-stop data.
Is the response from the device always a fixed size? If so, try using SerialPort.Read and pass the packet size. This will block, so combine it with DataReceived. Better yet, if the response always ends with the same character(s), and this ending signature is guaranteed to be unique in the packet, set the NewLine property and use ReadLine. This will immunize you against future packet size changes.