65.9K
CodeProject is changing. Read more.
Home

How to open a serial COM port in Managed C++

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.52/5 (24 votes)

Apr 17, 2002

viewsIcon

303716

How to open a Serial COM port using Managed C++ and loading unmanaged DLLs

Introduction

Many days ago, I was looking for a class in managed C++ that gives me access to the COM port. I was surprised I couldn't find anything to use. There were many classes to use for sockets and files, but none for COM ports.

I tried to use System::IO::File without any success. Actually File::Open gave me an exception. So I went ahead and created a class to handle serial COM ports. Please note I am using DllImport to import the kernel32.dll 

// Header File
__gc class CSerialPort
{
public:
    CSerialPort(void);
    __nogc struct DCB
    {
        int DCBlength;
        int BaudRate;
        int fBinary;
        int fParity;
        int fOutxCtsFlow;
        int fOutxDsrFlow;
        int fDtrControl;
        int fDsrSensitivity;
        int fTXContinueOnXoff;
        int fOutX;
        int fInX;
        int fErrorChar;
        int fNull;
        int fRtsControl;
        int fAbortOnError;
        int fDummy2;
        unsigned short wReserved;
        unsigned short XonLim;
        unsigned char ByteSize;    //byte
        unsigned char Parity; // byte
        unsigned char StopBits;    //byte
        char XonChar;
        char XoffChar;
        char ErrorChar;
        char EofChar;
        char EvtChar;
        unsigned short wReserved1;
    };

    bool    Open( char *szComPort);
    void    Write(String __gc * buf);
    String  *Read();
    void    Close();

private:    
    FileStream    * m_MyFileStream;
    StreamWriter * m_MyStreamWriter;
    StreamReader * m_MyStreamReader;
    
    static long GENERIC_READ  = 0x80000000L;
    static long GENERIC_WRITE = 0x40000000L;

    static int OPEN_ALWAYS = 4;

    static int FILE_FLAG_NO_BUFFERING = 0x20000000;
    static int FILE_FLAG_OVERLAPPED   = 0x40000000;

};

namespace MyKernel
{
[DllImport("kernel32.dll")]
extern bool SetCommState(System::IntPtr hFile, CSerialPort::DCB * lpDBC);
[DllImport("kernel32.dll")]
extern int CreateFile( char * lpFileName , int dwDesiredAccess, int dwShareMode,
  IntPtr lpSecurityAttributes, int dwCreationDisposition,
  int dwFlagsAndAttributes, IntPtr hTemplateFile );

}

Also note that I am creating a new namespace to use the imported functions from kernel32.dll

This is the cpp file:

CSerialPort::CSerialPort(void)
{
}

bool CSerialPort::Open( char *szComPort)
{
    int handle;
            
    handle = MyKernel::CreateFile(
        szComPort,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_ALWAYS,
        FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
        NULL);

    try
    {
        IntPtr    pCom(handle);
        m_MyFileStream = new FileStream(pCom, 
                                        FileAccess::ReadWrite, 
                                        true, 1000, true);        
    }

    catch (Exception  * e ) {
        Console::WriteLine(e->ToString());
          return false;
      }

    
    DCB    * CommSettings = new DCB();
    CommSettings->DCBlength = sizeof(CommSettings);
    CommSettings->BaudRate  = 38400;
    CommSettings->ByteSize  = 8;
    CommSettings->Parity    = 0;
    CommSettings->StopBits    = 1;

    MyKernel::SetCommState(m_MyFileStream->get_Handle(), 
                           CommSettings);
    

    try
    {
        m_MyStreamReader = new StreamReader(m_MyFileStream);
    }

    catch (Exception  * e ) {
        Console::WriteLine(e->ToString());
          return false;
      }

    if ( m_MyFileStream->get_CanWrite() )
    {
        try
        {
            m_MyStreamWriter = new StreamWriter(m_MyFileStream);
        }

        catch (Exception  * e ) {
            Console::WriteLine(e->ToString());
            return false;
        }
    }

    return true;
}

void CSerialPort::Write(String __gc * buf)
{
    try
    {
        m_MyStreamWriter->Write(buf);
        m_MyStreamWriter->Flush();    
        
    }

    catch (Exception  * e ) {
        Console::WriteLine(e->ToString());    
      }
}

String  * CSerialPort::Read()
{
    String    *buf;
    
    buf = m_MyStreamReader->ReadLine();

    return (buf);
}

void CSerialPort::Close()
{
    m_MyStreamWriter->Close();
    m_MyStreamReader->Close();
    m_MyFileStream->Close();
}

To use it:

CSerialPort * pSerial = new CSerialPort;

pSerial->Open(S"COM2:");
Console::WriteLine(pSerial->Read());

We open COM port 2 and we wait to read a LINE with \r\n or \n. If you waiting for bytes you need to change m_MyStreamReader->ReadLine() to m_MyStreamReader->Read().