实现C++ 字符串消息和handler的关联

本文介绍了如何在C++中实现类似于MFC的消息映射机制,通过自定义宏定义,将字符串消息与处理函数关联。通过声明和使用如DECLARE_TESTCOMMAND_MAP(), ON_TEST_COMMAND等宏,创建一个简单的消息映射系统,使得可以调用ExecuteCommand接口来执行对应的消息处理函数。" 117844196,10541728,前端纯JS实现word文档导出,"['前端开发', 'Vue', '文件操作', 'docx模板']

       有过MFC编程经验的都了解MFC中的消息封装。巧妙地利用宏定义实现了windows消息和消息处理函数的映射(也可以理解成UINT型命令和handler之间的映射)。通常,我们会在MFC的窗口类头文件中看到一行宏定义代码。

 

DECLARE_MESSAGE_MAP()


        而在窗口类文件中,会看到类似如下的一堆宏定义:

 

 

 

 

BEGIN_MESSAGE_MAP(DlgTest, CDialogEx)
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_ERASEBKGND()
	ON_BN_CLICKED(IDC_BTN_OK, OnBnClickedBtnOk)
	ON_MESSAGE(WM_TEST, OnTestHandler)
END_MESSAGE_MAP()

     
        我们追根溯源,找到这些宏定义的定义处会发现,这些宏定义定义一个窗口类的类static变量和一系列配套的类static函数,这些变量和函数项目配合,封装了MFC的消息映射。

 

 

#define DECLARE_MESSAGE_MAP() \
protected: \
	static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
	virtual const AFX_MSGMAP* GetMessageMap() const; \


