CreateFile、ReadFile/WriteFile、DeviceIoControl 操作;DCB、COMMTIMEOUTS、COMSTAT、COMMCONFIG...;SetCommState/GetCommState、SetCommTimeouts/GetCommTimeouts、SetCommMask/WaitCommEvent、ReadFile/WriteFile (異步需 OVERLAPPED)。OnComm 回呼處理 comEvReceive (2)、comEvSend、comEvCTS 等;InBufferCount、InputLen、InputMode)。MSCOMM32.OCX,版本相容性風險;// 初始化
m_Comm.SetCommPort(2);
m_Comm.SetSettings(_T("4800,N,8,1"));
m_Comm.SetInBufferSize(1024);
m_Comm.SetInBufferCount(0);
m_Comm.SetInputMode(1); // Binary
m_Comm.SetInputLen(0); // 讀全部
m_Comm.SetPortOpen(TRUE);
// OnComm 事件
void CMyDlg::OnComm() {
switch (m_Comm.GetCommEvent()) {
case 2: // comEvReceive
// m_Comm.GetInput() 讀取...
break;
}
}
CSimpleComm (靈活、可執行緒)CreateFile 開啟,**異步旗標** FILE_FLAG_OVERLAPPED;GetCommState → 修改 DCB → SetCommState;SetCommTimeouts 設定讀寫超時;ReadFile/WriteFile + OVERLAPPED + WaitForSingleObject 等待完成;SetTimer 週期性輪詢 ReadFile (簡單場景);SetCommMask(EV_RXCHAR|EV_TXEMPTY|EV_RXFLAG) + WaitCommEvent 事件驅動。BOOL CSimpleComm::Open() {
m_hComm = CreateFile(_T("COM2"), GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL);
if (m_hComm == INVALID_HANDLE_VALUE) return FALSE;
DCB dcb = {0}; dcb.DCBlength = sizeof(DCB);
GetCommState(m_hComm, &dcb);
dcb.BaudRate = CBR_4800; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT;
SetCommState(m_hComm, &dcb);
COMMTIMEOUTS cto = {0};
cto.ReadIntervalTimeout = MAXDWORD;
cto.ReadTotalTimeoutConstant = 0;
cto.WriteTotalTimeoutMultiplier = 2*9600/dcb.BaudRate;
cto.WriteTotalTimeoutConstant = 25;
SetCommTimeouts(m_hComm, &cto);
return TRUE;
}
int CSimpleComm::ReadData(char* buf, int len) {
DWORD dwRead = 0;
BOOL ok = ReadFile(m_hComm, buf, len, &dwRead, &m_osRead);
if (!ok && GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(m_osRead.hEvent, 1000);
GetOverlappedResult(m_hComm, &m_osRead, &dwRead, FALSE);
}
return (int)dwRead;
}
WaitCommEvent;ON_MESSAGE + PostMessage 接收通知,安全更新介面。CWinThread* pThread = AfxBeginThread(
CommWatchThread, // 執行緒函數
(LPVOID)pPortInfo, // 傳遞埠資訊結構
THREAD_PRIORITY_ABOVE_NORMAL,
0, CREATE_SUSPENDED, NULL);
pThread->ResumeThread();
UINT CommWatchThread(LPVOID pParam) {
PortInfo* pInfo = (PortInfo*)pParam;
DWORD dwEvtMask;
while (!pInfo->bStop) {
if (WaitCommEvent(pInfo->hComm, &dwEvtMask, NULL)) {
if (dwEvtMask & pInfo->dwEvtMask) { // 關注的事件
WaitForSingleObject(pInfo->hEventPost, INFINITE);
ResetEvent(pInfo->hEventPost);
::PostMessage(pInfo->hWndNotify, pInfo->nMsgID, 0, 0); // 通知 UI
}
}
}
return 0;
}
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_MESSAGE(ID_COM1_DATA, OnProcessCom1Data)
ON_MESSAGE(ID_COM2_DATA, OnProcessCom2Data)
END_MESSAGE_MAP()
LRESULT CMyView::OnProcessCom1Data(WPARAM, LPARAM) {
// 從 CSimpleComm 實例讀取資料並處理
// 必須在下一次事件前處理完畢,否則資料會覆蓋/丟失
}
| 維度 | MSComm 控件 | 單執行緒類 | 多執行緒架構 |
|---|---|---|---|
| 部署複雜度 | 需註冊 OCX | 純 C++ 程式碼 | 純 C++ 程式碼 |
| 執行緒親和性 | 僅 UI 執行緒 | 任意執行緒 | 專用工作執行緒 |
| 多埠支援 | 困難 (每埠一控件) | 可多實例 | 原生支援 (每埠一執行緒) |
| 即時性 | 事件驅動 (尚可) | 輪詢/事件可選 | 事件驅動 + 高優先級 (最佳) |
| 資料處理安全 | 單執行緒無鎖問題 | 需自行加鎖 | Critical Section + Event 完整 |
| 適用場景 | 簡單單埠工具 | 中等複雜度、單/少埠 | 工業控制、多埠、高可靠 |
溫度監控系統 (Win95/98) 採用**方案二**:單執行緒類 + SetTimer 週期讀取,運行穩定。若擴展至多儀器並發,會遷移至方案三。