Windows API 直接操作串口(CreateFile/ReadFile/WriteFile + DCB 結構)雖功能完整,但設定繁瑣、錯誤易發。Microsoft 提供的 MSComm ActiveX 控件(MSCOMM32.OCX)將串口操作封裝為屬性/方法/事件模型,極大降低開發門檻。本文詳細列出關鍵屬性、事件處理架構,並提供完整的初始化、接收、發送程式碼範例。
專案整合 MSComm 控件
- 對話框編輯器右鍵 → 插入 ActiveX 控件 → 選擇 Microsoft Comm Control 6.0。
- 類別精靈為控件加入成員變數
m_comm (型別 MSComm 或 CMSComm)。
- 確認專案連結
mscomm32.ocx (通常自動註冊)。
核心屬性完整對照表
| 屬性 | 型別 | 說明 | 典型值 |
CommPort | short | 串口號 (1=COM1, 2=COM2...) | 1~4 |
Settings | CString | 通訊參數字串 "Baud,Parity,Data,Stop" | "9600,N,8,1" |
PortOpen | BOOL | 開啟/關閉串口,讀取可得知狀態 | TRUE/FALSE |
InputMode | long | 接收緩衝區讀取模式 | 0=Text, 1=Binary |
Input | VARIANT | 從接收緩衝區讀取資料 (唯讀) | — |
InBufferCount | short | 接收緩衝區位元組數 | — |
InBufferSize | short | 接收緩衝區大小 (預設 1024) | 1024~65535 |
Output | VARIANT | 寫入發送緩衝區 (唯寫) | — |
OutBufferCount | short | 發送緩衝區待發位元組數 | — |
OutBufferSize | short | 發送緩衝區大小 (預設 512) | 512~65535 |
InputLen | short | 每次 Input 讀取位元組數 (0=全部) | 0~65535 |
RThreshold | short | 接收觸發 OnComm 事件的字元數 | 1 (每字元觸發) |
SThreshold | short | 發送緩衝區剩餘觸發 OnComm 字元數 | 0 (不觸發) |
CommEvent | short | 最近發生的通訊事件碼 (唯讀) | 見下表 |
EOFEnable | BOOL | Text 模式下是否啟用 EOF 字元檢測 | FALSE |
EOFChar | BYTE | EOF 字元值 (預設 0x1A/Ctrl+Z) | 0x0D (CR), 0x0A (LF) |
OnComm 事件代碼
| 常數 | 值 | 事件描述 |
comEventBreak | 1 | 斷線偵測 |
comEventCTS | 2 | CTS 訊號變化 |
comEventDSR | 4 | DSR 訊號變化 |
comEventRing | 8 | 電話響鈴偵測 |
comEventRLSD | 16 | RLSD (CD) 訊號變化 |
comEventReceive | 2 | 接收緩衝區達 RThreshold |
comEventSend | 4 | 發送緩衝區低於 SThreshold |
comEventEOF | 8 | Text 模式偵測到 EOF 字元 |
完整程式碼範例
1. 串口初始化 (OnInitDialog 或按鈕)
void CMyDlg::InitSerialPort()
{
if (m_comm.GetPortOpen()) return; // 已開啟
m_comm.SetCommPort(1); // COM1
m_comm.SetSettings(_T("9600,N,8,1")); // 9600,無校驗,8資料位,1停止位
m_comm.SetInputMode(0); // Text 模式
m_comm.SetRThreshold(1); // 每收 1 字元觸發 OnComm
m_comm.SetInputLen(0); // 讀取所有可用字元
m_comm.SetInBufferSize(4096); // 擴大接收緩衝
m_comm.SetOutBufferSize(2048); // 擴大發送緩衝
m_comm.SetPortOpen(TRUE); // 開啟串口
if (!m_comm.GetPortOpen()) {
AfxMessageBox(_T("串口開啟失敗,請檢查埠號與驅動"));
}
}
2. 接收資料 (OnComm 事件處理)
void CMyDlg::OnComm()
{
// 僅處理接收事件
if (m_comm.GetCommEvent() != 2) return; // comEventReceive = 2
VARIANT var = m_comm.GetInput(); // 讀取接收緩衝區
if (var.vt == VT_BSTR) {
CString str = var.bstrVal; // Text 模式直接得 BSTR
m_strReceived += str;
UpdateData(FALSE); // 顯示至 Edit Control
}
else if (var.vt == (VT_ARRAY | VT_UI1)) {
// Binary 模式: SAFEARRAY of BYTE
SAFEARRAY* psa = var.parray;
long lBound, uBound;
SafeArrayGetLBound(psa, 1, &lBound);
SafeArrayGetUBound(psa, 1, &uBound);
long count = uBound - lBound + 1;
BYTE* pData = new BYTE[count];
SafeArrayAccessData(psa, (void**)&pData);
// 處理二進位資料...
SafeArrayUnaccessData(psa);
delete[] pData;
}
VariantClear(&var);
}
3. 發送資料
// 發送文字
void CMyDlg::OnSendText()
{
CString str = _T("Hello Serial!");
m_comm.SetOutput(COleVariant(str)); // 自動轉 BSTR
}
// 發送二進位
void CMyDlg::OnSendBinary()
{
BYTE cmd[] = {0xAA, 0x55, 0x01, 0x02, 0x03};
SAFEARRAYBOUND bound = {5, 0};
SAFEARRAY* psa = SafeArrayCreate(VT_UI1, 1, &bound);
BYTE* pData; SafeArrayAccessData(psa, (void**)&pData);
memcpy(pData, cmd, 5); SafeArrayUnaccessData(psa);
m_comm.SetOutput(COleVariant(psa));
SafeArrayDestroy(psa);
}
4. 關閉串口 (OnDestroy 或按鈕)
void CMyDlg::CloseSerialPort()
{
if (m_comm.GetPortOpen()) {
m_comm.SetPortOpen(FALSE);
}
}
進階技巧
| 需求 | 實作方法 |
| 非標準鮑率 (如 250000) | Settings 不支援,需用 Win32 API SetCommState 直接設定 DCB |
| 硬體流控 (RTS/CTS) | MSComm 不直接支援,需 EscapeCommFunction + DCB.fRtsControl |
| 多執行緒讀取 | MSComm 非執行緒安全,建議單一 UI 執行緒處理 OnComm,資料放入佇列供工作者執行緒取用 |
| 自動偵測可用 COM 埠 | 列舉登錄 HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM |
| 替代方案 | 新專案建議用 Win32 API 或 CSerial 開源類別,避免 OCX 部署相依 |
常見錯誤排查
- OnComm 不觸發:檢查
RThreshold > 0、InputMode 正確、控件已正確訊息映射 (ON_EVENT)。
- 亂碼:確認雙方
Settings 完全一致 (鮑率、校驗、資料位、停止位)。
- 發送無反應:檢查
OutBufferCount 是否長期累積 (對方未讀取/流控)。
- 部署缺少 OCX:安裝程式需包含
MSCOMM32.OCX 並註冊 (regsvr32)。
總結
MSComm ActiveX 控件以屬性/方法/事件模型將串口通訊簡化為「設定屬性 → 開啟埠 → 事件驅動收發」,適合快速開發儀器控制、POS 周邊、模組通訊等應用。掌握 Settings 字串格式、OnComm 事件分派、VARIANT 資料轉換三大核心,即可輕鬆駕馭串口通訊。
- 上一篇文章: MFC 並行埠控制類 CParallelPort:跨平台相容、Unicode 支援與 PC-to-PC 檔案傳輸實作
- 下一篇文章: TCP/IP Winsock 同步程式設計關鍵技巧:Nagle 算法關閉、緩衝區調優、連線超時優化與斷線偵測
Copyright ©
程式碼之家 All Rights Reserved