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

VC++ 串口通訊完整指南:MSComm ActiveX 控件 vs 直接 Win32 API (CreateFile/ReadFile/WriteFile) 兩大方案對比

     編輯:VC 教學

串口 (COM 口) 通訊是工業控制、儀器儀表、嵌入式互聯的基石。VC++ 提供兩條主流路徑:Microsoft Comm Control 6.0 (MSComm32.ocx) ActiveX 控件 簡單快速,Win32 API (CreateFile/ReadFile/WriteFile/DeviceIoControl) 底層可控、無運行時依賴。本文對比兩者架構、代碼範例、優劣勢、避坑指南。

方案一:MSComm ActiveX 控件 (簡單、事件驅動)

1. 部署與引入

  • 專案 Project -> Add To Project -> Components and Controls -> Registered ActiveX Controls -> Microsoft Comm Control 6.0。
  • 對話框編輯器放入控件,ClassWizard 生成成員變數 MSComm m_Comm。

2. 核心屬性設置

// OnInitDialog
m_Comm.SetCommPort(1);           // COM1
m_Comm.SetSettings(_T("9600,n,8,1")); // 波特率,校驗,資料位,停止位
m_Comm.SetPortOpen(TRUE);        // 打開端口
m_Comm.SetInBufferCount(0);      // 清空輸入緩衝
m_Comm.SetInputLen(0);           // 0=讀取所有可用字元
m_Comm.SetRThreshold(1);         // 觸發 OnComm 事件的最小字元數

3. 事件驅動接收 (OnComm)

ClassWizard 為控件添加 OnComm 事件處理:

void CMyDlg::OnComm() {
    switch (m_Comm.GetCommEvent()) {
    case comEventReceive: { // 接收到數據
        VARIANT var = m_Comm.GetInput(); // 讀取輸入緩衝
        // var 為 VT_ARRAY | VT_UI1 (BYTE 陣列) 或 VT_BSTR
        // 處理數據...
        break;
    }
    case comEventSend: break; // 發送完成
    case comEventBreak: break; // 斷線
    }
}

4. 發送數據

CString cmd = _T("GET_DATA\r\n");
m_Comm.SetOutput(COleVariant(cmd)); // 自動轉 BSTR
// 或二進位:
BYTE data[] = {0x01, 0x02, 0x03};
VARIANT var; var.vt = VT_ARRAY | VT_UI1;
var.parray = SafeArrayCreateVector(VT_UI1, 0, 3);
memcpy(var.parray->pvData, data, 3);
m_Comm.SetOutput(var);

5. 關閉

m_Comm.SetPortOpen(FALSE); // OnDestroy 中調用

方案二:Win32 API 直接操作 (專業、高效、無 OCX 依賴)

1. 打開串口 (CreateFile)

HANDLE hCom = CreateFile(
    _T("COM1"),                    // 埠名 (COM10+ 需用 \\\\.\\COM10)
    GENERIC_READ | GENERIC_WRITE,  // 讀寫
    0,                             // 獨佔
    NULL,                          // 安全屬性
    OPEN_EXISTING,                 // 必須存在
    FILE_FLAG_OVERLAPPED,          // 關鍵:異步 I/O (串口必須)
    NULL
);
if (hCom == INVALID_HANDLE_VALUE) { /* 錯誤處理 */ }

2. 設定通訊參數 (DCB + SetCommState)


DCB dcb = {0}; dcb.DCBlength = sizeof(DCB);
GetCommState(hCom, &dcb); // 獲取當前
dcb.BaudRate = CBR_9600;
dcb.ByteSize = 8;
dcb.Parity   = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fBinary  = TRUE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
SetCommState(hCom, &dcb);

3. 設定超時 (COMMTIMEOUTS) - 關鍵決定讀寫行為


COMMTIMEOUTS ct = {0};
// 讀取: 總計最多等待 100ms, 每字元間隔 10ms
ct.ReadIntervalTimeout = 10;
ct.ReadTotalTimeoutMultiplier = 10;
ct.ReadTotalTimeoutConstant = 100;
// 寫入: 總計最多 1000ms
ct.WriteTotalTimeoutMultiplier = 10;
ct.WriteTotalTimeoutConstant = 1000;
SetCommTimeouts(hCom, &ct);

4. 異步讀寫 (OVERLAPPED + 事件通知) - 專業標準


// 讀取
OVERLAPPED ovRead = {0}; ovRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
BYTE buf[256]; DWORD read;
BOOL ok = ReadFile(hCom, buf, 256, &read, &ovRead);
if (!ok && GetLastError() == ERROR_IO_PENDING) {
    // 等待完成 (或用 WaitForMultipleObjects 整合到主訊息迴路)
    WaitForSingleObject(ovRead.hEvent, INFINITE);
    GetOverlappedResult(hCom, &ovRead, &read, TRUE);
    // buf[0..read-1] 為接收數據
}

// 寫入
OVERLAPPED ovWrite = {0}; ovWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
BYTE cmd[] = {0xAA, 0x55}; DWORD written;
WriteFile(hCom, cmd, 2, &written, &ovWrite);
// 同樣等待 ovWrite.hEvent

5. 事件驅動替代方案: WaitCommEvent


// 執行緒循環等待事件 (RXCHAR, TXEMPTY, BREAK, ERR...)
DWORD evtMask;
WaitCommEvent(hCom, &evtMask, &ovRead); // 異步等待
if (evtMask & EV_RXCHAR) { /* 讀取數據 */ }

6. 關閉


CloseHandle(hCom);
CloseHandle(ovRead.hEvent); CloseHandle(ovWrite.hEvent);

方案對比總結

維度MSComm ActiveXWin32 API
部署依賴需註冊 MSCOMM32.OCX (系統目錄)純系統 DLL (kernel32.dll), 零依賴
程式模型事件驅動 (OnComm), 易上手同步/異步 API, 需自行管理執行緒/重疊 I/O
效能/延遲ActiveX 包裝層有開銷零開銷, 直接核心轉換
功能完整性基本功能覆蓋, 進階控制 (DTR/RTS 細粒度、特殊波特率) 受限完全控制 (DCB、Timeouts、Modem 狀態、特殊 IOCTL)
穩定性OCX 相容性問題 (64位、Win10+ 易失效)系統級 API, 極度穩定
調試Visual Studio 整合良好需自行記錄/斷點, 較繁瑣
適用場景快速原型、簡單儀表、教學、舊專案維護產品級、高可靠、高效能、64位、長期維護專案

避坑指南 (通用)

  1. COM10 以上: 必須用 \\\\.\\COM10 格式 (Win32) 或 MSComm SetCommPort(10) (6.0 支援)。
  2. 緩衝區清理: 打開後 PurgeComm(hCom, PURGE_RXCLEAR | PURGE_TXCLEAR)。
  3. 二進位 vs 文本: 串口傳輸位元組流,協定層自行定義幀結構 (長度、校驗、分隔符),切勿假設文本行結束符。
  4. 執行緒安全: 串口句柄若多執行緒存取,需互斥鎖保護。
  5. 權限: 需屬於 Dialout 群組或管理員 (Linux/Unix),Windows 通常需管理員或 Serial Port 裝置權限。

現代專案強烈建議使用 Win32 API 方案,構建專用 CSerialPort 封裝類管理生命週期、異步讀寫、協定解析,獲得最佳穩定性與可移植性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved