Commit 6b14ac6db7c527c2ab5687d519eb590fd812d954

Authored by xiemeng
1 parent b308a51f

封装类

Showing 2 changed files with 979 additions and 0 deletions
  1 +/*
  2 +** FILENAME CSerialPort.cpp
  3 +**
  4 +** PURPOSE This class can read, write and watch one serial port.
  5 +** It sends messages to its owner when something happends on the port
  6 +** The class creates a thread for reading and writing so the main
  7 +** program is not blocked.
  8 +**
  9 +** CREATION DATE 15-09-1997
  10 +** LAST MODIFICATION 12-11-1997
  11 +**
  12 +** AUTHOR Remon Spekreijse
  13 +**
  14 +** 2007-12-25 mrlong https://code.google.com/p/mycom/
  15 +** 2011-11-06 liquanhai http://blog.csdn.net/liquanhai/article/details/6941574
  16 +** 2013-12-04 viruscamp http://github.com/viruscamp/CSerialPort
  17 +**
  18 +*/
  19 +
  20 +#include "stdafx.h"
  21 +#include "SerialPort.h"
  22 +
  23 +#include <assert.h>
  24 +
  25 +//
  26 +// Constructor
  27 +//
  28 +CSerialPort::CSerialPort()
  29 +{
  30 + m_hComm = NULL;
  31 +
  32 + // initialize overlapped structure members to zero
  33 + m_ov.Offset = 0;
  34 + m_ov.OffsetHigh = 0;
  35 +
  36 + // create events
  37 + m_ov.hEvent = NULL;
  38 + m_hWriteEvent = NULL;
  39 + m_hShutdownEvent = NULL;
  40 +
  41 + m_szWriteBuffer = NULL;
  42 +
  43 + m_bThreadAlive = FALSE;
  44 + m_nWriteSize = 1;
  45 + m_userdata = 0;
  46 +}
  47 +
  48 +//
  49 +// Delete dynamic memory
  50 +//
  51 +CSerialPort::~CSerialPort()
  52 +{
  53 + do
  54 + {
  55 + SetEvent(m_hShutdownEvent);
  56 + } while (m_bThreadAlive);
  57 +
  58 + if (m_hComm != NULL)
  59 + {
  60 + CloseHandle(m_hComm);
  61 + m_hComm = NULL;
  62 + }
  63 + // Close Handles
  64 + if(m_hShutdownEvent!=NULL)
  65 + CloseHandle( m_hShutdownEvent);
  66 + if(m_ov.hEvent!=NULL)
  67 + CloseHandle( m_ov.hEvent );
  68 + if(m_hWriteEvent!=NULL)
  69 + CloseHandle( m_hWriteEvent );
  70 +
  71 + //TRACE("Thread ended\n");
  72 +
  73 + delete [] m_szWriteBuffer;
  74 +}
  75 +
  76 +//
  77 +// Initialize the port. This can be port 1 to 4.
  78 +//
  79 +//
  80 +//parity:
  81 +// n=none
  82 +// e=even
  83 +// o=odd
  84 +// m=mark
  85 +// s=space
  86 +//data:
  87 +// 5,6,7,8
  88 +//stop:
  89 +// 1,1.5,2
  90 +//
  91 +BOOL CSerialPort::InitPort(HWND pPortOwner, // the owner (CWnd) of the port (receives message)
  92 + UINT portnr, // portnumber (1..4)
  93 + UINT baud, // baudrate
  94 + char parity, // parity
  95 + UINT databits, // databits
  96 + UINT stopbits, // stopbits
  97 + DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc
  98 + UINT writebuffersize,// size to the writebuffer
  99 + DWORD userdata,
  100 +
  101 + DWORD ReadIntervalTimeout,
  102 + DWORD ReadTotalTimeoutMultiplier,
  103 + DWORD ReadTotalTimeoutConstant,
  104 + DWORD WriteTotalTimeoutMultiplier,
  105 + DWORD WriteTotalTimeoutConstant )
  106 +
  107 +{
  108 + assert(portnr > 0 && portnr < 200);
  109 + assert(pPortOwner != NULL);
  110 +
  111 + // if the thread is alive: Kill
  112 + if (m_bThreadAlive)
  113 + {
  114 + do
  115 + {
  116 + SetEvent(m_hShutdownEvent);
  117 + } while (m_bThreadAlive);
  118 + //TRACE("Thread ended\n");
  119 + }
  120 + m_userdata = userdata;
  121 +
  122 + // create events
  123 + if (m_ov.hEvent != NULL)
  124 + ResetEvent(m_ov.hEvent);
  125 + else
  126 + m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  127 +
  128 + if (m_hWriteEvent != NULL)
  129 + ResetEvent(m_hWriteEvent);
  130 + else
  131 + m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  132 +
  133 + if (m_hShutdownEvent != NULL)
  134 + ResetEvent(m_hShutdownEvent);
  135 + else
  136 + m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  137 +
  138 + // initialize the event objects
  139 + m_hEventArray[0] = m_hShutdownEvent; // highest priority
  140 + m_hEventArray[1] = m_ov.hEvent;
  141 + m_hEventArray[2] = m_hWriteEvent;
  142 +
  143 + // initialize critical section
  144 + InitializeCriticalSection(&m_csCommunicationSync);
  145 +
  146 + // set buffersize for writing and save the owner
  147 + m_pOwner = pPortOwner;
  148 +
  149 + if (m_szWriteBuffer != NULL)
  150 + delete [] m_szWriteBuffer;
  151 + m_szWriteBuffer = new char[writebuffersize];
  152 +
  153 + m_nPortNr = portnr;
  154 +
  155 + m_nWriteBufferSize = writebuffersize;
  156 + m_dwCommEvents = dwCommEvents;
  157 +
  158 + BOOL bResult = FALSE;
  159 + char *szPort = new char[50];
  160 + char *szBaud = new char[50];
  161 +
  162 + // now it critical!
  163 + EnterCriticalSection(&m_csCommunicationSync);
  164 +
  165 + // if the port is already opened: close it
  166 + if (m_hComm != NULL)
  167 + {
  168 + CloseHandle(m_hComm);
  169 + m_hComm = NULL;
  170 + }
  171 +
  172 + // prepare port strings
  173 + sprintf(szPort, "\\\\.\\COM%d", portnr);
  174 +
  175 + // stop is index 0 = 1 1=1.5 2=2
  176 + int mystop;
  177 + int myparity;
  178 + switch(stopbits)
  179 + {
  180 + case 0:
  181 + mystop = ONESTOPBIT;
  182 + break;
  183 + case 1:
  184 + mystop = ONE5STOPBITS;
  185 + break;
  186 + case 2:
  187 + mystop = TWOSTOPBITS;
  188 + break;
  189 + }
  190 + myparity = 0;
  191 + parity = toupper(parity);
  192 + switch(parity)
  193 + {
  194 + case 'N':
  195 + myparity = 0;
  196 + break;
  197 + case 'O':
  198 + myparity = 1;
  199 + break;
  200 + case 'E':
  201 + myparity = 2;
  202 + break;
  203 + case 'M':
  204 + myparity = 3;
  205 + break;
  206 + case 'S':
  207 + myparity = 4;
  208 + break;
  209 + }
  210 + sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, mystop);
  211 +
  212 + // get a handle to the port
  213 + m_hComm = CreateFile(szPort, // communication port string (COMX)
  214 + GENERIC_READ | GENERIC_WRITE, // read/write types
  215 + 0, // comm devices must be opened with exclusive access
  216 + NULL, // no security attributes
  217 + OPEN_EXISTING, // comm devices must use OPEN_EXISTING
  218 + FILE_FLAG_OVERLAPPED, // Async I/O
  219 + 0); // template must be 0 for comm devices
  220 +
  221 + if (m_hComm == INVALID_HANDLE_VALUE)
  222 + {
  223 + // port not found
  224 + delete [] szPort;
  225 + delete [] szBaud;
  226 +
  227 + return FALSE;
  228 + }
  229 +
  230 + // set the timeout values
  231 + m_CommTimeouts.ReadIntervalTimeout = ReadIntervalTimeout * 1000;
  232 + m_CommTimeouts.ReadTotalTimeoutMultiplier = ReadTotalTimeoutMultiplier * 1000;
  233 + m_CommTimeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant * 1000;
  234 + m_CommTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;
  235 + m_CommTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant * 1000;
  236 +
  237 + // configure
  238 + if (SetCommTimeouts(m_hComm, &m_CommTimeouts))
  239 + {
  240 + if (SetCommMask(m_hComm, dwCommEvents))
  241 + {
  242 + if (GetCommState(m_hComm, &m_dcb))
  243 + {
  244 + m_dcb.EvtChar = 'q';
  245 + m_dcb.fRtsControl = RTS_CONTROL_ENABLE; // set RTS bit high!
  246 + m_dcb.BaudRate = baud; // add by mrlong
  247 + m_dcb.Parity = myparity;
  248 + m_dcb.ByteSize = databits;
  249 + m_dcb.StopBits = mystop;
  250 +
  251 + //if (BuildCommDCB(szBaud, &m_dcb))
  252 + //{
  253 + if (SetCommState(m_hComm, &m_dcb) == FALSE)
  254 + ProcessErrorMessage("SetCommState()");
  255 + //}
  256 + //else
  257 + // ProcessErrorMessage("BuildCommDCB()");
  258 + }
  259 + else
  260 + ProcessErrorMessage("GetCommState()");
  261 + }
  262 + else
  263 + ProcessErrorMessage("SetCommMask()");
  264 + }
  265 + else
  266 + ProcessErrorMessage("SetCommTimeouts()");
  267 +
  268 + delete [] szPort;
  269 + delete [] szBaud;
  270 +
  271 + // flush the port
  272 + PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  273 +
  274 + // release critical section
  275 + LeaveCriticalSection(&m_csCommunicationSync);
  276 +
  277 + //TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);
  278 +
  279 + return TRUE;
  280 +}
  281 +
  282 +//
  283 +// The CommThread Function.
  284 +//
  285 +DWORD WINAPI CSerialPort::CommThread(LPVOID pParam)
  286 +{
  287 + // Cast the void pointer passed to the thread back to
  288 + // a pointer of CSerialPort class
  289 + CSerialPort *port = (CSerialPort*)pParam;
  290 +
  291 + // Set the status variable in the dialog class to
  292 + // TRUE to indicate the thread is running.
  293 + port->m_bThreadAlive = TRUE;
  294 +
  295 + // Misc. variables
  296 + DWORD BytesTransfered = 0;
  297 + DWORD Event = 0;
  298 + DWORD CommEvent = 0;
  299 + DWORD dwError = 0;
  300 + COMSTAT comstat;
  301 + BOOL bResult = TRUE;
  302 +
  303 + // Clear comm buffers at startup
  304 + if (port->m_hComm) // check if the port is opened
  305 + PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  306 +
  307 + // begin forever loop. This loop will run as long as the thread is alive.
  308 + for (;;)
  309 + {
  310 +
  311 + // Make a call to WaitCommEvent(). This call will return immediatly
  312 + // because our port was created as an async port (FILE_FLAG_OVERLAPPED
  313 + // and an m_OverlappedStructerlapped structure specified). This call will cause the
  314 + // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to
  315 + // be placed in a non-signeled state if there are no bytes available to be read,
  316 + // or to a signeled state if there are bytes available. If this event handle
  317 + // is set to the non-signeled state, it will be set to signeled when a
  318 + // character arrives at the port.
  319 +
  320 + // we do this for each port!
  321 +
  322 + bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);
  323 +
  324 + if (!bResult)
  325 + {
  326 + // If WaitCommEvent() returns FALSE, process the last error to determin
  327 + // the reason..
  328 + switch (dwError = GetLastError())
  329 + {
  330 + case ERROR_IO_PENDING:
  331 + {
  332 + // This is a normal return value if there are no bytes
  333 + // to read at the port.
  334 + // Do nothing and continue
  335 + break;
  336 + }
  337 + case 87:
  338 + {
  339 + // Under Windows NT, this value is returned for some reason.
  340 + // I have not investigated why, but it is also a valid reply
  341 + // Also do nothing and continue.
  342 + break;
  343 + }
  344 + default:
  345 + {
  346 + // All other error codes indicate a serious error has
  347 + // occured. Process this error.
  348 + port->ProcessErrorMessage("WaitCommEvent()");
  349 + break;
  350 + }
  351 + }
  352 + }
  353 + else
  354 + {
  355 + // If WaitCommEvent() returns TRUE, check to be sure there are
  356 + // actually bytes in the buffer to read.
  357 + //
  358 + // If you are reading more than one byte at a time from the buffer
  359 + // (which this program does not do) you will have the situation occur
  360 + // where the first byte to arrive will cause the WaitForMultipleObjects()
  361 + // function to stop waiting. The WaitForMultipleObjects() function
  362 + // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state
  363 + // as it returns.
  364 + //
  365 + // If in the time between the reset of this event and the call to
  366 + // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again
  367 + // to the signeled state. When the call to ReadFile() occurs, it will
  368 + // read all of the bytes from the buffer, and the program will
  369 + // loop back around to WaitCommEvent().
  370 + //
  371 + // At this point you will be in the situation where m_OverlappedStruct.hEvent is set,
  372 + // but there are no bytes available to read. If you proceed and call
  373 + // ReadFile(), it will return immediatly due to the async port setup, but
  374 + // GetOverlappedResults() will not return until the next character arrives.
  375 + //
  376 + // It is not desirable for the GetOverlappedResults() function to be in
  377 + // this state. The thread shutdown event (event 0) and the WriteFile()
  378 + // event (Event2) will not work if the thread is blocked by GetOverlappedResults().
  379 + //
  380 + // The solution to this is to check the buffer with a call to ClearCommError().
  381 + // This call will reset the event handle, and if there are no bytes to read
  382 + // we can loop back through WaitCommEvent() again, then proceed.
  383 + // If there are really bytes to read, do nothing and proceed.
  384 +
  385 + bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
  386 +
  387 + if (comstat.cbInQue == 0)
  388 + continue;
  389 + } // end if bResult
  390 +
  391 + // Main wait function. This function will normally block the thread
  392 + // until one of nine events occur that require action.
  393 + Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
  394 +
  395 + switch (Event)
  396 + {
  397 + case 0:
  398 + {
  399 + // Shutdown event. This is event zero so it will be
  400 + // the higest priority and be serviced first.
  401 +
  402 + port->m_bThreadAlive = FALSE;
  403 +
  404 + // Kill this thread. break is not needed, but makes me feel better.
  405 + //AfxEndThread(100);
  406 + ::ExitThread(100);
  407 +
  408 + break;
  409 + }
  410 + case 1: // read event
  411 + {
  412 + GetCommMask(port->m_hComm, &CommEvent);
  413 + if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中
  414 + ReceiveChar(port);
  415 +
  416 + if (CommEvent & EV_CTS) //CTS信号状态发生变化
  417 + ::SendMessage(port->m_pOwner, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  418 + if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中
  419 + ::SendMessage(port->m_pOwner, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  420 + if (CommEvent & EV_BREAK) //输入中发生中断
  421 + ::SendMessage(port->m_pOwner, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  422 + if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY
  423 + ::SendMessage(port->m_pOwner, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  424 + if (CommEvent & EV_RING) //检测到振铃指示
  425 + ::SendMessage(port->m_pOwner, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
  426 +
  427 + break;
  428 + }
  429 + case 2: // write event
  430 + {
  431 + // Write character event from port
  432 + WriteChar(port);
  433 + break;
  434 + }
  435 +
  436 + } // end switch
  437 +
  438 + } // close forever loop
  439 +
  440 + return 0;
  441 +}
  442 +
  443 +//
  444 +// start comm watching
  445 +//
  446 +BOOL CSerialPort::StartMonitoring()
  447 +{
  448 + //if (!(m_Thread = AfxBeginThread(CommThread, this)))
  449 + if (!(m_Thread = ::CreateThread (NULL, 0, CommThread, this, 0, NULL )))
  450 + return FALSE;
  451 + //TRACE("Thread started\n");
  452 + return TRUE;
  453 +}
  454 +
  455 +//
  456 +// Restart the comm thread
  457 +//
  458 +BOOL CSerialPort::RestartMonitoring()
  459 +{
  460 + //TRACE("Thread resumed\n");
  461 + //m_Thread->ResumeThread();
  462 + ::ResumeThread(m_Thread);
  463 + return TRUE;
  464 +}
  465 +
  466 +
  467 +//
  468 +// Suspend the comm thread
  469 +//
  470 +BOOL CSerialPort::StopMonitoring()
  471 +{
  472 + //TRACE("Thread suspended\n");
  473 + //m_Thread->SuspendThread();
  474 + ::SuspendThread(m_Thread);
  475 + return TRUE;
  476 +}
  477 +
  478 +
  479 +//
  480 +// If there is a error, give the right message
  481 +//
  482 +void CSerialPort::ProcessErrorMessage(char* ErrorText)
  483 +{
  484 + char *Temp = new char[200];
  485 +
  486 + LPVOID lpMsgBuf;
  487 +
  488 + FormatMessage(
  489 + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  490 + NULL,
  491 + GetLastError(),
  492 + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  493 + (LPTSTR) &lpMsgBuf,
  494 + 0,
  495 + NULL
  496 + );
  497 +
  498 + sprintf(Temp, "WARNING: %s Failed with the following error: \n%s\nPort: %d\n", (char*)ErrorText, lpMsgBuf, m_nPortNr);
  499 + MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);
  500 +
  501 + LocalFree(lpMsgBuf);
  502 + delete[] Temp;
  503 +}
  504 +
  505 +//
  506 +// Write a character.
  507 +//
  508 +void CSerialPort::WriteChar(CSerialPort* port)
  509 +{
  510 + BOOL bWrite = TRUE;
  511 + BOOL bResult = TRUE;
  512 +
  513 + DWORD BytesSent = 0;
  514 + DWORD SendLen = port->m_nWriteSize;
  515 + ResetEvent(port->m_hWriteEvent);
  516 +
  517 +
  518 + // Gain ownership of the critical section
  519 + EnterCriticalSection(&port->m_csCommunicationSync);
  520 +
  521 + if (bWrite)
  522 + {
  523 + // Initailize variables
  524 + port->m_ov.Offset = 0;
  525 + port->m_ov.OffsetHigh = 0;
  526 +
  527 + // Clear buffer
  528 + PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  529 +
  530 + bResult = WriteFile(port->m_hComm, // Handle to COMM Port
  531 + port->m_szWriteBuffer, // Pointer to message buffer in calling finction
  532 + SendLen, // add by mrlong
  533 + //strlen((char*)port->m_szWriteBuffer), // Length of message to send
  534 + &BytesSent, // Where to store the number of bytes sent
  535 + &port->m_ov); // Overlapped structure
  536 +
  537 + // deal with any error codes
  538 + if (!bResult)
  539 + {
  540 + DWORD dwError = GetLastError();
  541 + switch (dwError)
  542 + {
  543 + case ERROR_IO_PENDING:
  544 + {
  545 + // continue to GetOverlappedResults()
  546 + BytesSent = 0;
  547 + bWrite = FALSE;
  548 + break;
  549 + }
  550 + default:
  551 + {
  552 + // all other error codes
  553 + port->ProcessErrorMessage("WriteFile()");
  554 + }
  555 + }
  556 + }
  557 + else
  558 + {
  559 + LeaveCriticalSection(&port->m_csCommunicationSync);
  560 + }
  561 + } // end if(bWrite)
  562 +
  563 + if (!bWrite)
  564 + {
  565 + bWrite = TRUE;
  566 +
  567 + bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port
  568 + &port->m_ov, // Overlapped structure
  569 + &BytesSent, // Stores number of bytes sent
  570 + TRUE); // Wait flag
  571 +
  572 + LeaveCriticalSection(&port->m_csCommunicationSync);
  573 +
  574 + // deal with the error code
  575 + if (!bResult)
  576 + {
  577 + port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");
  578 + }
  579 + } // end if (!bWrite)
  580 +
  581 + // Verify that the data size send equals what we tried to send
  582 + if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/) // add by
  583 + {
  584 + //TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, strlen((char*)port->m_szWriteBuffer));
  585 + }
  586 +}
  587 +
  588 +//
  589 +// Character received. Inform the owner
  590 +//
  591 +void CSerialPort::ReceiveChar(CSerialPort* port)
  592 +{
  593 + BOOL bRead = TRUE;
  594 + BOOL bResult = TRUE;
  595 + DWORD dwError = 0;
  596 + DWORD BytesRead = 0;
  597 + DWORD lparam = 0;
  598 + COMSTAT comstat;
  599 + unsigned char RXBuff;
  600 +
  601 + for (;;)
  602 + {
  603 + //add by liquanhai 2011-11-06 防止死锁
  604 + if(WaitForSingleObject(port->m_hShutdownEvent,0)==WAIT_OBJECT_0)
  605 + return;
  606 +
  607 + // Gain ownership of the comm port critical section.
  608 + // This process guarantees no other part of this program
  609 + // is using the port object.
  610 +
  611 + EnterCriticalSection(&port->m_csCommunicationSync);
  612 +
  613 + // ClearCommError() will update the COMSTAT structure and
  614 + // clear any other errors.
  615 +
  616 + bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
  617 +
  618 + LeaveCriticalSection(&port->m_csCommunicationSync);
  619 +
  620 + // start forever loop. I use this type of loop because I
  621 + // do not know at runtime how many loops this will have to
  622 + // run. My solution is to start a forever loop and to
  623 + // break out of it when I have processed all of the
  624 + // data available. Be careful with this approach and
  625 + // be sure your loop will exit.
  626 + // My reasons for this are not as clear in this sample
  627 + // as it is in my production code, but I have found this
  628 + // solutiion to be the most efficient way to do this.
  629 +
  630 + if (comstat.cbInQue == 0)
  631 + {
  632 + // break out when all bytes have been read
  633 + break;
  634 + }
  635 +
  636 + EnterCriticalSection(&port->m_csCommunicationSync);
  637 +
  638 + if (bRead)
  639 + {
  640 + bResult = ReadFile(port->m_hComm, // Handle to COMM port
  641 + &RXBuff, // RX Buffer Pointer
  642 + 1, // Read one byte
  643 + &BytesRead, // Stores number of bytes read
  644 + &port->m_ov); // pointer to the m_ov structure
  645 + // deal with the error code
  646 + if (!bResult)
  647 + {
  648 + switch (dwError = GetLastError())
  649 + {
  650 + case ERROR_IO_PENDING:
  651 + {
  652 + // asynchronous i/o is still in progress
  653 + // Proceed on to GetOverlappedResults();
  654 + bRead = FALSE;
  655 + break;
  656 + }
  657 + default:
  658 + {
  659 + // Another error has occured. Process this error.
  660 + port->ProcessErrorMessage("ReadFile()");
  661 + break;
  662 + }
  663 + }
  664 + }
  665 + else
  666 + {
  667 + // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
  668 + bRead = TRUE;
  669 + }
  670 + } // close if (bRead)
  671 +
  672 + if (!bRead)
  673 + {
  674 + bRead = TRUE;
  675 + bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port
  676 + &port->m_ov, // Overlapped structure
  677 + &BytesRead, // Stores number of bytes read
  678 + TRUE); // Wait flag
  679 +
  680 + // deal with the error code
  681 + if (!bResult)
  682 + {
  683 + port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");
  684 + }
  685 + } // close if (!bRead)
  686 +
  687 + LeaveCriticalSection(&port->m_csCommunicationSync);
  688 +
  689 + // notify parent that a byte was received
  690 + lparam = RXBuff;
  691 + lparam = lparam<<16|port->m_nPortNr;
  692 + ::SendMessage((port->m_pOwner), WM_COMM_RXCHAR, port->m_userdata,lparam);
  693 + } // end forever loop
  694 +
  695 +}
  696 +
  697 +//
  698 +// Write a string to the port
  699 +//
  700 +void CSerialPort::WriteToPort(char* string)
  701 +{
  702 + assert(m_hComm != 0);
  703 +
  704 + memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  705 + strcpy(m_szWriteBuffer, string);
  706 + m_nWriteSize=strlen(string); // add by mrlong
  707 + // set event for write
  708 + SetEvent(m_hWriteEvent);
  709 +}
  710 +
  711 +//
  712 +// Return the device control block
  713 +//
  714 +DCB CSerialPort::GetDCB()
  715 +{
  716 + return m_dcb;
  717 +}
  718 +
  719 +//
  720 +// Return the communication event masks
  721 +//
  722 +DWORD CSerialPort::GetCommEvents()
  723 +{
  724 + return m_dwCommEvents;
  725 +}
  726 +
  727 +//
  728 +// Return the output buffer size
  729 +//
  730 +DWORD CSerialPort::GetWriteBufferSize()
  731 +{
  732 + return m_nWriteBufferSize;
  733 +}
  734 +
  735 +BOOL CSerialPort::IsOpen()
  736 +{
  737 + return m_hComm != NULL;
  738 +}
  739 +
  740 +void CSerialPort::ClosePort()
  741 +{
  742 + MSG message;
  743 + do
  744 + {
  745 + SetEvent(m_hShutdownEvent);
  746 + if(::PeekMessage(&message,m_pOwner,0,0,PM_REMOVE))
  747 + {
  748 + ::TranslateMessage(&message);
  749 + ::DispatchMessage(&message);
  750 + }
  751 + } while (m_bThreadAlive);
  752 +
  753 + // if the port is still opened: close it
  754 + if (m_hComm != NULL)
  755 + {
  756 + CloseHandle(m_hComm);
  757 + m_hComm = NULL;
  758 + }
  759 +
  760 + // Close Handles
  761 + if(m_hShutdownEvent!=NULL)
  762 + ResetEvent(m_hShutdownEvent);
  763 + if(m_ov.hEvent!=NULL)
  764 + ResetEvent(m_ov.hEvent);
  765 + if(m_hWriteEvent!=NULL)
  766 + ResetEvent(m_hWriteEvent);
  767 +
  768 + //delete [] m_szWriteBuffer;
  769 +
  770 +}
  771 +
  772 +void CSerialPort::WriteToPort(char* string,int n)
  773 +{
  774 + assert(m_hComm != 0);
  775 + memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  776 + memcpy(m_szWriteBuffer, string, n);
  777 + m_nWriteSize = n;
  778 +
  779 + // set event for write
  780 + SetEvent(m_hWriteEvent);
  781 +}
  782 +
  783 +void CSerialPort::WriteToPort(LPCTSTR string)
  784 +{
  785 + assert(m_hComm != 0);
  786 + memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  787 + strcpy(m_szWriteBuffer, string);
  788 + m_nWriteSize=strlen(string);
  789 + // set event for write
  790 + SetEvent(m_hWriteEvent);
  791 +}
  792 +
  793 +void CSerialPort::WriteToPort(BYTE* Buffer, int n)
  794 +{
  795 + assert(m_hComm != 0);
  796 + memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  797 + int i;
  798 + for(i=0; i<n; i++)
  799 + {
  800 + m_szWriteBuffer[i] = Buffer[i];
  801 + }
  802 + m_nWriteSize=n;
  803 +
  804 + // set event for write
  805 + SetEvent(m_hWriteEvent);
  806 +}
  807 +
  808 +
  809 +void CSerialPort::SendData(LPCTSTR lpszData, const int nLength)
  810 +{
  811 + assert(m_hComm != 0);
  812 + memset(m_szWriteBuffer, 0, nLength);
  813 + strcpy(m_szWriteBuffer, lpszData);
  814 + m_nWriteSize=nLength;
  815 + // set event for write
  816 + SetEvent(m_hWriteEvent);
  817 +}
  818 +
  819 +BOOL CSerialPort::RecvData(LPTSTR lpszData, const int nSize)
  820 +{
  821 + //
  822 + //接收数据
  823 + //
  824 + assert(m_hComm!=0);
  825 + memset(lpszData,0,nSize);
  826 + DWORD mylen = 0;
  827 + DWORD mylen2 = 0;
  828 + while (mylen<nSize) {
  829 + if(!ReadFile(m_hComm,lpszData,nSize,&mylen2,NULL))
  830 + return FALSE;
  831 + mylen += mylen2;
  832 +
  833 +
  834 + }
  835 +
  836 + return TRUE;
  837 +}
  1 +/*
  2 +** FILENAME CSerialPort.h
  3 +**
  4 +** PURPOSE This class can read, write and watch one serial port.
  5 +** It sends messages to its owner when something happends on the port
  6 +** The class creates a thread for reading and writing so the main
  7 +** program is not blocked.
  8 +**
  9 +** CREATION DATE 15-09-1997
  10 +** LAST MODIFICATION 12-11-1997
  11 +**
  12 +** AUTHOR Remon Spekreijse
  13 +**
  14 +**
  15 +************************************************************************************
  16 +** author: mrlong date:2007-12-25
  17 +**
  18 +** 改进
  19 +** 1) 增加 ClosePort
  20 +** 2) 增加 WriteToPort 两个方法
  21 +** 3) 增加 SendData 与 RecvData 方法
  22 +************************************************************************************
  23 +************************************************************************************
  24 +** author:liquanhai date:2011-11-04
  25 +**
  26 +** 改进
  27 +** 1) 增加 ClosePort 中交出控制权,防止死锁问题
  28 +** 2) 增加 ReceiveChar 中防止线程死锁
  29 +************************************************************************************
  30 +************************************************************************************
  31 +** author:viruscamp date:2013-12-04
  32 +**
  33 +** 改进
  34 +** 1) 增加 IsOpen 判断是否打开
  35 +** 2) 修正 InitPort 中 parity Odd Even 参数取值错误
  36 +** 3) 修改 InitPort 中 portnr 取值范围,portnr>9 时特殊处理
  37 +** 4) 取消对 MFC 的依赖,使用 HWND 替代 CWnd,使用 win32 thread 函数而不是 MFC 的
  38 +** 5) 增加用户消息编号自定义,方法来自 CnComm
  39 +*/
  40 +
  41 +#ifndef __SERIALPORT_H__
  42 +#define __SERIALPORT_H__
  43 +
  44 +#ifndef WM_COMM_MSG_BASE
  45 + #define WM_COMM_MSG_BASE WM_USER + 617 //!< 消息编号的基点
  46 +#endif
  47 +
  48 +#define WM_COMM_BREAK_DETECTED WM_COMM_MSG_BASE + 1 // A break was detected on input.
  49 +#define WM_COMM_CTS_DETECTED WM_COMM_MSG_BASE + 2 // The CTS (clear-to-send) signal changed state.
  50 +#define WM_COMM_DSR_DETECTED WM_COMM_MSG_BASE + 3 // The DSR (data-set-ready) signal changed state.
  51 +#define WM_COMM_ERR_DETECTED WM_COMM_MSG_BASE + 4 // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
  52 +#define WM_COMM_RING_DETECTED WM_COMM_MSG_BASE + 5 // A ring indicator was detected.
  53 +#define WM_COMM_RLSD_DETECTED WM_COMM_MSG_BASE + 6 // The RLSD (receive-line-signal-detect) signal changed state.
  54 +#define WM_COMM_RXCHAR WM_COMM_MSG_BASE + 7 // A character was received and placed in the input buffer.
  55 +#define WM_COMM_RXFLAG_DETECTED WM_COMM_MSG_BASE + 8 // The event character was received and placed in the input buffer.
  56 +#define WM_COMM_TXEMPTY_DETECTED WM_COMM_MSG_BASE + 9 // The last character in the output buffer was sent.
  57 +
  58 +class CSerialPort
  59 +{
  60 +public:
  61 + // contruction and destruction
  62 + CSerialPort();
  63 + virtual ~CSerialPort();
  64 +
  65 + // port initialisation
  66 + BOOL InitPort(HWND pPortOwner, UINT portnr = 1, UINT baud = 19200,
  67 + char parity = 'N', UINT databits = 8, UINT stopsbits = 1,
  68 + DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512,
  69 + DWORD userdata = 0,
  70 +
  71 + DWORD ReadIntervalTimeout = 1000,
  72 + DWORD ReadTotalTimeoutMultiplier = 1000,
  73 + DWORD ReadTotalTimeoutConstant = 1000,
  74 + DWORD WriteTotalTimeoutMultiplier = 1000,
  75 + DWORD WriteTotalTimeoutConstant = 1000);
  76 +
  77 + // start/stop comm watching
  78 + BOOL StartMonitoring();
  79 + BOOL RestartMonitoring();
  80 + BOOL StopMonitoring();
  81 +
  82 + DWORD GetWriteBufferSize();
  83 + DWORD GetCommEvents();
  84 + DCB GetDCB();
  85 +
  86 + void WriteToPort(char* string);
  87 + void WriteToPort(char* string,int n); // add by mrlong 2007-12-25
  88 + void WriteToPort(LPCTSTR string); // add by mrlong 2007-12-25
  89 + void WriteToPort(BYTE* Buffer, int n);// add by mrlong
  90 + void ClosePort(); // add by mrlong 2007-12-2
  91 + BOOL IsOpen();
  92 +
  93 + void SendData(LPCTSTR lpszData, const int nLength); //串口发送函数 by mrlong 2008-2-15
  94 + BOOL RecvData(LPTSTR lpszData, const int nSize); //串口接收函数 by mrlong 2008-2-15
  95 +
  96 +protected:
  97 + // protected memberfunctions
  98 + void ProcessErrorMessage(char* ErrorText);
  99 + static DWORD WINAPI CommThread(LPVOID pParam);
  100 + static void ReceiveChar(CSerialPort* port);
  101 + static void WriteChar(CSerialPort* port);
  102 +
  103 + // thread
  104 + //CWinThread* m_Thread;
  105 + HANDLE m_Thread;
  106 +
  107 + // synchronisation objects
  108 + CRITICAL_SECTION m_csCommunicationSync;
  109 + BOOL m_bThreadAlive;
  110 +
  111 + // handles
  112 + HANDLE m_hShutdownEvent; //stop发生的事件
  113 + HANDLE m_hComm; // read
  114 + HANDLE m_hWriteEvent; // write
  115 +
  116 + // Event array.
  117 + // One element is used for each event. There are two event handles for each port.
  118 + // A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).
  119 + // There is a general shutdown when the port is closed.
  120 + HANDLE m_hEventArray[3];
  121 +
  122 + // structures
  123 + OVERLAPPED m_ov;
  124 + COMMTIMEOUTS m_CommTimeouts;
  125 + DCB m_dcb;
  126 +
  127 + // owner window
  128 + //CWnd* m_pOwner;
  129 + HWND m_pOwner;
  130 +
  131 +
  132 + // misc
  133 + UINT m_nPortNr; //?????
  134 + char* m_szWriteBuffer;
  135 + DWORD m_dwCommEvents;
  136 + DWORD m_nWriteBufferSize;
  137 +
  138 + int m_nWriteSize; //add by mrlong 2007-12-25
  139 + int m_userdata;
  140 +};
  141 +
  142 +#endif __SERIALPORT_H__
Please register or login to post a comment