序列埠以檔案方式存取,核心 API 與檔案操作一致:CreateFile、ReadFile、WriteFile、CloseHandle,搭配 DCB 設定、逾時設定、事件遮罩。
HANDLE hCom = CreateFile(
"COM1", // 裝置名稱
GENERIC_READ | GENERIC_WRITE, // 讀寫存取
0, // 不共享 (序列埠獨佔)
NULL, // 預設安全性
OPEN_EXISTING, // 必須開啟現有裝置
FILE_FLAG_OVERLAPPED, // 關鍵:啟用重疊 I/O (非同步)
NULL // 無模板檔案
);
if (hCom == INVALID_HANDLE_VALUE) { /* 錯誤處理 */ }
FILE_FLAG_OVERLAPPED:啟用非同步 I/O,ReadFile/WriteFile 立即返回,背景完成傳輸,避免執行緒阻塞。搭配 GetOverlappedResult 等待完成。
DCB dcb = {0};
dcb.DCBlength = sizeof(DCB);
GetCommState(hCom, &dcb); // 取得目前設定
dcb.BaudRate = CBR_9600; // 9600 bps
dcb.ByteSize = 8; // 8 資料位
dcb.Parity = NOPARITY; // 無檢查位
dcb.StopBits = ONESTOPBIT; // 1 停止位
dcb.fBinary = TRUE; // 二進位模式
dcb.fParity = TRUE; // 啟用檢查位檢查 (若 Parity != NOPARITY)
SetCommState(hCom, &dcb); // 套用設定
SetupComm(hCom, 1024, 1024); // 輸入/輸出緩衝區 1024 bytes
PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
COMMTIMEOUTS ct = {0};
ct.ReadIntervalTimeout = 10; // 字元間最大間隔 10ms
ct.ReadTotalTimeoutMultiplier = 0;
ct.ReadTotalTimeoutConstant = 0;
ct.WriteTotalTimeoutMultiplier = 0;
ct.WriteTotalTimeoutConstant = 0;
SetCommTimeouts(hCom, &ct);
此設定:讀取單一字元最多等待 10ms,適合高頻輪詢模式。
SetCommMask(hCom, EV_RXCHAR); // 關注接收字元事件
OVERLAPPED ov = {0};
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // 手動重置事件
DWORD dwEvtMask;
BOOL fWaiting = WaitCommEvent(hCom, &dwEvtMask, &ov); // 非同步等待事件
if (!fWaiting && GetLastError() == ERROR_IO_PENDING) {
// 事件未立即發生,可執行其他工作或 WaitForSingleObject(ov.hEvent)
}
// 讀取
char buf[100];
DWORD dwRead;
OVERLAPPED ovRead = {0};
ovRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
ReadFile(hCom, buf, sizeof(buf)-1, &dwRead, &ovRead);
// 立即返回,實際資料稍後完成
// 稍後呼叫 GetOverlappedResult(hCom, &ovRead, &dwRead, TRUE) 等待完成
// 寫入
char msg[] = "Hello";
DWORD dwWritten;
OVERLAPPED ovWrite = {0};
ovWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
WriteFile(hCom, msg, strlen(msg), &dwWritten, &ovWrite);
CreateThread 執行 SerialPortThreadProc,迴圈 ReadFile (同步簡化版) → 收到資料 PostMessage 通知主視窗更新。
// 全域變數
HANDLE hCom, hCommThread;
#define WM_RECV_DATA (WM_USER + 1000)
// 背景執行緒
DWORD WINAPI SerialPortThreadProc(HWND hWnd) {
char buf[101];
DWORD dwRead;
while (true) {
ReadFile(hCom, buf, 100, &dwRead, NULL); // 同步讀取 (簡化)
if (dwRead > 0) {
buf[dwRead] = '\0';
PostMessage(hWnd, WM_RECV_DATA, (WPARAM)_strdup(buf), dwRead);
}
}
return 0;
}
// 主視窗訊息處理
LRESULT CSerialDlg::OnRecvData(WPARAM wParam, LPARAM lParam) {
char* str = (char*)wParam;
m_strRecv += str;
UpdateData(FALSE);
free(str); // _strdup 配對釋放
return 0;
}
// 發送按鈕
void CSerialDlg::OnSend() {
UpdateData(TRUE);
DWORD dwWritten;
WriteFile(hCom, m_strSend, m_strSend.GetLength(), &dwWritten, NULL);
}
ReadFile + OVERLAPPED,完成通知透過 GetOverlappedResult 或 I/O 完成埠 (IOCP) 處理,適合高併發伺服器。fOutxCtsFlow (CTS)、fOutxDsrFlow (DSR)、fRtsControl (RTS)、fOutX/fInX (XON/XOFF) 硬體/軟體流控設定。GetCommErrors、ClearCommError 讀取/清除溢位、幀錯誤、檢查錯誤。CSerialPort 類別,每埠一執行緒或 IOCP 統一調度。| 現象 | 可能原因 | 對策 |
|---|---|---|
| CreateFile 失敗 | 埠號錯誤、被佔用、驅動未安裝 | 檢查裝置管理員、關閉佔用程式 |
| 收到亂碼 | 鮑率/資料位/停止位/檢查位不匹配 | 雙端一致設定 DCB |
| 資料遺失 | 緩衝區溢位、無流控制 | 啟用 RTS/CTS 或 XON/XOFF、增大緩衝區 |
| 程式無回應 | 同步 ReadFile 永久阻塞 | 設定 ReadIntervalTimeout、改用非同步 |