Win32 C++串口写入程序运行一段时间后冻结问题排查
问题定位与修复方案
你的程序冻结的核心原因是串口默认采用阻塞模式,且未设置合理的写入超时参数。当串口输出缓冲区被填满、硬件无法及时接收数据时,WriteFile会无限阻塞,导致程序卡死。结合你的代码,还有几个细节问题需要修正:
关键问题分析
- 未配置串口超时:默认情况下,
CreateFile打开串口后,COMMTIMEOUTS的写超时是无限等待。一旦输出缓冲区饱和,WriteFile会一直挂起,直到缓冲区有空间,这直接导致程序冻结。 - WriteFile参数错误:写入"x"和"y"时,你使用了
sizeof(message)-1(值为1),虽然结果碰巧正确,但逻辑混乱,应该直接指定要写入的字节数(比如1)。 - 未检查WriteFile返回值:无法判断写入操作是否失败,也无法及时处理错误。
修复步骤
- 添加串口超时配置:设置合理的写超时,避免
WriteFile无限阻塞。 - 修正WriteFile的字节数参数:明确指定要写入的字符长度。
- 添加错误检查:捕获
WriteFile的失败状态,便于排查问题。 - 可选:设置串口缓冲区大小:根据需求调整输入输出缓冲区,减少阻塞概率。
修改后的代码
#include <iostream> #include <windows.h> // 假设stop是全局变量或已定义的控制变量 volatile int stop = 0; int main() { HANDLE hSerial; DCB dcbSerialParams = { 0 }; COMMTIMEOUTS timeouts = { 0 }; // Open the serial port hSerial = CreateFile(L"COM1", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hSerial == INVALID_HANDLE_VALUE) { std::cout << "Error opening serial port, error code: " << GetLastError() << std::endl; return 1; } // Set the serial port parameters dcbSerialParams.DCBlength = sizeof(dcbSerialParams); if (GetCommState(hSerial, &dcbSerialParams) == 0) { std::cout << "Error getting serial port state, error code: " << GetLastError() << std::endl; CloseHandle(hSerial); return 1; } dcbSerialParams.BaudRate = CBR_9600; dcbSerialParams.ByteSize = 8; dcbSerialParams.StopBits = ONESTOPBIT; dcbSerialParams.Parity = NOPARITY; if (SetCommState(hSerial, &dcbSerialParams) == 0) { std::cout << "Error setting serial port state, error code: " << GetLastError() << std::endl; CloseHandle(hSerial); return 1; } // 配置串口超时:写操作最多等待500ms,避免无限阻塞 timeouts.WriteTotalTimeoutConstant = 500; timeouts.WriteTotalTimeoutMultiplier = 10; if (!SetCommTimeouts(hSerial, &timeouts)) { std::cout << "Error setting comm timeouts, error code: " << GetLastError() << std::endl; CloseHandle(hSerial); return 1; } // Send the serial message DWORD dwBytesWritten; char message[] = "c"; if (!WriteFile(hSerial, message, sizeof(message) - 1, &dwBytesWritten, NULL)) { std::cout << "Initial write failed, error code: " << GetLastError() << std::endl; CloseHandle(hSerial); return 1; } while (stop == 0) { // 写入"x",明确指定字节数1 if (!WriteFile(hSerial, "x", 1, &dwBytesWritten, NULL)) { std::cout << "Write 'x' failed, error code: " << GetLastError() << std::endl; // 可以在这里添加错误处理逻辑,比如重试或退出 } int i = 0; while (i < 7) { i++; //some code that works fine without serial port writings } // 写入"y",明确指定字节数1 if (!WriteFile(hSerial, "y", 1, &dwBytesWritten, NULL)) { std::cout << "Write 'y' failed, error code: " << GetLastError() << std::endl; // 可以在这里添加错误处理逻辑,比如重试或退出 } i = 0; while (i < 7) { i++; //some code that works fine without serial port writings } } // 程序结束时关闭串口句柄 CloseHandle(hSerial); return 0; }
额外建议
- 如果你的串口设备处理速度较慢,可以尝试增大输出缓冲区大小,通过
SetupComm(hSerial, 1024, 1024)设置(在SetCommState之后调用)。 - 若需要更高的并发性能,可以考虑使用异步IO(打开串口时指定
FILE_FLAG_OVERLAPPED),配合OVERLAPPED结构体实现非阻塞写入,但会增加代码复杂度。
内容的提问来源于stack exchange,提问作者DisplayDude
相关产品推荐
相关产品推荐

