Search code examples
c#serial-portautomated-testsvirtual-serial-portcom0com

Creating a pair between a real and virtual serial port


I am working on a utility that automates the testing of a legacy application. The legacy application runs on Windows 2000, and uses 3 devices connected via serial (COM) ports.

I have access to the application's source code but don't have the go-ahead to make any changes. However, the source code has allowed me to verify that device COM port names are hard coded into the application as COM1 (a printer), COM2 (a bank note reader) and COM3 (a barcode/OMR reader). I can also see the bytes that are sent to each device, and how the application processes the returns.

For now, I would like to write tests regarding the bank note reader. I read from various questions here (e.g. Faking an RS232 Serial Port) that I can use com0com to fake a serial port. I was hoping that there was a way to send bytes on behalf of a real COM port device that will be picked up by the application.

After installing the utility I configured a pair as "COM2" to "COM6". The COM2 label appears in red, and when I press OK I get the following message:

The port name COM2 is already used for other device \Device\Serial1

I can then press Cancel, Try Again or Continue.

On pressing Continue I get:

The port name COM2 is already logged as "in use" in the COM port database

Again I press Continue.

I wrote a small application in C# to test the COM2 and COM6 port pairing. When I send valid bytes to COM2 I get the expected 11 bytes back (which mirrors a scenario exactly in the legacy application). Nothing is received on COM6. When I send the same bytes to COM6 I get no response from either COM2 or COM6.

I have tried rebooting the machine, setting up the pairing again, trying different COM ports (COM1 to COM6, COM3 to COM6) but never get a response on these virtual ports (COM1 and COM3 are also in red, and give me the same errors via com0com).

The test C# app is as follows:

using System;
using System.IO.Ports;

public class Com
{
    private static byte[] s_pDataSent = new byte[32];
    private static byte[] s_pDataRead = new byte[32];

    private static void port_OnReceivedDatazz(
        object sender,
        SerialDataReceivedEventArgs e)
    {
        var sp = (SerialPort)sender;
        var buffer = new byte[sp.BytesToRead];
        Console.WriteLine("DATA RECEIVED ON " + sp.PortName);
        sp.Read(buffer, 0, buffer.Length);
        foreach(var b in buffer)
        {
            Console.Write(b.ToString() + " ");
        }
        Console.WriteLine();
    }

    public void Do(string portName, string virtualPort)
    {
        var port = new SerialPort(
            portName,
            9600,
            Parity.Even,
            7,
            StopBits.One);
        port.RtsEnable = false;
        port.DtrEnable = false;
        port.DataReceived += port_OnReceivedDatazz;
        port.Open();

        if (!port.IsOpen)
        {
            Console.WriteLine("Port is closed");
        }

        var vport = new SerialPort(
            virtualPort,
            9600,
            Parity.Even,
            7,
            StopBits.One);
                vport.RtsEnable = false;
                vport.DtrEnable = false;
                vport.DataReceived += port_OnReceivedDatazz;
                vport.Open();

        if (!vport.IsOpen)
        {
            Console.WriteLine("VPort is closed");
        }

        for (var i = 0; i < s_pDataSent.Length; ++i)
        {
            s_pDataSent[i] = 0;
        }

        // populate s_pDataSent ...
        // I have left these steps out of this example
        // ...but they do exist in my test app

        port.Write(
            s_pDataSent,
            0,
            8);

        Console.ReadLine();
        Console.WriteLine("Closing");
        port.Close();
    }
}

When I use HyperTerminal, I can see messages being sent between 2 virtual ports but nothing is being sent between a real and virtual port.

Is it possible to pair a real and virtual COM port, via com0com or otherwise?

If so, how can I resolve my problem?

Is there an alternative to faking outputs from a real serial port?


Solution

  • I managed to get a fully automated test system up and running by:

    • Using com0com to set up 2 virtual port pairings (COM6-COMD and COMI-COMJ).
    • Writing a simple application that emulated the device by writing back a set of bytes to COMD when something read on COMD. The bytes written back would be a default set of "nothing to do" bytes unless test bytes were also read on COMJ.
    • Added functionality to my automated test scripts that would be capable of sending specific bytes on COMI. For example, the bytes that represented a £5 note being read or the note being invalid.
    • Modified the legacy executable with an hex editor to replace the "COM2:" ascii string (i.e. hard-coded port) to "COM6:".

    I can now have a test terminal set up to use the original legacy application for manual testing. All of the devices work correctly. On the same terminal, the automated test scripts are linked to a modified executable. This executable will use emulated devices.

    I need to maintain a "modified" executable until work is done to make the COM ports configurable in the application.

    One minor observation was that I needed to use "real" COM port names in the modified exe. I couldn't use "COMA" for example. It needed to be COM1 to COM9.