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:
- Create File—to open the COM port.
- Write File—to send commands.
- 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 + "rn"); //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