|
先放源码: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来反美的!
|