Introduction
As RS-232 is still used often in industrial applications, Microsoft has added the SerialPort
class to its .NET 2.0 framework.
When communicating with a Windows Forms application and displaying the received data, for instance in a TextBox
, the problem occurs, that the serial port and the textbox are using different threads. MSDN describes a way to solve this problem.
As an example of how to apply all this, using Visual C#, a small application has been built:

First of all, you have to place, from the Toolbox=>Components, a SerialPort
onto the Form.
Then, in the Properties, set the serial port to 9600,8N1 and the port name to X (don't be alarmed; after the application has started, you will get the opportunity to choose a proper COM-name!)
Now, place the top-textbox (txtIn
), showing received data, and the lower textbox (txtOut
), showing the data to be send after clicking the Send button. Optionally, you could also place a SttusStrip
(as I did).
Add a ComboBox
(cmbComSelect
) with DropDownStyle
set to DropDown
and Sorted
set to true
.
The Namespace
The System.IO.Ports
namespace contains classes for controlling serial ports. The most important class is the SerialPort
class.
The Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
namespace RS232
{
public partial class fclsRS232Tester : Form
{
string InputData = String.Empty;
delegate void SetTextCallback(string text);
public fclsRS232Tester()
{
InitializeComponent();
string[] ports = SerialPort.GetPortNames();
foreach (string port in ports)
{
cmbComSelect.Items.Add(port);
}
}
private void cmbComSelect_SelectionChangeCommitted(object sender,
EventArgs e)
{
if (port.IsOpen) port.Close();
port.PortName = cmbComSelect.SelectedItem.ToString();
stsStatus.Text = port.PortName + ": 9600,8N1";
try
{
port.Open();
}
catch
{
MessageBox.Show("Serial port " + port.PortName +
" cannot be opened!", "RS232 tester",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
cmbComSelect.SelectedText = "";
stsStatus.Text = "Select serial port!";
}
}
private void btnSend_Click(object sender, EventArgs e)
{
if (port.IsOpen) port.WriteLine(txtOut.Text);
else MessageBox.Show("Serial port is closed!",
"RS232 tester",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
txtOut.Clear();
}
private void btnClear_Click(object sender, EventArgs e)
{
txtIn.Clear();
}
private void port_DataReceived_1(object sender,
SerialDataReceivedEventArgs e)
{
InputData = port.ReadExisting();
if (InputData != String.Empty)
{
SetText(InputData);
}
}
private void SetText(string text)
{
if (this.txtIn.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else this.txtIn.Text += text;
}
}
}
Getting the COM-port
The SerialPort
gives us a nice method to find all COM-ports available in the computer:
string[] ports = SerialPort.GetPortNames();
Now that we have all ports available, we fill the Items
list of the ComboBox
:
foreach (string port in ports)
{
cmbComSelect.Items.Add(port);
}
and, through the SelectionChangeCommitted
event from the ComboBox
, we can choose a particular COM-port.
Because the selected COM-port may be in use, we try to open it, and if an exception is thrown, we catch it and display a proper message.
Achieving thread-safety
The statement txtIn.Text = InputData;
will cause problems on receiving data, because the Receive thread is different from the thread which started the textbox!!
To solve this problem in a thread-safe way, we use the following statement instead:
SetText(InputData);
First, we have to declare a delegate method:
delegate void SetTextCallback(string text);
and then this method can be defined as:
private void SetText(string text)
{
if (this.txtIn.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else this.txtIn.Text += text;
}