Accessing COM Ports

Wednesday Apr 23rd 2003 by Anand Saini
Share:

COM ports can be accessed without using a COM DLL. This C# article explains the details needed to communicate with COM ports for reading information and writing commands.

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

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved