Click here to Skip to main content
15,883,810 members
Articles / Programming Languages / C#
Tip/Trick

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

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
1 Dec 2014CPOL2 min read 18.3K   12   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

C#
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

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

Install a new port pair

C#
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.

C#
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.

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO
South Africa South Africa
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionError: The requested operation requires elevation Pin
Member 785581811-Apr-18 4:49
Member 785581811-Apr-18 4:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.