一、简介
最近用到了获取,串口消息,消息处理主要用到了微软的<afxwin.h>头文件的afx_msg类。先调研一番消息处理函数,然后举例说明。那么我们分为下面几个步骤讨论MFC中afx_msg定义的消息。
步骤一:
在头文件KGenDel.h中,用afx_msg定义消息及其格式。
步骤二:
在KGenDel.cpp中,使用 BEGIN_MESSAGE_MAP 启动消息及其格式,它的具体格式如下:
BEGIN_MESSAGE_MAP(KGenDel, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER(&ReduceDB::OnTimer) //启动定时器
ON_MESSAGE(WM_COMM_RXCHAR, &KGenDel::OnComm)//单字节响应函数
ON_BN_CLICKED(IDC_BUTTON1, &KGenDel::OnBnClickedButton1)
ON_WM_CLOSE()
END_MESSAGE_MAP()
稍后我会解释每一行的代码。
步骤三:
在KGenDel.cpp中,对消息函数KGenDel::OnComm 和 ReduceDB::OnTimer进行实现。
二、afx_msg定义消息及其格式
afx_msg主要在MFC中应用,比较古老了。消息函数主要用到afx_msg关键字来定义。这几篇博客完美的解析了afx_msg关键字。
https://blog.csdn.net/holandstone/article/details/7452384
https://www.cnblogs.com/linkzijun/p/6196165.html
那么,我把我项目中的KGenDel.h文件拿出来,分析下afx_msg及其格式。
KGenDel.h:
class KGenDel : public CDialogEx
{
public:
KGenDel(CWnd* pParent = NULL); // 标准构造函数
virtual ~KGenDel();
//nIDEvent:是触发器的名字,启动定时器,间隔发指令给串口
afx_msg void OnTimer(UINT_PTR nIDEvent);
//接收来自串口的指令。
afx_msg LRESULT OnComm(WPARAM ch, LPARAM port);
}
三、BEGIN_MESSAGE_MAP 启动消息及其格式
https://www.cnblogs.com/liangjq/p/4462926.html
https://blog.csdn.net/luoti784600/article/details/10070939
参看https://www.cnblogs.com/linkzijun/p/6196165.html,得到了在MFC界面中,按钮的点击消息,使得界面显示的结果得以改变,它提供的BEGIN_MESSAGE_MAP 格式如下:
而我的KGenDel.cpp用到的格式如下:
BEGIN_MESSAGE_MAP(KGenDel, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER(&ReduceDB::OnTimer) //启动定时器
ON_MESSAGE(WM_COMM_RXCHAR, &KGenDel::OnComm)//单字节响应函数
ON_BN_CLICKED(IDC_BUTTON1, &KGenDel::OnBnClickedButton1)
ON_WM_CLOSE()
END_MESSAGE_MAP()
那么你可以看到,格式大致相同。基类CWnd与基类CDialogEx不同,消息类型也有很多种,比如CHANGE、CLICKED、TIMER不同。
那么来解说下这样的结构。
1、BEGIN_MESSAGE_MAP(参数1,参数2):表示开启消息映射,参数1为该类的类名,参数2为该类基类的类名。
https://www.cnblogs.com/liangjq/p/4462926.html
BEGIN_MESSAGE_MAP(CChileView, Cwnd)——CChileView.cpp类是继承Cwnd类,用来操作窗体。
BEGIN_MESSAGE_MAP(KGenDel, CDialogEx)——KGenDel.cpp类是继承CDialogEx类,用来操作对话框。
2、END_MESSAGE_MAP()——结束消息映射
3、ON_MESSAGE(参数1,参数2):参数1为响应的消息,参数2为该消息对应的处理的函数名。
ON_MESSAGE(WM_COMM_RXCHAR, &KGenDel::OnComm)—— 表示开启消息响应函数,一旦受到串口来的消息WM_COMM_RXCHAR(注意啊,WM_COMM_RXCHAR是一个消息,它由串口类触发),那么响应函数KGenDel::OnComm。这行代码就是用来接收串口传回来的指令。
4、ON_BN_CLICKED(参数1,参数2)—— 单击控件后,响应此消息。
5、ON_WM_TIMER(&ReduceDB::OnTimer) ——定时器。
四、消息函数KGenDel::OnComm 和 ReduceDB::OnTimer进行实现:工程中应用举例
1、.h文件中定义消息
LRESULT——表示长整数(默认)
WPARAM——表示长整数(默认)
LPARAM——表示短整数(默认)
ch——形参
port——形参
2、启动消息
BEGIN_MESSAGE_MAP(KGenDel, CDialogEx)——启动消息,KGenDel表示类。CDialogEx是系统生成了不知道啥意思。
ON_MESSAGE(WM_COMM_RXCHAR, &KGenDel::OnComm)——通过WM_COMM_RXCHAR与串口关联起来,一旦接收串口类的指令,则响应消息OnComm。事件上,OnComm是KGenDel.cpp的函数。
END_MESSAGE_MAP()——关闭
3、 消息函数的实现
定时器发消息给串口:
void KGenDel::OnTimer(UINT_PTR nIDEvent)
{
switch (nIDEvent)
{
case 1:
Exposure();//检测高压命令
//RetrErrorLog();//检索高压错误日志
//ErrorParameters();//检查曝光参数错误
//RetrieveExposure();//检索曝光结果
break;
default:
break;
}
}
注意啊, nIDEvent表示定时器的ID,因为一个系统可能要用多个定时器,所以,必须设置定时器的ID。
当ID = 1,那么我就启动第一个定时器,来处理第一件事情。
当ID = 2,那么我就启动第2个定时器,来处理第2件事情。
当ID = 3,那么我就启动第3个定时器,来处理第3件事情。
......
好了,那么,请问如何设置定时器?微软给我提供了SetTimer定时器:
int TimerID = 1;
SetTimer(TimerID, 200, NULL);
200ms表示定时器的时间:每隔200ms让它执行Exposure()函数。
接收来自串口的消息:
//消息响应函数:
LRESULT KGenDel::OnComm(WPARAM ch, LPARAM port)
{
if (16 > ch)
{
str.Format(_T("0%X "), ch);
}
else
{
str.Format(_T("%X "), ch);
}
m_RecvBuf += str;
data[cnt] = ch;
cnt++;
BYTE *p = (BYTE*)&data;
if (136 == data[12] && 240 == data[0]) //函数发送短命令
{
m_cLogger.WriteDebugLog("KGenDel::OnComm: 命令解析开始 ", 200);
m_cLogger.WriteDebugLog("KGenDel:: OnComm: " + m_RecvBuf, 200);
m_RecvBuf.ReleaseBuffer(); //第一步清空函数内存,保证存取上一次数据
m_RecvBuf.Empty(); //第二步清空函数内存,强制设置为空
m_cLogger.WriteDebugLog("KGenDel::OnComm: 命令结束", 200);
}
if (136 == data[18] && 240 == data[0]) //函数发送长命令
{
m_cLogger.WriteDebugLog("KGenDel::OnComm: 命令解析开始 ", 200);
m_cLogger.WriteDebugLog("KGenDel:: OnComm: " + m_RecvBuf, 200);
m_RecvBuf.ReleaseBuffer();
m_RecvBuf.Empty();
m_cLogger.WriteDebugLog("KGenDel::OnComm: 命令结束", 200);
}
OnComm1();
return 0;
}
4、串口类中,包含了WM_COMM_RXCHAR(虽然不知道串口怎么关联消息)