MFC串口编程——使用标准SerialCom类
附件为实现visual studio C++串口通讯的类
使用方法,定义一个类:CSerialCom m_serialCom;
然后使用
m_serialCom.InitPort(this, nPort, nBaud, 'E');
m_serialCom.StartMonitoring();
开始监控报文
使用
m_serialCom.ClosePort();
关闭串口
同时需要定义一个串口数据消息回调接收函数
ON_MESSAGE(WM_COMM_RXCHAR, &OnReceiveData)
1 LRESULT CVS_CCO_FOR_UpgradeDlg::OnReceiveData(WPARAM wParam, LPARAM lParam) 2 { 3 Uart_Has_RecOneByte = TRUE; 4 uart_time_count = 0; 5 ringq_push(&ringq_rbuff, (BYTE)wParam); 6 7 return 0; 8 }
附件:SerialCom.cpp
1 #include "stdafx.h" 2 #include "SerialCom.h" 3 #include <assert.h> 4 // 5 // Constructor 6 // 7 #pragma warning(disable:4996) 8 CSerialCom::CSerialCom() 9 { 10 m_hComm = NULL; 11 // initialize overlapped structure members to zero 12 m_ov.Offset = 0; 13 m_ov.OffsetHigh = 0; 14 // create events 15 m_ov.hEvent = NULL; 16 m_hWriteEvent = NULL; 17 m_hShutdownEvent = NULL; 18 m_szWriteBuffer = NULL; 19 m_nWriteSize = 1; 20 m_bThreadAlive = FALSE; 21 } 22 // 23 // Delete dynamic memory 24 // 25 CSerialCom::~CSerialCom() 26 { 27 do 28 { 29 SetEvent(m_hShutdownEvent); 30 } while (m_bThreadAlive); 31 32 TRACE("Thread ended/n"); 33 delete[] m_szWriteBuffer; 34 } 35 // 36 // Initialize the port. This can be port 1 to 4. 37 // 38 BOOL CSerialCom::InitPort(CWnd* pPortOwner, // the owner (CWnd) of the port (receives message) 39 UINT portnr, // portnumber (1..8) 40 UINT baud, // baudrate 41 char parity, // parity 42 UINT databits, // databits 43 UINT stopbits, // stopbits 44 DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc 45 UINT writebuffersize) // size to the writebuffer 46 { 47 assert(portnr > 0 && portnr < 20); 48 assert(pPortOwner != NULL); 49 // if the thread is alive: Kill 50 if (m_bThreadAlive) 51 { 52 do 53 { 54 SetEvent(m_hShutdownEvent); 55 } while (m_bThreadAlive); 56 TRACE("Thread ended/n"); 57 } 58 // create events 59 if (m_ov.hEvent != NULL) 60 ResetEvent(m_ov.hEvent); 61 m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 62 if (m_hWriteEvent != NULL) 63 ResetEvent(m_hWriteEvent); 64 m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 65 66 if (m_hShutdownEvent != NULL) 67 ResetEvent(m_hShutdownEvent); 68 m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 69 // initialize the event objects 70 m_hEventArray[0] = m_hShutdownEvent; // highest priority 71 m_hEventArray[1] = m_hWriteEvent; 72 m_hEventArray[2] = m_ov.hEvent; 73 // initialize critical section 74 InitializeCriticalSection(&m_csCommunicationSync); 75 76 // set buffersize for writing and save the owner 77 m_pOwner = pPortOwner; 78 if (m_szWriteBuffer != NULL) 79 delete[] m_szWriteBuffer; 80 m_szWriteBuffer = new char[writebuffersize]; 81 m_nPortNr = portnr; 82 m_nWriteBufferSize = writebuffersize; 83 m_dwCommEvents = dwCommEvents; 84 BOOL bResult = FALSE; 85 char *szPort = new char[50]; 86 char *szBaud = new char[50]; 87 // now it critical! 88 EnterCriticalSection(&m_csCommunicationSync); 89 // if the port is already opened: close it 90 if (m_hComm != NULL) 91 { 92 CloseHandle(m_hComm); 93 m_hComm = NULL; 94 } 95 // prepare port strings 96 sprintf(szPort, "COM%d", portnr); 97 sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits); 98 // get a handle to the port 99 m_hComm = CreateFileA(szPort, // communication port string (COMX) 100 GENERIC_READ | GENERIC_WRITE, // read/write types 101 0, // comm devices must be opened with exclusive access 102 NULL, // no security attributes 103 OPEN_EXISTING, // comm devices must use OPEN_EXISTING 104 FILE_FLAG_OVERLAPPED, // Async I/O 105 0); // template must be 0 for comm devices 106 if (m_hComm == INVALID_HANDLE_VALUE) 107 { 108 // port not found 109 delete[] szPort; 110 delete[] szBaud; 111 return FALSE; 112 } 113 // set the timeout values 114 m_CommTimeouts.ReadIntervalTimeout = 1000; 115 m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000; 116 m_CommTimeouts.ReadTotalTimeoutConstant = 1000; 117 m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000; 118 m_CommTimeouts.WriteTotalTimeoutConstant = 1000; 119 // configure 120 if (SetCommTimeouts(m_hComm, &m_CommTimeouts)) 121 { 122 if (SetCommMask(m_hComm, dwCommEvents)) 123 { 124 if (GetCommState(m_hComm, &m_dcb)) 125 { 126 m_dcb.EvtChar = 'q'; 127 m_dcb.fRtsControl = RTS_CONTROL_ENABLE; // set RTS bit high! 128 if (BuildCommDCBA(szBaud, &m_dcb)) 129 { 130 if (SetCommState(m_hComm, &m_dcb)) 131 ; // normal operation... continue 132 else 133 ProcessErrorMessage("SetCommState()"); 134 } 135 else 136 ProcessErrorMessage("BuildCommDCB()"); 137 } 138 else 139 ProcessErrorMessage("GetCommState()"); 140 } 141 else 142 ProcessErrorMessage("SetCommMask()"); 143 } 144 else 145 ProcessErrorMessage("SetCommTimeouts()"); 146 delete[] szPort; 147 delete[] szBaud; 148 // flush the port 149 PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); 150 // release critical section 151 LeaveCriticalSection(&m_csCommunicationSync); 152 TRACE("Initialisation for communicationport %d completed./nUse Startmonitor to communicate./n", portnr); 153 return TRUE; 154 } 155 // 156 // The CommThread Function. 157 // 158 UINT CSerialCom::CommThread(LPVOID pParam) 159 { 160 // Cast the void pointer passed to the thread back to 161 // a pointer of CSerialCom class 162 CSerialCom *port = (CSerialCom*)pParam; 163 164 // Set the status variable in the dialog class to 165 // TRUE to indicate the thread is running. 166 port->m_bThreadAlive = TRUE; 167 168 // Misc. variables 169 DWORD BytesTransfered = 0; 170 DWORD Event = 0; 171 DWORD CommEvent = 0; 172 DWORD dwError = 0; 173 COMSTAT comstat; 174 memset(&comstat, 0, sizeof(COMSTAT)); 175 BOOL bResult = TRUE; 176 177 // Clear comm buffers at startup 178 if (port->m_hComm) // check if the port is opened 179 PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); 180 // begin forever loop. This loop will run as long as the thread is alive. 181 for (;;) 182 { 183 // Make a call to WaitCommEvent(). This call will return immediatly 184 // because our port was created as an async port (FILE_FLAG_OVERLAPPED 185 // and an m_OverlappedStructerlapped structure specified). This call will cause the 186 // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to 187 // be placed in a non-signeled state if there are no bytes available to be read, 188 // or to a signeled state if there are bytes available. If this event handle 189 // is set to the non-signeled state, it will be set to signeled when a 190 // character arrives at the port. 191 // we do this for each port! 192 bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov); 193 if (!bResult) 194 { 195 // If WaitCommEvent() returns FALSE, process the last error to determin 196 // the reason.. 197 switch (dwError = GetLastError()) 198 { 199 case ERROR_IO_PENDING: 200 { 201 // This is a normal return value if there are no bytes 202 // to read at the port. 203 // Do nothing and continue 204 break; 205 } 206 case 87: 207 { 208 // Under Windows NT, this value is returned for some reason. 209 // I have not investigated why, but it is also a valid reply 210 // Also do nothing and continue. 211 break; 212 } 213 default: 214 { 215 // All other error codes indicate a serious error has 216 // occured. Process this error. 217 port->ProcessErrorMessage("WaitCommEvent()"); 218 break; 219 } 220 } 221 } 222 else 223 { /* 224 // If WaitCommEvent() returns TRUE, check to be sure there are 225 // actually bytes in the buffer to read. 226 // 227 // If you are reading more than one byte at a time from the buffer 228 // (which this program does not do) you will have the situation occur 229 // where the first byte to arrive will cause the WaitForMultipleObjects() 230 // function to stop waiting. The WaitForMultipleObjects() function 231 // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state 232 // as it returns. 233 // 234 // If in the time between the reset of this event and the call to 235 // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again 236 // to the signeled state. When the call to ReadFile() occurs, it will 237 // read all of the bytes from the buffer, and the program will 238 // loop back around to WaitCommEvent(). 239 // 240 // At this point you will be in the situation where m_OverlappedStruct.hEvent is set, 241 // but there are no bytes available to read. If you proceed and call 242 // ReadFile(), it will return immediatly due to the async port setup, but 243 // GetOverlappedResults() will not return until the next character arrives. 244 // 245 // It is not desirable for the GetOverlappedResults() function to be in 246 // this state. The thread shutdown event (event 0) and the WriteFile() 247 // event (Event2) will not work if the thread is blocked by GetOverlappedResults(). 248 // 249 // The solution to this is to check the buffer with a call to ClearCommError(). 250 // This call will reset the event handle, and if there are no bytes to read 251 // we can loop back through WaitCommEvent() again, then proceed. 252 // If there are really bytes to read, do nothing and proceed. 253 */ 254 bResult = ClearCommError(port->m_hComm, &dwError, &comstat); 255 if (comstat.cbInQue == 0) 256 continue; 257 } // end if bResult 258 // Main wait function. This function will normally block the thread 259 // until one of nine events occur that require action. 260 Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE); 261 switch (Event) 262 { 263 case 0: 264 { 265 // Shutdown event. This is event zero so it will be 266 // the higest priority and be serviced first. 267 CloseHandle(port->m_hComm); 268 port->m_hComm = NULL; 269 port->m_bThreadAlive = FALSE; 270 271 // Kill this thread. break is not needed, but makes me feel better. 272 AfxEndThread(100); 273 break; 274 } 275 case 2: // read event 276 { 277 GetCommMask(port->m_hComm, &CommEvent); 278 if (CommEvent & EV_RXCHAR) 279 // Receive character event from port. 280 ReceiveChar(port, comstat); 281 if (CommEvent & EV_CTS) 282 ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr); 283 if (CommEvent & EV_BREAK) 284 ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr); 285 if (CommEvent & EV_ERR) 286 ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr); 287 if (CommEvent & EV_RING) 288 ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr); 289 290 if (CommEvent & EV_RXFLAG) 291 ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr); 292 293 break; 294 } 295 case 1: // write event 296 { 297 // Write character event from port 298 WriteChar(port); 299 break; 300 } 301 } // end switch 302 } // close forever loop 303 return 0; 304 } 305 // 306 // start comm watching 307 // 308 BOOL CSerialCom::StartMonitoring() 309 { 310 if (!(m_Thread = AfxBeginThread(CommThread, this))) 311 return FALSE; 312 TRACE("Thread started/n"); 313 return TRUE; 314 } 315 // 316 // Restart the comm thread 317 // 318 BOOL CSerialCom::RestartMonitoring() 319 { 320 TRACE("Thread resumed/n"); 321 m_Thread->ResumeThread(); 322 return TRUE; 323 } 324 325 // 326 // Suspend the comm thread 327 // 328 BOOL CSerialCom::StopMonitoring() 329 { 330 TRACE("Thread suspended/n"); 331 m_Thread->SuspendThread(); 332 return TRUE; 333 } 334 335 // 336 // If there is a error, give the right message 337 // 338 void CSerialCom::ProcessErrorMessage(char* ErrorText) 339 { 340 char *Temp = new char[200]; 341 342 LPVOID lpMsgBuf; 343 FormatMessage( 344 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 345 NULL, 346 GetLastError(), 347 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language 348 (LPTSTR)&lpMsgBuf, 349 0, 350 NULL 351 ); 352 sprintf(Temp, "WARNING: %s Failed with the following error: /n%s/nPort: %d/n", ErrorText, (char*)lpMsgBuf, m_nPortNr); 353 MessageBoxA(NULL, Temp, "Application Error", MB_ICONSTOP); 354 LocalFree(lpMsgBuf); 355 delete[] Temp; 356 } 357 // 358 // Write a character. 359 // 360 void CSerialCom::WriteChar(CSerialCom* port) 361 { 362 BOOL bWrite = TRUE; 363 BOOL bResult = TRUE; 364 DWORD BytesSent = 0; 365 ResetEvent(port->m_hWriteEvent); 366 // Gain ownership of the critical section 367 EnterCriticalSection(&port->m_csCommunicationSync); 368 if (bWrite) 369 { 370 // Initailize variables 371 port->m_ov.Offset = 0; 372 port->m_ov.OffsetHigh = 0; 373 // Clear buffer 374 PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); 375 bResult = WriteFile(port->m_hComm, // Handle to COMM Port 376 port->m_szWriteBuffer, // Pointer to message buffer in calling finction 377 // strlen((char*)port->m_szWriteBuffer), // Length of message to send 378 port->m_nWriteSize, // Length of message to send 379 &BytesSent, // Where to store the number of bytes sent 380 &port->m_ov); // Overlapped structure 381 // deal with any error codes 382 if (!bResult) 383 { 384 DWORD dwError = GetLastError(); 385 switch (dwError) 386 { 387 case ERROR_IO_PENDING: 388 { 389 // continue to GetOverlappedResults() 390 BytesSent = 0; 391 bWrite = FALSE; 392 break; 393 } 394 default: 395 { 396 // all other error codes 397 port->ProcessErrorMessage("WriteFile()"); 398 } 399 } 400 } 401 else 402 { 403 LeaveCriticalSection(&port->m_csCommunicationSync); 404 } 405 } // end if(bWrite) 406 if (!bWrite) 407 { 408 bWrite = TRUE; 409 410 bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port 411 &port->m_ov, // Overlapped structure 412 &BytesSent, // Stores number of bytes sent 413 TRUE); // Wait flag 414 LeaveCriticalSection(&port->m_csCommunicationSync); 415 // deal with the error code 416 // if (!bResult) 417 { 418 // port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()"); 419 } 420 } // end if (!bWrite) 421 // Verify that the data size send equals what we tried to send 422 // if (BytesSent != strlen((char*)port->m_szWriteBuffer)) 423 { 424 // TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d/n", BytesSent, strlen((char*)port->m_szWriteBuffer)); 425 } 426 // ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_TXEMPTY_DETECTED, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr); 427 ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_TXEMPTY_DETECTED, 0, (LPARAM)port->m_nPortNr); 428 } 429 // 430 // Character received. Inform the owner 431 // 432 void CSerialCom::ReceiveChar(CSerialCom* port, COMSTAT comstat) 433 { 434 BOOL bRead = TRUE; 435 BOOL bResult = TRUE; 436 DWORD dwError = 0; 437 DWORD BytesRead = 0; 438 unsigned char RXBuff; 439 for (;;) 440 { 441 // Gain ownership of the comm port critical section. 442 // This process guarantees no other part of this program 443 // is using the port object. 444 445 EnterCriticalSection(&port->m_csCommunicationSync); 446 // ClearCommError() will update the COMSTAT structure and 447 // clear any other errors. 448 449 bResult = ClearCommError(port->m_hComm, &dwError, &comstat); 450 LeaveCriticalSection(&port->m_csCommunicationSync); 451 // start forever loop. I use this type of loop because I 452 // do not know at runtime how many loops this will have to 453 // run. My solution is to start a forever loop and to 454 // break out of it when I have processed all of the 455 // data available. Be careful with this approach and 456 // be sure your loop will exit. 457 // My reasons for this are not as clear in this sample 458 // as it is in my production code, but I have found this 459 // solutiion to be the most efficient way to do this. 460 461 if (comstat.cbInQue == 0) 462 { 463 // break out when all bytes have been read 464 break; 465 } 466 467 EnterCriticalSection(&port->m_csCommunicationSync); 468 if (bRead) 469 { 470 bResult = ReadFile(port->m_hComm, // Handle to COMM port 471 &RXBuff, // RX Buffer Pointer 472 1, // Read one byte 473 &BytesRead, // Stores number of bytes read 474 &port->m_ov); // pointer to the m_ov structure 475 // deal with the error code 476 if (!bResult) 477 { 478 switch (dwError = GetLastError()) 479 { 480 case ERROR_IO_PENDING: 481 { 482 // asynchronous i/o is still in progress 483 // Proceed on to GetOverlappedResults(); 484 bRead = FALSE; 485 break; 486 } 487 default: 488 { 489 // Another error has occured. Process this error. 490 port->ProcessErrorMessage("ReadFile()"); 491 break; 492 } 493 } 494 } 495 else 496 { 497 // ReadFile() returned complete. It is not necessary to call GetOverlappedResults() 498 bRead = TRUE; 499 } 500 } // close if (bRead) 501 if (!bRead) 502 { 503 bRead = TRUE; 504 bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port 505 &port->m_ov, // Overlapped structure 506 &BytesRead, // Stores number of bytes read 507 TRUE); // Wait flag 508 // deal with the error code 509 if (!bResult) 510 { 511 port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()"); 512 } 513 } // close if (!bRead) 514 515 LeaveCriticalSection(&port->m_csCommunicationSync); 516 // notify parent that a byte was received 517 ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM)RXBuff, (LPARAM)port->m_nPortNr);//发送消息,对应的消息函数接收数据 518 } // end forever loop 519 } 520 // 521 // Write a string to the port 522 // 523 void CSerialCom::WriteToPort(char* string) 524 { 525 assert(m_hComm != 0); 526 527 memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); 528 strcpy(m_szWriteBuffer, string); 529 m_nWriteSize = strlen(string); 530 // set event for write 531 SetEvent(m_hWriteEvent); 532 } 533 534 void CSerialCom::WriteToPort(char* string, int n) 535 { 536 assert(m_hComm != 0); 537 memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); 538 // memset(m_szWriteBuffer, 0, n); 539 // strncpy(m_szWriteBuffer, string, n); 540 memcpy(m_szWriteBuffer, string, n); 541 m_nWriteSize = n; 542 // set event for write 543 SetEvent(m_hWriteEvent); 544 } 545 546 547 #if 0 548 void CSerialCom::WriteToPort(LPCTSTR string) 549 { 550 assert(m_hComm != 0); 551 memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); 552 strcpy(m_szWriteBuffer, string); 553 m_nWriteSize = strlen(string); 554 // set event for write 555 SetEvent(m_hWriteEvent); 556 } 557 #endif 558 void CSerialCom::WriteToPort(LPCTSTR string, int n) 559 { 560 assert(m_hComm != 0); 561 memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer)); 562 // strncpy(m_szWriteBuffer, string, n); 563 memcpy(m_szWriteBuffer, string, n); 564 m_nWriteSize = n; 565 // set event for write 566 SetEvent(m_hWriteEvent); 567 } 568 // 569 // Return the device control block 570 // 571 DCB CSerialCom::GetDCB() 572 { 573 return m_dcb; 574 } 575 // 576 // Return the communication event masks 577 // 578 DWORD CSerialCom::GetCommEvents() 579 { 580 return m_dwCommEvents; 581 } 582 // 583 // Return the output buffer size 584 // 585 DWORD CSerialCom::GetWriteBufferSize() 586 { 587 return m_nWriteBufferSize; 588 } 589 590 void CSerialCom::ClosePort() 591 { 592 SetEvent(m_hShutdownEvent); 593 } 594 595 void CSerialCom::EnumerateSerialPorts(CUIntArray& ports) 596 { 597 ports.RemoveAll(); //Make sure we clear out any elements which may already be in the array 598 599 //Determine what OS we are running on 600 OSVERSIONINFO osvi; 601 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 602 BOOL bGetVer = GetVersionEx(&osvi); 603 604 //On NT use the QueryDosDevice API 605 if (bGetVer && (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)) 606 { 607 //Use QueryDosDevice to look for all devices of the form COMx. This is a better 608 //solution as it means that no ports have to be opened at all. 609 TCHAR szDevices[65535]; 610 DWORD dwChars = QueryDosDevice(NULL, szDevices, 65535); 611 if (dwChars) { 612 int i = 0; 613 for (;;) { 614 TCHAR* pszCurrentDevice = &szDevices[i]; //Add the port number to the array which will be returned 615 //If it looks like "COMX" then 616 //add it to the array which will be returned 617 int nLen = _tcslen(pszCurrentDevice); 618 if (nLen > 3 && _tcsnicmp(pszCurrentDevice, _T("COM"), 3) == 0) { 619 int nPort = _ttoi(&pszCurrentDevice[3]); //Work out the port number 620 ports.Add(nPort); //clear out com_0 621 } 622 // Go to next NULL character 623 while (szDevices[i] != _T('\0')) i++; 624 i++; // Bump pointer to the next string 625 // The list is double-NULL terminated, so if the character is 626 // now NULL, we're at the end 627 if (szDevices[i] == _T('\0')) break; 628 } 629 } 630 else TRACE(_T("Failed in call to QueryDosDevice, GetLastError:%d\n"), GetLastError()); 631 } 632 else { 633 //On 95/98 open up each port to determine their existence 634 //Up to 255 COM ports are supported so we iterate through all of them seeing 635 //if we can open them or if we fail to open them, get an access denied or general error error. 636 //Both of these cases indicate that there is a COM port at that number. 637 for (UINT i = 1; i<256; i++) 638 { 639 //Form the Raw device name 640 CString sPort; 641 sPort.Format(_T("\\\\.\\COM%d"), i); 642 643 //Try to open the port 644 BOOL bSuccess = FALSE; 645 HANDLE hPort = ::CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); 646 if (hPort == INVALID_HANDLE_VALUE) 647 { 648 DWORD dwError = GetLastError(); 649 //Check to see if the error was because some other app had the port open or a general failure 650 if (dwError == ERROR_ACCESS_DENIED || dwError == ERROR_GEN_FAILURE) 651 bSuccess = TRUE; 652 } 653 else { 654 bSuccess = TRUE; //The port was opened successfully 655 CloseHandle(hPort); //Don't forget to close the port, since we are going to do nothing with it anyway 656 } 657 if (bSuccess) ports.Add(i); //Add the port number to the array which will be returned 658 } 659 } 660 }
SerialCom.h
1 #pragma once 2 3 #define WM_COMM_BREAK_DETECTED WM_USER+1 // A break was detected on input. 4 #define WM_COMM_CTS_DETECTED WM_USER+2 // The CTS (clear-to-send) signal changed state. 5 #define WM_COMM_DSR_DETECTED WM_USER+3 // The DSR (data-set-ready) signal changed state. 6 #define WM_COMM_ERR_DETECTED WM_USER+4 // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. 7 #define WM_COMM_RING_DETECTED WM_USER+5 // A ring indicator was detected. 8 #define WM_COMM_RLSD_DETECTED WM_USER+6 // The RLSD (receive-line-signal-detect) signal changed state. 9 #define WM_COMM_RXCHAR WM_USER+7 // A character was received and placed in the input buffer. 10 #define WM_COMM_RXFLAG_DETECTED WM_USER+8 // The event character was received and placed in the input buffer. 11 #define WM_COMM_TXEMPTY_DETECTED WM_USER+9 // The last character in the output buffer was sent. 12 class CSerialCom 13 { 14 public: 15 int m_nWriteSize; 16 void ClosePort(); 17 // contruction and destruction 18 CSerialCom(); 19 virtual ~CSerialCom(); 20 // port initialisation 21 BOOL InitPort(CWnd* pPortOwner, UINT portnr = 1, UINT baud = 9600, char parity = 'N', UINT databits = 8, UINT stopbits = 1, DWORD dwCommEvents = EV_RXCHAR, UINT writebuffersize = 1024); 22 HANDLE m_hComm; 23 // start/stop comm watching 24 BOOL StartMonitoring(); 25 BOOL RestartMonitoring(); 26 BOOL StopMonitoring(); 27 DWORD GetWriteBufferSize(); 28 DWORD GetCommEvents(); 29 DCB GetDCB(); 30 void WriteToPort(char* string); 31 void WriteToPort(char* string, int n); 32 void WriteToPort(LPCTSTR string); 33 void WriteToPort(LPCTSTR string, int n); 34 void EnumerateSerialPorts(CUIntArray& ports); 35 protected: 36 // protected memberfunctions 37 void ProcessErrorMessage(char* ErrorText); 38 static UINT CommThread(LPVOID pParam); 39 static void ReceiveChar(CSerialCom* port, COMSTAT comstat); 40 static void WriteChar(CSerialCom* port); 41 // thread 42 CWinThread* m_Thread; 43 // synchronisation objects 44 CRITICAL_SECTION m_csCommunicationSync; 45 BOOL m_bThreadAlive; 46 // handles 47 HANDLE m_hWriteEvent; 48 HANDLE m_hShutdownEvent; 49 // Event array. 50 // One element is used for each event. There are two event handles for each port. 51 // A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent). 52 // There is a general shutdown when the port is closed. 53 HANDLE m_hEventArray[3]; 54 // structures 55 OVERLAPPED m_ov; 56 COMMTIMEOUTS m_CommTimeouts; 57 DCB m_dcb; 58 // owner window 59 CWnd* m_pOwner; 60 // misc 61 UINT m_nPortNr; 62 char* m_szWriteBuffer; 63 DWORD m_dwCommEvents; 64 DWORD m_nWriteBufferSize; 65 };