Accessing COM Ports

Environment: C#, .NET

In this column, we'll explore how to communicate with COM ports of your computer. We will cover how to send commands to the port and how to read information from ports.

Background

The whole idea behind this work was to access a GPRS/GSM modem connected to COM port to send and receive SMS messages through Windows XP service.

There is a nice article about Creating a Windows Service in .NET by Mark Strawmyer here.

There are not many possible ways to include this functionality. There were attempts to attach some COMMLIB DLL files. As the application itself is a service, so the use of System.Windows.Forms was to be ignored.

The Approach

The way out was to import the access functions from kernel32. A standard way to access API functions is using DLL Import. So, I decided to import three functions:

  1. Create File—to open the COM port.
  2. Write File—to send commands.
  3. Read File—to read information.

And of course, you won't get away without using the Get Last Error.

//file open masks
const uint GENERIC_READ  = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const uint OPEN_EXISTING = 3;

[DllImport("kernel32", SetLastError=true)]
static extern unsafe int CreateFile(
  string filename,       // file name
  uint desiredAccess,    // read? write?
  uint shareMode,        // sharing
  uint attributes,       // SecurityAttributes pointer
  uint creationDisposition,
  uint flagsAndAttributes,
  uint templateFile);

[DllImport("kernel32", SetLastError=true)]
static extern unsafe bool ReadFile(int hFile,  // handle to file
  void* lpBuffer,                              // data buffer
  int nBytesToRead,                            // number of bytes
                                               // to read
  int* nBytesRead,                             // number of bytes
                                               // read
  int overlapped);                             // overlapped buffer

[DllImport("kernel32", SetLastError=true)]
static extern unsafe bool WriteFile(int hFile, // handle to file
  void* lpBuffer,                              // data buffer
  int nBytesToWrite,                           // number of bytes
                                               // to write
  int* nBytesWritten,                          // number of bytes
                                               // written
  int overlapped);                             // overlapped buffer

[DllImport("kernel32", SetLastError=true)]
static extern int GetLastError();

The Code

The next step is to use these functions in your code and get the getting going.

Note: I have changed my WriteLogFile function to console.writeline for easy understanding.
/// <summary>
/// Connect to the COM Port.
/// </summary>
private void GetDevice(string Port)
{
  // open the existing port...
  m_ihandle = CreateFile(Port,
    GENERIC_READ | GENERIC_WRITE,
    0,              // comm devices must be opened
                    // w/exclusive-access
    0,              // no security attributes
    OPEN_EXISTING,  // comm devices must use OPEN_EXISTING
    0,              // not overlapped I/O
    0);             // hTemplate must be NULL for comm devices
  //if the handle value is -1, that means you got an error....
  if(m_ihandle == -1)
    //write failure log
    Console.WriteLine("open COM port failed" + GetLastError());
  else
    //write success log
    Console.WriteLine(Port + " opened successfully!");
}

/// <summary>
/// Send Command to the COM Port.
/// As I am using a modem, I send command like "AT+CGMM"
/// </summary>
private unsafe void SendCommand(string szCmd)
{
  int i = 0, n = 0;
  //get string length
  int Len = szCmd.Length;
  //use ASCIIEncoding to work with byte and string
  ASCIIEncoding e = new ASCIIEncoding();
  //assign string to byte buffer and add "return"
  byte[]Buffer = e.GetBytes(szCmd + "\r\n");
  //use fixed to avoid more memory allocation
  fixed (byte* p = Buffer)
  {
    i=0;
    //write command to the port
    if(!WriteFile(m_ihandle, p + i, Len+1, &n, 0))
      //if false, write failure log
      Console.WriteLine("Send Command " + szCmd + " failed");
    else
      // write success log
      Console.WriteLine("Send Command Successed");
  }
}
/// <summary>
/// Read information from the COM Port.
/// </summary>
private unsafe void ReadModem()
{
  //set the maximum limit to read
  int count = 128;
  //create buffer to store the info
  byte[] buffer = new byte[count];
  //use ASCII encoding to work with string and byte
  ASCIIEncoding e = new ASCIIEncoding();
  //loop through read until done...
  int index = 0;
  int n = 1;
  while (n!=0)
  {
    n = 0;
    fixed (byte* p = buffer)
    {
      //read file
      if(!ReadFile(m_ihandle, p + index, count, &n, 0))
        //write the value received in log
        Console.WriteLine(e.GetString(buffer));
      else
        //write failure log
        Console.WriteLine("Read Modem Failed");
    }
  }
}

