Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Null Modem Emulator and C# - How They Can Learn To Love One Another

0.00/5 (No votes)
1 Dec 2014 1  
A simple example of a C# Application interfacing with the Null Modem Emulator (com0com) driver to allow run time creation and configuration of Virtual Serial Ports

Introduction

This is a simple example of a C# Application interfacing with the Null Modem Emulator (com0com) driver to allow run time creation and configuration of Virtual Serial Ports

Background

This originally started as a hard coded solution in our environment. We use a lot of serial com port interfaces to connect to various modems and radio transceivers. As part of the testing environment, a physical device had to be connected to the test machine and the serial communications were split to a second machine to monitor and interpret the resulting data.

During a recent environment change, this hardware based split has become unfeasible and too costly to maintain. We then decided to implement a virtual serial ports via Null Modem Emulator. The current configuration works perfectly, but maintaining and modifying the environment has become more difficult for non-IT staff.

Thus, the need was born to develop a unified solution that will present an all inclusive, user friendly platform to initialize and test various hardware and software.

This brings us to the purpose of the tip, a simple way to interface with null modem, issue commands and read feedback.

Using the Code

Null Modem can be controlled via the executable by issuing a command flag to the application with any required flags. We can launch external applications directly from C# and through that mechanism, we can control Null Modem. After execution, Null Modem provides the relevant feedback on the task executed.

Sending Commands to Null Modem

private static string execute_command(string command)
        {
            // Start the child process.
            Process p = new Process();
            //Set parameters to redirect input/output from the application
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardInput = true;
            //Hide the application window
            p.StartInfo.CreateNoWindow = true;
            
            //Set the Application Path and Working to directory to the location of setupc.exe 
            p.StartInfo.WorkingDirectory = "c:\\NullModem\\";
            p.StartInfo.FileName = "c:\\NullModem\\setupc.exe";
            
            //Append command and flags and execute the process
            p.StartInfo.Arguments = " " + command;
            p.Start();

            string output = "";

            output += p.StandardOutput.ReadToEnd() + "\r\n";
            p.WaitForExit();
            return output
       }

Example

List all of the available null modem ports

execute_command("list");
Output from Null Modem
             CNCA0 PortName=COM25
             CNCB0 PortName=COM26

Install a new port pair

execute_command("install PortName=COM26,EmuBR=yes,EmuOverrun=yes,
cts=ropen PortName=COM25,EmuBR=yes,EmuOverrun=yes,cts=ropen");
Output from Null Modem
       CNCA2 PortName=COM100,EmuBR=yes,EmuOverrun=yes,cts=ropen
       CNCB2 PortName=COM101,EmuBR=yes,EmuOverrun=yes,cts=ropen
ComDB: COM100 - logged as "in use"
ComDB: COM101 - logged as "in use"

Interpreting the Results

The following method groups the received lines from Null Modem into a logical port pair. Each line is parsed and a NullModemPort object is generated.

private static List<NullModemPair> Parse_NME_Data(string data)
        {
            var lstNMP = new List<NullModemPair>();

            var nmp = new NullModemPair();

            foreach(var x in data.Split('\n'))
            {
                var p = Parse_NME_Line(x);
                if (!string.IsNullOrEmpty(p.nme_Name))
                {
                    if (p.is_A)
                    {
                        nmp.Index = p.Index;
                        nmp.A = p;
                    }else if (nmp.Index == p.Index)
                    {
                        nmp.B = p;

                        lstNMP.Add(new NullModemPair(nmp.Index, nmp.A, nmp.B));

                        nmp = new NullModemPair();
                    }
                }
            }

            return lstNMP;
        }

To parse the individual lines from Null Modem, we use Regex to look for familiar patterns in the results.

private static NullModemPort Parse_NME_Line(string line)
        {
            /* REGEX Formulas                               
             * ---------------------------------------------*
             * Get CNC Name:        (CNC[AB][0-9]*)         *
             * Get A/B grouping:    CNC([AB])[0-9]*.        *
             * Get Index:           CNC[AB]([0-9]*)         *
             * Get Portname:        PortName=(.*?),|\n      *
             * Get Parameters:      [A-Z][0-9]?.*?,(.*)     *
             * ---------------------------------------------*
            */

            var p = new NullModemPort();

            //Get CNC Name
            Regex rgx = new Regex("(CNC[AB][0-9]*)", RegexOptions.IgnoreCase);
            MatchCollection matches = rgx.Matches(line);
            foreach (Match match in matches)
                p.nme_Name = match.Groups[1].Value;

            //Get A/B
            rgx = new Regex("CNC([AB])[0-9]*. ", RegexOptions.IgnoreCase);
            matches = rgx.Matches(line);
            foreach (Match match in matches)
            {
                if (match.Groups[1].Value.Contains("A"))
                    p.is_A = true;
                else
                    p.is_A = false;
            }

            //Get Index
            rgx = new Regex("CNC[AB]([0-9]*)", RegexOptions.IgnoreCase);
            matches = rgx.Matches(line);
            foreach (Match match in matches)
                int.TryParse(match.Groups[1].Value, out p.Index);

            //Get Portname
            rgx = new Regex("PortName=(.*?),|\n", RegexOptions.IgnoreCase);
            matches = rgx.Matches(line);
            foreach (Match match in matches)
                p.PortName = match.Groups[1].Value;
            
            //Get Parameters
            string pars = "";
            rgx = new Regex("[A-Z][0-9]?.*?,(.*)", RegexOptions.IgnoreCase);
            matches = rgx.Matches(line);
            foreach (Match match in matches)
                pars = match.Groups[1].Value;

            p.EmulateBaudRate = false;
            p.BufferOverrun = false;
            p.CTS_Open = false;

            if (pars.Contains("EmuBR=yes"))
                p.EmulateBaudRate = true;

            if (pars.Contains("EmuOverrun=yes"))
                p.BufferOverrun = true;

            if (pars.Contains("cts=ropen"))
                p.CTS_Open = true;

            return p;
        }

The port object is returned to the grouping method. The method then pairs and stores the information.

This will allow us to have easy access to the information throughout the rest of the program.

Points of Interest

The sample application as a whole is very loosely constructed and only meant to demonstrate the mechanisms through which we can access Null Modem and issue commands to it.

A lot of optimization can be done to how the process is called and results are retrieved.

Additionally, the structure of the NullModemInterface and the resulting hierarchy should be redone to fit in with the requirements of the application.

Error handling has been omitted for the purposes of the demonstration. Some thought and care should be put into adding proper exception handling into the application.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here