#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
	PTM_WARNING_DISABLE \
	const AFX_MSGMAP* theClass::GetMessageMap() const \
		{ return GetThisMessageMap(); } \
	const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
	{ \
		typedef theClass ThisClass;\
		typedef baseClass TheBaseClass;\
		static const AFX_MSGMAP_ENTRY _messageEntries[] = \
		{

#define END_MESSAGE_MAP() \
		{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
	}; \
		static const AFX_MSGMAP messageMap = \
		{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
		return &messageMap; \
	}\
	PTM_WARNING_RESTORE


        其中,实现消息映射最关键的是变量#define DECLARE_MESSAGE_MAP() \AFX_MSGMAP_ENTRY _messageEntries,

<think>我们需要解决的是在C++实现串口的异步I/O操作。根据用户的要求,他希望通过异步方式实现串口数据读取,即当串口有数据到达时立即读取并返回。根据提供的引用,我们可以总结出两种主要方法:1.使用`WaitCommEvent`函数等待`EV_RXCHAR`事件(表示接收到新字符)的发生,然后使用`ReadFile`读取数据。2.利用`ReadFile`函数的特性,设置异步操作,当缓冲区中有足够数据(达到指定字节数)时,函数立即返回。此外,引用[3]提到了使用Boost.Asio库来实现串口异步通信,这是一种跨平台且更现代化的方法。由于用户明确提到异步I/O,我们将重点介绍两种实现方式:使用WindowsAPI的原生异步I/O操作,以及使用Boost.Asio库。下面我们将分步骤介绍:一、使用WindowsAPI实现异步串口I/O步骤:1.打开串口:使用`CreateFile`打开串口,注意要设置`FILE_FLAG_OVERLAPPED`标志,表示使用异步(重叠)I/O。2.配置串口参数:使用`GetCommState``SetCommState`设置波特率、数据位、停止位校验位。3.设置超时:使用`SetCommTimeouts`设置超时参数,对于异步操作,超时设置可以避免无限等待。4.设置事件掩码:使用`SetCommMask`设置我们关心的事件,例如`EV_RXCHAR`(接收到字符)。5.创建事件对象:为读写操作等待事件创建OVERLAPPED结构,并关联事件对象(通过`CreateEvent`)。6.启动异步操作:方法一(等待事件):-调用`WaitCommEvent`函数(传入一个OVERLAPPED结构),函数会立即返回,等待事件完成。-使用`WaitForSingleObject`或`WaitForMultipleObjects`等待事件触发(或者使用异步过程调用(APC))。-当事件触发(如`EV_RXCHAR`),则调用`ReadFile`读取数据。方法二(直接使用`ReadFile`):-调用`ReadFile`函数(传入OVERLAPPED结构),函数立即返回,如果数据未到达,则返回错误`ERROR_IO_PENDING`。-然后使用`WaitForSingleObject`或`GetOverlappedResult`等待读取完成。7.当操作完成(事件被触发或者I/O操作完成),处理接收到的数据。8.关闭串口:关闭所有句柄,如引用[2]中所示。二、使用Boost.Asio库Boost.Asio提供了一种更简洁的异步I/O编程模型,支持跨平台(Windows/Linux等)。步骤:1.创建`io_context`对象。2.创建串口对象:`boost::asio::serial_port`,并打开串口设备(如COM3)。3.设置串口参数:使用`set_option`设置波特率等。4.异步读取:使用`async_read_some`或`async_read`(需要指定完成条件,如至少读取一个字节)启动异步读取操作,并指定一个完成回调函数。5.运行`io_context`:在回调函数中处理接收到的数据,然后再次启动异步读取以持续监听。6.关闭串口:在不需要时关闭串口。下面我们分别给出两种方法的代码框架:方法一:WindowsAPI(等待事件方式)注意:以下代码仅为示例,未包含完整错误处理。```cpp#include<Windows.h>#include<iostream>HANDLEhCom;OVERLAPPEDovWait={0};DWORDdwEvtMask;//初始化串口boolOpenSerialPort(constchar*portName){hCom=CreateFile(portName,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);if(hCom==INVALID_HANDLE_VALUE){std::cerr<< "CreateFilefailed"<< std::endl;returnfalse;}//配置串口参数(省略)//设置事件掩码if(!SetCommMask(hCom,EV_RXCHAR)){std::cerr<< "SetCommMaskfailed"<< std::endl;returnfalse;}//创建事件对象ovWait.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);if(ovWait.hEvent==NULL){std::cerr<< "CreateEventfailed"<< std::endl;returnfalse;}//启动等待事件if(!WaitCommEvent(hCom,&dwEvtMask,&ovWait)){if(GetLastError()!=ERROR_IO_PENDING){std::cerr<< "WaitCommEventfailed"<< std::endl;returnfalse;}}returntrue;}voidReadData(){DWORDdwRead;charbuffer[256];OVERLAPPEDovRead={0};ovRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);while(true){//等待事件(这里只等待了ovWait事件,实际应用中可能需要同时等待多个事件)DWORDdwRet=WaitForSingleObject(ovWait.hEvent,INFINITE);if(dwRet==WAIT_OBJECT_0){//检查事件类型if(dwEvtMask&EV_RXCHAR){//读取数据if(!ReadFile(hCom,buffer,sizeof(buffer),&dwRead,&ovRead)){if(GetLastError()!=ERROR_IO_PENDING){//错误处理break;}//等待读取完成WaitForSingleObject(ovRead.hEvent,INFINITE);}//处理读取到的数据if(dwRead>0){//处理buffer中的dwRead个字节}//重置等待事件,以便下一次等待ResetEvent(ovWait.hEvent);//再次启动等待事件if(!WaitCommEvent(hCom,&dwEvtMask,&ovWait)){if(GetLastError()!=ERROR_IO_PENDING){break;}}}}else{break;}}}//关闭串口(参考引用[2])voidCloseSerialPort(){if(hCom!=INVALID_HANDLE_VALUE){CloseHandle(hCom);hCom=INVALID_HANDLE_VALUE;}if(ovWait.hEvent!=NULL){CloseHandle(ovWait.hEvent);}}```方法二:Boost.Asio```cpp#include<boost/asio.hpp>#include<boost/bind/bind.hpp>#include<iostream>boost::asio::io_contextio;boost::asio::serial_portserial(io);voidread_handler(constboost::system::error_code&ec,std::size_tbytes_transferred,char*buffer){if(!ec){//处理接收到的数据std::cout.write(buffer,bytes_transferred);//继续异步读取serial.async_read_some(boost::asio::buffer(buffer,256),boost::bind(read_handler,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred,buffer));}else{//处理错误std::cerr<< "Error:"<< ec.message()<< std::endl;}}intmain(){try{serial.open("COM3");//打开串口//配置串口参数serial.set_option(boost::asio::serial_port::baud_rate(9600));serial.set_option(boost::asio::serial_port::character_size(8));serial.set_option(boost::asio::serial_port::parity(boost::asio::serial_port::parity::none));serial.set_option(boost::asio::serial_port::stop_bits(boost::asio::serial_port::stop_bits::one));charbuffer[256];//开始异步读取serial.async_read_some(boost::asio::buffer(buffer),boost::bind(read_handler,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred,buffer));//运行io_contextio.run();}catch(conststd::exception&e){std::cerr<< e.what()<< std::endl;}return0;}```注意事项:1.在使用WindowsAPI时,需要处理各种错误等待状态,代码相对复杂。2.Boost.Asio版本简洁,且跨平台,但需要安装Boost库。三、根据引用[3]的建议,使用Boost.Asio时还可以添加超时处理、协议解析、队列管理等功能。结论:如果用户希望使用原生WindowsAPI,可以采用第一种方法。如果希望跨平台或使用更现代的C++风格,建议使用Boost.Asio。相关问题:1.如何处理异步I/O操作中的错误?2.如何设置串口的超时参数?3.在Boost.Asio中,如何同时进行异步读写操作?4.如何保证多线程环境下串口操作的安全性?以上代码说明提供了实现异步串口I/O的基本框架,用户可以根据具体需求调整扩展。</think> ### C++ 串口异步 I/O 实现详解 在 C++实现串口异步 I/O 操作可以通过两种主要方式:Windows API 原生实现或使用 Boost.Asio 库。以下是完整实现方案: #### 一、Windows API 原生异步实现 核心步骤及代码框架: ```cpp #include <Windows.h> HANDLE hSerial; OVERLAPPED ovRead = {0}; // 1. 打开串口(异步模式) bool OpenSerialPort(LPCSTR portName) { hSerial = CreateFile(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (hSerial == INVALID_HANDLE_VALUE) return false; // 2. 配置串口参数(波特率/数据位等) DCB dcb = {0}; dcb.DCBlength = sizeof(DCB); GetCommState(hSerial, &dcb); dcb.BaudRate = CBR_9600; dcb.ByteSize = 8; dcb.StopBits = ONESTOPBIT; dcb.Parity = NOPARITY; SetCommState(hSerial, &dcb); // 3. 创建读取事件 ovRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); SetCommMask(hSerial, EV_RXCHAR); // 监听接收事件 // 4. 启动异步读取线程 CreateThread(NULL, 0, ReadThread, NULL, 0, NULL); return true; } // 5. 异步读取线程 DWORD WINAPI ReadThread(LPVOID lpParam) { DWORD dwEvtMask; while (true) { // 5.1 等待接收事件(核心) WaitCommEvent(hSerial, &dwEvtMask, &ovRead); DWORD dwWait = WaitForSingleObject(ovRead.hEvent, INFINITE); if (dwWait == WAIT_OBJECT_0) { // 5.2 检查接收状态 DWORD dwErrors; COMSTAT comstat; ClearCommError(hSerial, &dwErrors, &comstat); if (comstat.cbInQue > 0) { // 5.3 执行异步读取 char buffer[256]; DWORD dwRead; OVERLAPPED ov = {0}; ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!ReadFile(hSerial, buffer, comstat.cbInQue, &dwRead, &ov)) { if (GetLastError() == ERROR_IO_PENDING) { // 等待读取完成 WaitForSingleObject(ov.hEvent, INFINITE); GetOverlappedResult(hSerial, &ov, &dwRead, TRUE); } } // 处理接收数据... CloseHandle(ov.hEvent); } } } return 0; } // 6. 关闭串口(引用[2]示例) void ClosePort() { SetCommMask(hSerial, 0); if (hSerial != INVALID_HANDLE_VALUE) { CloseHandle(hSerial); hSerial = INVALID_HANDLE_VALUE; } if (ovRead.hEvent != NULL) { CloseHandle(ovRead.hEvent); ovRead.hEvent = NULL; } } ``` #### 二、Boost.Asio 跨平台实现 基于引用[3]的增强版实现: ```cpp #include <boost/asio.hpp> #include <boost/bind/bind.hpp> boost::asio::io_service io; boost::asio::serial_port serial(io); void read_handler(const boost::system::error_code& ec, size_t bytes_transferred); void OpenPort(std::string port) { serial.open(port); // 打开串口 // 配置参数(简化版) serial.set_option(boost::asio::serial_port::baud_rate(9600)); serial.set_option(boost::asio::serial_port::flow_control( boost::asio::serial_port::flow_control::none)); // 启动异步读取 char buffer[256]; serial.async_read_some(boost::asio::buffer(buffer), boost::bind(read_handler, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); io.run(); // 启动I/O服务 } void read_handler(const boost::system::error_code& ec, size_t bytes_read) { if (!ec) { // 处理接收数据... char new_buffer[256]; // 继续启动下次异步读取(核心机制) serial.async_read_some(boost::asio::buffer(new_buffer), boost::bind(read_handler, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } } // 关闭串口 void ClosePort() { serial.cancel(); serial.close(); } ``` #### 关键机制对比 | **特性** | **Windows API** | **Boost.Asio** | |------------------|----------------------------------------|-----------------------------| | 异步原理 | 事件对象+OVERLAPPED结构 | 异步回调+io_service调度 | | 线程要求 | 需要专用线程处理事件 | 单线程处理所有I/O | | 跨平台支持 | 仅Windows | 跨平台(Windows/Linux/macOS) | | 超时处理 | 需手动实现WaitForSingleObject超时 | 内置deadline_timer | | 数据到达即读取 | 通过EV_RXCHAR事件触发(引用[1]) | 自动触发async_read_some回调 | | 实现复杂度 | ★★★★☆ (需处理底层细节) | ★★★☆☆ (封装良好) | ### 实际应用注意事项 1. **事件驱动机制**:核心原理都是数据到达时通过事件(EV_RXCHAR)或回调立即响应 2. **资源管理**:必须关闭所有句柄(引用[2]强调关闭hEventhCom) 3. **错误处理**:需检查: ```cpp if (GetLastError() == ERROR_IO_PENDING) // Windows if (!ec) // Boost ``` 4. **性能优化**: - 使用循环缓冲区减少内存分配 - 设置合理超时避免阻塞 - 双缓冲技术(预置下个异步读取) ### 相关问题 1. 如何解决异步读取中的数据分包问题? 2. 串口通信中如何实现可靠的超时控制机制? 3. 对比分析同步异步串口通信的性能差异? 4. 如何在多线程环境下安全管理串口资源? 5. 如何实现跨平台的串口热插拔检测? > 提示:在复杂工业场景中,建议优先选择 Boost.Asio 方案(引用[3]),因其提供更完善的超时处理异常安全机制。对于 Windows 专用系统,原生 API 可获得更精细控制(引用[1][2])。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值