Windows消息分类
先讲下Windows消息的分类。Windows消息分为系统消息和用户自定义消息。Windows系统消息有三种:
1.标准Windows消息。除WM_COMMAND外以WM_开头的消息是标准消息。例如,WM_CREATE、WM_CLOSE。
2.命令消息。消息名为WM_COMMAND,消息中附带了标识符ID来区分是来自哪个菜单、工具栏按钮或加速键的消息。
3.通知消息。通知消息一般由列表框等子窗口发送给父窗口,消息名也是WM_COMMAND,其中附带了控件通知码来区分控件。
CWnd的派生类都可以接收到标准Windows消息、通知消息和命令消息。命令消息还可以由文档类等接收。
用户自定义消息是实际上就是用户定义一个宏作为消息,此宏的值应该大于等于WM_USER,然后此宏就可以跟系统消息一样使用,窗口类中可以定义它的处理函数。
添加消息处理函数
1.在类定义中加入消息处理函数的函数声明,注意要以afx_msg打头。例如MainFrm.h中WM_CREATE的消息处理函数的函数声明:afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);。
2.在类的消息映射表中添加该消息的消息映射入口项。例如WM_CREATE的消息映射入口项:ON_WM_CREATE()。
3.在类实现中添加消息处理函数的函数实现。例如,MainFrm.cpp中WM_CREATE的消息处理函数的实现:
MFC对话框
利用类添加向导
1.在编辑框上点右键,在右键菜单中选择“Add Variable”。弹出添加成员变量的向导对话框。
2.我们想为其添加值变量而不是控件变量,所以对话框中“Category”下的组合框中选择Value。
3.“Variable type”下的组合框此时默认选中的是“CString”,CString是字符串类,显然不能进行加法运算。我们可以选择double、float、int等。这里我们选择double,即编辑框关联一个double类型的变量。
4.在“Variable name”中写入自定义的变量名
对话框类的数据交换和检验
在DoDataExchange()函数中利用DDX_XXX来绑定对话框的控件程序的对象来实现数据的交换
DDX_Control(pDX, IDC_EDT_NAME, m_EDT_strName); DDX_Text(pDX, IDC_EDT_NAME, m_strName); DDX_TEXT()可以认为是把字符串变量和控件的内容关联起来; DDX_Control()可以认为是把变量和控件本身关联起来; DoDataExchange(pDX)就是处理所有变量与其关联控件交换数据的函数。 DDX_TEXT()数据交换过程: UpdateData(TRUE);//将控件的内容传回 m_strName; m_strName = _T("关联变量");//修改值 UpdateData(FALSE);//将m_strName的值显示到控件 DDX_Control()数据交换过程: CString str; m_EDT_strName.GetWindowText(str);//得到控件的内容保存到str str = _T("控件绑定变量"); m_EDT_strName.SetWindowText(str);//设置控件的内容为str
但是这种数据交换机制中,DoDataExchange()并不是被自动调用的,而是需要我们在程序中调用CDialogEx::UpdateData()函数,由UpdateData()函数再去自动调用DoDataExchange()的,如在按键消息处理函数中调用。
还有的是GetDlgItem(里面是ID标识符)是CWnd的一个成员函数,用来获取对话框中控件的指针
设置TAB顺序
在主菜单中选择“Format”->"Tab Order",或者按快捷键Ctrl+D,对话框模板上就会显示各个控件的Tab顺序数字。
模态对话框:
CAdditionDlg dlg; // 定义对话框类CAdditionDlg的对象dlg m_pMainWnd = &dlg; // 将dlg设为主窗口 INT_PTR nResponse = dlg.DoModal(); // 弹出对话框dlg,并将DoModal函数的返回值(退出时点击按钮的ID)赋值给nResponse if (nResponse == IDOK) // 判断返回值是否为OK按钮(其ID为IDOK,鸡啄米已经将它删除) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) // 判断返回值是否为Cancel按钮(其ID为IDCANCEL,鸡啄米将它的Caption改为了“退出”) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } 非模态对话框 声明一个CTipDlg *m_pTipDlg; if (NULL == m_pTipDlg) { // 创建非模态对话框实例 m_pTipDlg = new CTipDlg(); m_pTipDlg->Create(IDD_TIP_DIALOG, this); } // 显示非模态对话框 m_pTipDlg->ShowWindow(SW_SHOW);
属性页对话框相关类
我们使用属性页对话框时,用到的类主要有两个:CPropertyPage类和CPropertySheet类
创建属性页对话框资源
1.在资源插入新的对话框模板
2.为各个对话框添加对应的类,因为属性页类都应继承于CPropertyPage类,所以要修改下面“Base class”的选项,在下拉列表中选择“CPropertyPage
3.为属性页类重载OnSetActive函数呢
4.重载函数:
BOOL CSummandPage::OnSetActive() { // TODO: Add your specialized code here and/or call the base class // 获得父窗口,即属性表CPropertySheet类 CPropertySheet* psheet = (CPropertySheet*) GetParent(); // 设置属性表只有“下一步”按钮 psheet->SetWizardButtons(PSWIZB_NEXT); return CPropertyPage::OnSetActive(); } 消息对话框 MessageBox()和全局函数AfxMessageBox() 文件对话框 CFileDialog() 字体对话框 CFontDialog() 颜色对话框 CColorDialog() 静态创建控件都是在资源里直接添加,然后创建一个对应得变量对象在对话框初始化得时候设置相关得值。 菜单资源详解 创建一个新的MFC单文档工程 CMenu类菜单主要能发送两种消息:COMMAND消息和UPDATE_COMMAND_UI消息。下面分别讲解:
COMMAND消息:在菜单项被点击时发送该消息。
UPDATE_COMMAND_UI消息:用来维护菜单项的各项状态,包括激活、禁用、变灰、选中、未选中等。在下拉菜单每次打开的时候,所有菜单项的此消息都会被发送出去。如果所属类中为菜单项的该消息添加了处理函数,则执行相应函数更新菜单状态,如果菜单项没有此消息处理函数,也没有COMMAND消息的处理函数,那么它就会变灰。
工具兰的创建
1. 创建工具栏资源。
2. 构造CMFCToolBar类的对象。
3. 调用CMFCToolBar类的Create或CreateEx成员函数创建工具栏。
4. 调用LoadToolBar成员函数加载工具栏资源。
、
动态分割窗口
创建动态分割窗口的步骤为:
1. 在父框架类中定义一个CSplitterWnd类型的成员对象。
2. 重载父框架类的CFrameWnd::OnCreateClient成员函数。
3. 在重载的CFrameWnd::OnCreateClient函数中调用CSplitterWnd成员对象的Create函数。
TIMER定时器类
1、启动定时器。
启动定时器就需要使用CWnd类的成员函数SetTimer。CWnd::SetTimer()
UINT_PTR SetTimer( UINT_PTR nIDEvent, UINT nElapse, void (CALLBACK* lpfnTimer )(HWND, UINT, UINT_PTR, DWORD ) );
参数nIDEvent指定一个非零的定时器ID;参数nElapse指定间隔时间,单位为毫秒;参数lpfnTimer指定一个回调函数的地址,如果该参数为NULL,则WM_TIMER消息被发送到应用程序的消息队列,并被CWnd对象处理。如果此函数成功则返回一个新的定时器的ID,我们可以使用此ID通过KillTimer成员函数来销毁该定时器,如果函数失败则返回0。
通过SetTimer成员函数我们可以看出,处理定时事件可以有两种方式,一种是通过WM_TIMER消息的消息响应函数,一种是通过回调函数。
为WM_TIMER消息添加消息处理函数,或者定义回调函数:void CExample44Dlg::OnTimer(UINT_PTR nIDEvent)
如果调用CWnd::SetTimer函数时最后一个参数不为NULL,则需要定义回调函数。回调函数的形式如下:
C++代码 void CALLBACK EXPORT TimerProc( HWND hWnd, // handle of CWnd that called SetTimer UINT nMsg, // WM_TIMER UINT nIDEvent // timer identification DWORD dwTime // system time );3、销毁定时器。
不再使用定时器时,可以销毁它。销毁定时器需使用CWnd类的KillTimer成员函数,CWnd::KillTimer函数的原型如下:
C++代码 BOOL KillTimer(UINT_PTR nIDEvent);MFC异常类
MFC将对异常的处理封装到了异常类--CException类及其子类中。其实即使我们不使用MFC异常宏而是使用C++标准异常处理的话,也是会用到MFC的CException类及其子类的。MFC异常类及其含义如下表:
MFC异常类含义CSimpleException资源紧张异常的基类CInvalidArgException无效参数异常CMemoryException内存不足CNotSupportedException响应对不支持服务的请求CArchiveException存档/序列化异常CFileException文件异常CResourceExceptionWindows 资源分配异常COleExceptionOLE 异常CDBException数据库异常(ODBC 类)COleDispatchException调度(自动化)异常CUserException用消息框警告用户然后引发一般CException 的异常CDaoException数据库异常(DAO 类)CInternetException网络异常virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL);
lpszFileName是要打开文件的路径,可以是相对,也可以是绝对,nOpenFlags是打开的标志,有modeCreat ,modeRead等
文件的读写如下:
virtual UINT Read(void* lpBuf, UINT nCount); nCount 是要读的大小同时返回 virtual void Write(const void* lpBuf, UINT nCount);nCount是要写的大小
lpBuf 是缓冲区指针 ,
virtual ULONGLONG Seek( LONGLONG lOff, UINT nFrom );
文件指针偏移,定位文件指针的位置,若成功则返回偏移量,否则返回不定并激活一个CFlieException对象(错误对象)LOFF是偏移量,nfrom是方式有begin current end3种就是移动指针在文件内容的位置
BOOL GetStatus( CFileStatus& rStatus ) const; static BOOL PASCAL GetStatus( LPCTSTR lpszFileName, //文件路径 CFileStatus& rStatus, // 用来存放返回的文件信息结构体 CAtlTransactionManager* pTM = NULL );函数调用成功放回true。
virtual void SetFilePath(LPCTSTR lpszNewName);lpszNewName指向指定新文件的路径字符串
调用此成员函数指定文件路径。例如当构造一个CFile对象而文件的路径无效时,可调用此成员函数提供路径。(无效强行使用) 注意:SetFilePath不打开或创建文件,只将CFile对象和路径名联系起来,然后即可使用
创建一个cfile对象时
TRY { 复合语句 } CATCH (MFC异常类名, 变量名) { 复合语句 } AND_CATCH (MFC异常类名, 变量名) { 复合语句 } AND_CATCH (MFC异常类名, 变量名) { 复合语句 } ...... END_CATCH
MFC 串口通讯
在VS环境下先在系统注册控件,然后在工具栏中加入。从editor combox 等控件获取串口名字后。通过按键消息打开串口
try{
if (m_mscomm.get_PortOpen())
{
//m_mscomm.put_PortOpen(FALSE);
}
}
catch (CException* e)
{
e = NULL;
CString str;
str.Format(" No this COM %d",comIndex);
//MsgBox::str(str.GetBuffer(MAX_PATH));
return FALSE;
}
try
{
m_mscomm.put__CommPort(comIndex);//笔记本选择com
m_mscomm.put_InBufferSize(MAX_RECV_BUFF);//接收缓冲区1024byte
m_mscomm.put_OutBufferSize(1024);//发送缓冲区 1024byte
m_mscomm.put_InputLen(0);//设置当前接收区数据长度为0,表示全部读取
m_mscomm.put_InputMode(1);//以二进制方式检取数据
m_mscomm.put_RThreshold(1);//参数1表示每当串口接收缓冲区中有多于或等
//于1个字符时将引发一个接收数据的OnComm事件
m_mscomm.put_Settings(g_comInfo);//串口基本信息
}
catch (CMemoryException* e)//
{
e = NULL;
}
catch (CException* e)
{
e = NULL;
CString str;
str.Format(" Unable to open COM %d",comIndex);
//MsgBox::str(str.GetBuffer(MAX_PATH));
AfxMessageBox(str);
return FALSE;
}
try{
if (!m_mscomm.get_PortOpen())
{
m_mscomm.put_PortOpen(true);//这个是打开串口的函数
strOut.Format("\r\n * COM%d open succeeded !",comIndex);
//AfxMessageBox(strOut);
//MsgBox::str(strOut.GetBuffer(MAX_PATH));
return true;
}
else
{
m_mscomm.put_OutBufferCount(0);
strOut.Format("\r\n * COM%d open failed !",comIndex);
//MsgBox::str(strOut.GetBuffer(MAX_PATH));
return false;
}
}
catch (CException* e)
{
e = NULL;
strOut.Format("\r\n * COM%d is erro !",comIndex);
//MsgBox::str(strOut.GetBuffer(MAX_PATH));
return false;
}
return true;
}
然后是对于串口事件的处理
VariantInit(&g_variant_inp);//初始化xxxxxxxxxxx
COleSafeArray safearray_inp;
long len, k;
CString strtemp;
if (m_mscomm.get_CommEvent() == 2)//事件值为2表示接收缓冲区内有字符
{
VariantClear(&g_variant_inp); //在使用它之前 ,要清除一下xxxxxxxxxxxx
g_variant_inp = m_mscomm.get_Input();//读缓冲区
safearray_inp = g_variant_inp;//VARIANT型变量转换为ColeSafeArray型变量
VariantClear(&g_variant_inp); //使用后要清除它xxxxxxxxxxxxxxxxxxxxxxx
len = safearray_inp.GetOneDimSize();//得到有效数据长度
if (len>MAX_RECV_BUFF)
{
AfxMessageBox("Recv Data too long");
len = MAX_RECV_BUFF;
}
for (k = 0; k<len; k++)
{
safearray_inp.GetElement(&k, g_rxdata + k);//转换为BYTE型数组
}
//g_recvTxt = "";clear it before fill data
for (k = 0; k<len; k++)//将数组转换为Cstring型变量
{
strtemp.Format(TEXT("%c"), *(g_rxdata + k));//字符型
g_recvTxt += strtemp;
}
g_comRecvNum += len;
CString txt;
txt.Format("Recv:%d / Len=%d", g_comRecvNum, len);
SetDlgItemText(IDC_RecvNum, txt);
g_readTmOutCnt = 0;
g_uart_flag_rx = true;
UpdateData