Summary

That's it. All the time that was spent to solve this problem gave pretty fine results. This is all that I intended to do and the results are achieved. I hope you now have a rough idea of how to communicate with, write commands to, and read information from ports. You can further try to catch events from ports. I am working on it. That's what you will get next, when I will write another article.

- Anand Saini



About the Author

Anand Saini

Anand Saini (Andy Tacker), has over 5 years experince as software architect, system developer and system analyst. Most of his big projects were SCADA systems using C++ & VC++ . He also has experience as CDMA & GSM BSS Engineer. He has worked for some of the MNCs like LG Industrial Systems, Huawei Technologies, Synergy Systems and Solutions. Currently, he is employed as Business Strategy Manager with Tradition. He is a volunteer Super moderator with Codeguru Discussion Forums. He occassionally write articles for Codeguru.com.

Comments

  • additional changes

    Posted by Legacy on 02/17/2004 12:00am

    Originally posted by: Daryl

    add the following 
    
    

    [DllImport("kernel32", SetLastError=true)]
    static extern void CloseHandle(int status);

    and call the function to release handle to the COM.

    Reply
  • m_ihandle

    Posted by Legacy on 01/23/2004 12:00am

    Originally posted by: marcos

    what is m_ihandle?

    Reply
  • Modified ReadModem (just nit-picking)

    Posted by Legacy on 01/07/2004 12:00am

    Originally posted by: Peter Kintu

    Hello all, I tried the code so kindly provided, but kept on getting incosistent results. The data from the modem was read only once, (I'm sure the response from the SendCmd function was available in the buffer, but for some reason was not being read.) The SendCmd worked perfect every time.
    I modified the code to use System.IO.FileStream and System.IO.StreamReader to read the modem response or data from the modem as below.

    private unsafe void ReadModem()
    {
    Console.WriteLine(m_ihandle); //Recall m_ihandle is system
    //file handle for port

    System.IntPtr iPtr = new System.IntPtr(m_ihandle);
    //Handle has to be passed to FileStream constructor
    //wrapped in IntPtr object

    System.IO.FileStream fs = new System.IO.FileStream(iPtr,FileAccess.Read);
    //Construct FileStream object on file handle with desired
    //Read or Write access. Must use FileAccess object //attributes

    System.IO.StreamReader sr = new System.IO.StreamReader(fs);
    //Open a reader on the stream

    String line;

    if(fs.CanRead){
    Console.WriteLine("Can read from port");
    while ((line = sr.ReadLine()) != null)
    Console.WriteLine(line);
    }else{
    Console.WriteLine("Cannot read from port");
    }
    }

    Reply
  • Help Me!

    Posted by Legacy on 01/06/2004 12:00am

    Originally posted by: Arun Pringan Kannan

    Hi everyone,
    Could anyone help me?
    i have developed a program with windows form in C# to read in address of a location and displaying the map of that locality with the help of MapPoint.Net.
    Now i need to interface a GPS through COM port to the program to read in the values provioded by the GPS and display the map accordingly.
    And also send some data through COM port to another machine.
    But i have no idea how to connect the COM port to the program.
    i have read an article in CodeGuru about COM port communication. but it says that Windows form should be ignored.
    But that means total shuffling of the present program.
    Can anyone suggest me a better way?
    thanking you,
    Arun
    arunpk29@yahoo.com

    Reply
  • acces modem

    Posted by Legacy on 05/01/2003 12:00am

    Originally posted by: arif miftahur

    i interest your artikle.but my modem is internal so i can't apply your artikle

    Reply
  • a little change here!

    Posted by Legacy on 04/24/2003 12:00am

    Originally posted by: Anand

    the code for readfile is using !ReadFile(), so, the console.writeline messages must be interchanged for correct working of the code.

    changed code
    //read file
    if(!ReadFile(m_ihandle, p + index, count, &n, 0))
    //write the value received in log
    Console.WriteLine("Read Modem Failed");
    else
    //write failure log
    Console.WriteLine(e.GetString(buffer));

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Hurricane Sandy was one of the most destructive natural disasters that the United States has ever experienced. Read this success story to learn how Datto protected its partners and their customers with proactive business continuity planning, heroic employee efforts, and the right mix of technology and support. With storm surges over 12 feet, winds that exceeded 90 mph, and a diameter spanning more than 900 miles, Sandy resulted in power outages to approximately 7.5 million people, and caused an estimated $50 …

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds