首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ 序列埠程式設計完整指南:基於 Win32 API 非同步 I/O 實作

     編輯:關於VC++

Win32 序列埠 API 概覽

序列埠以檔案方式存取,核心 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 (Device Control Block)


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)


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);

完整範例:MFC 對話方塊 + 背景執行緒輪詢

架構

  • 主執行緒:UI 介面、發送按鈕處理。
  • 背景執行緒: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);
}

進階主題

  • 完整非同步 I/O:主執行緒發起 ReadFile + OVERLAPPED,完成通知透過 GetOverlappedResult 或 I/O 完成埠 (IOCP) 處理,適合高併發伺服器。
  • 流控制:DCB fOutxCtsFlow (CTS)、fOutxDsrFlow (DSR)、fRtsControl (RTS)、fOutX/fInX (XON/XOFF) 硬體/軟體流控設定。
  • 錯誤處理:GetCommErrors、ClearCommError 讀取/清除溢位、幀錯誤、檢查錯誤。
  • 多埠管理:封裝 CSerialPort 類別,每埠一執行緒或 IOCP 統一調度。

常見問題排查

總結

Win32 序列埠程式設計核心:CreateFile(FILE_FLAG_OVERLAPPED) → DCB 參數設定 → COMMTIMEOUTS 逾時策略 → SetCommMask 事件驅動 → ReadFile/WriteFile 非同步傳輸。掌握同步簡化版與完整非同步版兩種模式,可應對從簡單儀器控制到高效能資料採集的各類需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
現象可能原因對策
CreateFile 失敗埠號錯誤、被佔用、驅動未安裝檢查裝置管理員、關閉佔用程式
收到亂碼鮑率/資料位/停止位/檢查位不匹配雙端一致設定 DCB
資料遺失緩衝區溢位、無流控制啟用 RTS/CTS 或 XON/XOFF、增大緩衝區
程式無回應同步 ReadFile 永久阻塞設定 ReadIntervalTimeout、改用非同步