Enhanced Serial Port Object for Visual Studio™ 2008 - 2013
Copyright © Richard L. Grier 2010
Hard & Software
Visual Studio 2005 and later editions have delivered a basic Serial Port object. It appears as the SerialPort control in the toolbox, which is a container for the System.IO.Ports.SerialPort type. This SerialPort object is adequate for most applications, and I have used it in a number of projects. However, it does have some limitations.
1. If a serial port that uses a USB adapter (these employ a Virtual Serial Port UART that is implemented as a Windows™ device driver) is opened and the adapter is physically removed, a potential serious problem is encountered.
As far as the SerialPort object is aware, the port still is open and available. The removal notification is not recognized by the SerialPort object. If the application then attempts to write to the port an exception is generated – and a read my result in an apparent “application lock-up.”
We need to make this more robust and there are a couple of techniques to apply to this end.
2. The SerialPort GetPortNames() method returns an array of type String with a list of the names of all serial port devices that are installed on the PC. This allows us to provide a list for the user from which to select. On occasion, however, users will not know which of many possible ports to select. The device driver that is installed for many serial devices also provides a Port Description string that might be useful if it, too, could be provided with the Port Name, to make selection easier.
Let’s add that capability.
3. The serial port write does not execute via a background thread, and all data that is to be written must be buffered by the Windows serial port driver before execution may continue by the calling code. In some cases this can cause the application to appear to be unresponsive.
Designing a robust background set of serial port write methods is somewhat complex.
Methods were added to the Enhanced Serial Port object to allow data to be transmitted in a “non-blocking” fashion. An Overloaded method, QueuedWrite, which accepts either a String or an array of type Byte as an argument was designed. The QueuedWrite methods add data to a Generic Queue (TxQueue), and a Background thread is created that extracts data from the TxQueue and actually sends it using the standard SerialPort Write methods. This decouples the SerialPort Write, which is a blocking method. The QueuedWrite method is non-blocking, so it returns control to the calling code as soon as the data have been queued.
The QueuedWrite methods break any data with Length longer than the SerialPort WriteBufferSize into smaller “chunks” that are each added to the TxQueue, theoretically allowing arbitrarily long non-blocking transmissions. These queued operations are integrated with the Watchdog function.
How should we tackle these other problems?
My first thought was to create a user control that inherited System.IO.Ports.SerialPort and use overrides to change the behavior that is causing trouble, and then to extend the inherited code to add methods to return Port Descriptions and future background write operations.
Unfortunately, the methods that need to be overridden are not marked “Overridable and/or Overloadable” thus using inheritance is not possible. So, I had to encapsulate the built-in SerialPort object instead. This means that I had to provide all of the same methods and properties that are used in the built-in object, and to manipulate the encapsulated SerialPort object through these methods. This simply requires extra coding, but does not, materially affect the design.
Step 1
After writing all of the wrapper code to provide the same interface that the built-in SerialPort object exposes, I added extensions to do three things:
When a Watchdog is activated, the code closes the associated port and attempts to reopen it at regular intervals. The reopen interval and duration are controlled by like named properties. The Watchdog operation provides event notifications to the calling application that track the Watchdog process.
In addition, all SerialPort write operations are wrapped with code that traps the exception that would be generated if the USB device was no longer available. The consequence of this change is that the ExhancedSerialPort object does not raise an exception if one attempts to write to a port that has not been opened, or has been closed intentionally. The exception that is generated when a write is attempted to a removed device now calls the same Watchdog code that closes the port and then attempts to reopen it.
The Watchdog does not eliminate the loss of data that would accompany the removal of a USB adapter. So be aware, if a Watchdog event happens, there is a chance that some data has been lost. Download the Enhanced SerialPort code now.
Step 2
An additional method has been added that uses System.Management to return a list of the descriptions for all installed serial devices. This list should include only devices that actually are connected and enabled.
Here is the Object Model. Notice the similarities and additions. I intend that any code that works with the built-in SerialPort object continue to work with this Enhanced version, without any change to code. The only code changes that will be needed are for the additional Watchdog and Port Description features.
The Test Enhanced Serial Port application illustrates the use of this SerialPort object.
Download the Enhanced SerialPort code now.
For more information, examples, and utility programs -- check out my book, Visual Basic Programmer's Guide to Serial Communications 5.