CreateFile/SetCommState/ReadFile/WriteFile/WaitCommEvent + Overlapped I/O,完全可控、無依賴、學習曲線陡峭。class TSerialPort {
HANDLE hComm;
OVERLAPPED ovRead, ovWrite;
HANDLE hEvent;
public:
bool Open(int portNo, DWORD baud=9600, BYTE parity=NOPARITY, BYTE data=8, BYTE stop=ONESTOPBIT) {
hComm = CreateFile(("\\\\.\\COM" + std::to_string(portNo)).c_str(),
GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, NULL);
if (hComm == INVALID_HANDLE_VALUE) return false;
DCB dcb{}; dcb.DCBlength = sizeof(dcb);
GetCommState(hComm, &dcb);
dcb.BaudRate = baud; dcb.Parity = parity; dcb.ByteSize = data; dcb.StopBits = stop;
dcb.fDtrControl = DTR_CONTROL_ENABLE; dcb.fRtsControl = RTS_CONTROL_ENABLE;
SetCommState(hComm, &dcb);
// 設置緩衝區、超時、事件遮罩...
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
ovRead.hEvent = ovWrite.hEvent = hEvent;
SetCommMask(hComm, EV_RXCHAR | EV_ERR);
return true;
}
int Read(void* buf, int len, DWORD timeout=1000) {
DWORD read=0; ResetEvent(hEvent);
if (!ReadFile(hComm, buf, len, &read, &ovRead)) {
if (GetLastError() == ERROR_IO_PENDING)
WaitForSingleObject(hEvent, timeout), GetOverlappedResult(hComm, &ovRead, &read, FALSE);
}
return read;
}
int Write(const void* buf, int len) {
DWORD written=0; ResetEvent(hEvent);
WriteFile(hComm, buf, len, &written, &ovWrite);
if (GetLastError() == ERROR_IO_PENDING)
WaitForSingleObject(hEvent, 5000), GetOverlappedResult(hComm, &ovWrite, &written, FALSE);
return written;
}
void Close() { if (hComm!=INVALID_HANDLE_VALUE) CloseHandle(hComm), hComm=INVALID_HANDLE_VALUE; }
};
TMSComm,拖入表單。CommPort、Settings (如 "9600,n,8,1")、PortOpen、InBufferCount、OutBufferCount、RThreshold (接收觸發閾值)、SThreshold (發送觸發閾值)。OnComm 事件:CommEvent 屬性指示事件類型(comEvReceive、comEvSend、comEvEOF...)。// 發送 (OleVariant 數組)
OleVariant tx = VarArrayCreate(OPENARRAY(int, (0, n-1)), varByte);
for (int i=0; iOutput = tx;
// 接收 (在 OnComm 或 Timer 中)
if (MSComm1->InBufferCount > 0) {
OleVariant rx = MSComm1->Input; // 同樣為 OleVariant 數組
int cnt = rx.GetElement(0); // 實際結構視版本而定,需實測
// 複製到自有緩衝區...
}
OnComm 事件不可靠,高負載易丟失,**強烈建議用 Timer (50~100ms) 主動輪詢 InBufferCount 讀取**。Input/Output 在 Object Inspector 不顯示,但運行時存在,類型為 OleVariant。| 庫 | 語言 | 特點 |
|---|---|---|
| Boost.Asio serial_port | C++11+ | 跨平台、異步、標準庫風格、Header-only 可選 |
| serialib | C++ | 輕量、單頭文件、Windows/Linux 同介面 |
| QSerialPort (Qt) | C++/Qt | 信號槽、事件循環整合、文檔豐富 |
| Windows Implementation Library (WIL) | C++ | RAII 封裝 HANDLE、OVERLAPPED、現代 C++ 風格 |
新專案**首選 Win32 API 封裝或 Boost.Asio**,掌握 Overlapped I/O 即掌握 Windows 高效串口編程精髓;舊專案維護 MSComm32 時注意 OnComm 不可靠、64 位不兼容,規劃遷移路徑。