搜索
bottom↓
回复: 0

全功能智能车之PC端MFC串口助手程序框架分析(第八篇)

[复制链接]

出0入0汤圆

发表于 2018-5-11 17:28:24 | 显示全部楼层 |阅读模式
先放源码:http://download.csdn.net/detail/chengdong1314/9266289
特别声明,该源码不是本人自己写的,源码出处:http://blog.sina.com.cn/s/blog_5db34aa401012hca.html

现在来分析程序:
先声明:本人研究MFC只是半路出家,以前只是写单片机的,在此之前也会一些PHP设计和安卓设计,如有不当之处,敬请见谅。
下面是在Serial1App.cpp文件里的定义:

// CSerial1AppAppBEGIN_MESSAGE_MAP(CSerial1AppApp, CWinApp)        ON_COMMAND(ID_HELP, &CWinApp::OnHelp)END_MESSAGE_MAP()// 唯一的一个 CSerial1AppApp 对象CSerial1AppApp theApp;让我们来看看CSerial1AppDlg这个类:
//#define WM_MYUPDATEDATA WM_USER+100// CSerial1AppDlg 对话框class CSerial1AppDlg : public CDialogEx{// 构造public:        CSerial1AppDlg(CWnd* pParent = NULL);        // 标准构造函数// 对话框数据        <span style="color:#FF0000;"><strong>enum { IDD = IDD_SERIAL1APP_DIALOG };</strong></span>        protected:        virtual void DoDataExchange(CDataExchange* pDX);        // DDX/DDV 支持// 实现protected:        HICON m_hIcon;        // 生成的消息映射函数        virtual BOOL OnInitDialog();        afx_msg void OnSysCommand(UINT nID, LPARAM lParam);        afx_msg void OnPaint();        afx_msg HCURSOR OnQueryDragIcon();        DECLARE_MESSAGE_MAP()public:        afx_msg void OnBnClickedSendBtn();        afx_msg void OnBnClickedOpencomBtn();        afx_msg void OnBnClickedClosecomBtn();        LRESULT OnUpdateMyData(WPARAM wParam, LPARAM lParam);        CEdit send_edit;//发送编辑        CString send_string;//发送数据        CButton open_btn;//打开串口        CButton close_btn;//关闭串口        CButton send_btn;//发送数据btn        CEdit receive_edit;//接收编辑        CString receive_string;//接收数据        CComboBox com_combobox;//com口多少        HANDLE hThread_receiver;        DWORD ThreadID;};其中:enum { IDD = IDD_SERIAL1APP_DIALOG };制定了布局,在Serial1App.rc2文件里有定义:

参考http://www.cnblogs.com/lidabo/archive/2012/07/19/2598646.html可知:程序第一个执行的是实例theApp的构造函数,也就是本文件里的:
BOOL CSerial1AppApp::InitInstance(){        // 如果一个运行在 Windows XP 上的应用程序清单指定要        // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,        //则需要 InitCommonControlsEx()。否则,将无法创建窗口。        INITCOMMONCONTROLSEX InitCtrls;        InitCtrls.dwSize = sizeof(InitCtrls);        // 将它设置为包括所有要在应用程序中使用的        // 公共控件类。        InitCtrls.dwICC = ICC_WIN95_CLASSES;        InitCommonControlsEx(&InitCtrls);        CWinApp::InitInstance();        AfxEnableControlContainer();        // 创建 shell 管理器,以防对话框包含        // 任何 shell 树视图控件或 shell 列表视图控件。        CShellManager *pShellManager = new CShellManager;        // 标准初始化        // 如果未使用这些功能并希望减小        // 最终可执行文件的大小,则应移除下列        // 不需要的特定初始化例程        // 更改用于存储设置的注册表项        // TODO: 应适当修改该字符串,        // 例如修改为公司或组织名        SetRegistryKey(_T("串口助手"));        <span style="color:#FF0000;"><strong>CSerial1AppDlg dlg;        m_pMainWnd = &dlg;        INT_PTR nResponse = dlg.DoModal();</strong></span>        if (nResponse == IDOK)        {                // TODO: 在此放置处理何时用                //  “确定”来关闭对话框的代码        }        else if (nResponse == IDCANCEL)        {                // TODO: 在此放置处理何时用                //  “取消”来关闭对话框的代码        }        // 删除上面创建的 shell 管理器。        if (pShellManager != NULL)        {                delete pShellManager;        }        // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,        //  而不是启动应用程序的消息泵。        return FALSE;}继续:这个函数:CSerial1AppDlg dlg;之前的注释里已经有所说明,而且不是我的重点,所以跳过不语:
<span style="color:#FF0000;"><strong>CSerial1AppDlg dlg;m_pMainWnd = &dlg;INT_PTR nResponse = dlg.DoModal();</strong></span>执行最后一句话之后将跳到Serial1AppDlg.cpp文件里的void CSerial1AppDlg::OnBnClickedOpencomBtn()函数里执行:具体原因参看:http://wenku.baidu.com/link?url= ... eg9RZg2U47p0LRgEbaq
本人在此摘抄出目前对我最主要的执行流程:
            程序中调用DoModal()时,只有当用户关闭对话框后,才能返回到程序中,此所谓的模式对话框。
            DoModal被调用时,实际上在幕后会引起一系列的动作,调用关系总结如下:
            CMyDialog::DoModal()
                  CMyDialog::OnInitDialog()
                      CDialog::OnInitDialog()
                         CWnd::UpdateData(false)
                            CMyDialog::DoDataExchange;

继续分析void CSerial1AppDlg::OnBnClickedOpencomBtn()函数,源码如下:

void CSerial1AppDlg::OnBnClickedOpencomBtn(){        // TODO: 在此添加控件通知处理程序代码        /*串口的初始化*/        CString str_com=L"";        com_combobox.GetWindowTextW(str_com);        hCom=CreateFile(str_com,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);   //打开串口        if(hCom==(HANDLE)-1)        {                MessageBox(L"打开COM失败!");        }        else{                open_btn.EnableWindow(FALSE);    //设置打开串口按钮的属性,其没有焦点                close_btn.EnableWindow(TRUE);                send_btn.EnableWindow(TRUE);                com_combobox.EnableWindow(FALSE);                SetupComm(hCom,100,100); //输入缓冲区和输出缓冲区的大小都是100                COMMTIMEOUTS TimeOuts;                //设定读超时                TimeOuts.ReadIntervalTimeout=MAXDWORD;                TimeOuts.ReadTotalTimeoutMultiplier=0;                TimeOuts.ReadTotalTimeoutConstant=0;                //在读一次输入缓冲区的内容后读操作就立即返回,                //而不管是否读入了要求的字符。                //设定写超时                TimeOuts.WriteTotalTimeoutMultiplier=100;                TimeOuts.WriteTotalTimeoutConstant=500;                SetCommTimeouts(hCom,&TimeOuts); //设置超时                DCB dcb;                GetCommState(hCom,&dcb); //获取串口状态                dcb.BaudRate=9600; //波特率为9600                dcb.ByteSize=8; //每个字节有8位                dcb.Parity=NOPARITY; //无奇偶校验位                dcb.StopBits=ONESTOPBIT ; //停止位                SetCommState(hCom,&dcb);  //设置串口参数                PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);   //清空缓冲区                //创建新线程读取串口数据                hThread_receiver=CreateThread(NULL,  0,  (LPTHREAD_START_ROUTINE)UpdateUIThread,  this,  0,  (unsigned long *)&ThreadID);         }}从这里可以看出,这个函数就十分的亲切了,初始化对话框和设置出口在这里执行。

由流程图可知下一步将执行void CSerial1AppDlg::DoDataExchange(CDataExchange* pDX)函数,源码如下:
void CSerial1AppDlg::DoDataExchange(CDataExchange* pDX){        CDialogEx::DoDataExchange(pDX);        DDX_Control(pDX, ID_SEND_EDIT, send_edit);        DDX_Text(pDX, ID_SEND_EDIT, send_string);        DDX_Control(pDX, ID_OPENCOM_BTN, open_btn);        DDX_Control(pDX, ID_CLOSECOM_BTN, close_btn);        DDX_Control(pDX, ID_SEND_BTN, send_btn);        DDX_Control(pDX, ID_GET_EDIT, receive_edit);        DDX_Text(pDX, ID_GET_EDIT, receive_string);        DDX_Control(pDX, ID_COMNUM_COMBO, com_combobox);}
这里是指各个组件,包括按钮和文本框的ID号.下面就是绑定事件:
BEGIN_MESSAGE_MAP(CSerial1AppDlg, CDialogEx)        ON_WM_SYSCOMMAND()        ON_WM_PAINT()        ON_WM_QUERYDRAGICON()ON_BN_CLICKED(ID_SEND_BTN, &CSerial1AppDlg::OnBnClickedSendBtn)ON_BN_CLICKED(ID_OPENCOM_BTN, &CSerial1AppDlg::OnBnClickedOpencomBtn)ON_BN_CLICKED(ID_CLOSECOM_BTN, &CSerial1AppDlg::OnBnClickedClosecomBtn)//ON_MESSAGE(WM_MYUPDATEDATA,OnUpdateMyData)END_MESSAGE_MAP()
下面的代码就轻松了,不过就是但鼠标点中了按钮就触发相应的绑定函数。为了方便我从点击打开串口按钮监视函数入手,代码如下:
void CSerial1AppDlg::OnBnClickedOpencomBtn(){        // TODO: 在此添加控件通知处理程序代码        /*串口的初始化*/        CString str_com=L"";        com_combobox.GetWindowTextW(str_com);        hCom=CreateFile(str_com,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);   //打开串口        if(hCom==(HANDLE)-1)        {                MessageBox(L"打开COM失败!");        }        else{                open_btn.EnableWindow(FALSE);                close_btn.EnableWindow(TRUE);                send_btn.EnableWindow(TRUE);                com_combobox.EnableWindow(FALSE);                SetupComm(hCom,100,100); //输入缓冲区和输出缓冲区的大小都是100                COMMTIMEOUTS TimeOuts;                //设定读超时                TimeOuts.ReadIntervalTimeout=MAXDWORD;                TimeOuts.ReadTotalTimeoutMultiplier=0;                TimeOuts.ReadTotalTimeoutConstant=0;                //在读一次输入缓冲区的内容后读操作就立即返回,                //而不管是否读入了要求的字符。                //设定写超时                TimeOuts.WriteTotalTimeoutMultiplier=100;                TimeOuts.WriteTotalTimeoutConstant=500;                SetCommTimeouts(hCom,&TimeOuts); //设置超时                DCB dcb;                GetCommState(hCom,&dcb); //获取串口状态                dcb.BaudRate=9600; //波特率为9600                dcb.ByteSize=8; //每个字节有8位                dcb.Parity=NOPARITY; //无奇偶校验位                dcb.StopBits=ONESTOPBIT ; //停止位                SetCommState(hCom,&dcb);  //设置串口参数                PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);   //清空缓冲区                //创建新线程读取串口数据                hThread_receiver=CreateThread(NULL,  0,  (LPTHREAD_START_ROUTINE)UpdateUIThread,  this,  0,  (unsigned long *)&ThreadID);         }}
这里设计了具体的串口属性,并且创建了一个新的线程,这个新的线程就是一直再读取串口的数据,但读取到数据的时候把数据显示在文本框里,代码如下://更新线程函数void UpdateUIThread(CSerial1AppDlg *p){        CString str=L"";        CString str_previous=L"";        while(1){                str_previous=str;                str.Append(readdata());                if(str_previous!=str){                        p->receive_edit.SetWindowTextW(str);                }                //p->SendMessage(WM_MYUPDATEDATA,FALSE);        }}

其他的按钮控制程序就不细看了,到这里就把MFC程序的架构看懂了一些了。




阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-28 23:36

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表