首页 手把手教你学VC经典教程

手把手教你学VC经典教程

举报
开通vip

手把手教你学VC经典教程手把手教你学VC经典教程 从零开始学VC系列教程 从零开始学VC系列教程 一.信息显示实 验..........................................................................1 从零开始学VC系列教程 二. 对话框及常用控件实 验........................................................8 从零开始学VC系列教程之 三.串口通信与自定义消 息.........................

手把手教你学VC经典教程
手把手教你学VC经典教程 从零开始学VC系列教程 从零开始学VC系列教程 一.信息显示实 验..........................................................................1 从零开始学VC系列教程 二. 对话框及常用控件实 验........................................................8 从零开始学VC系列教程之 三.串口通信与自定义消 息....................................................21 从零开始学VC系列教程之 四.并口控制与类的使 用........................................................34 从零开始学VC系列教程 五.消息机制与自定义消 息.........................................................45 从零开始学VC系列教程 一.信息显示实验 题外话:第一次写教程,不知道该怎么开始.以前见过的教程可能都会介绍比较多的理论 然后才开始讲实际操作,我想对于VC,理论讲得太多大家可能更糊涂,所以选择了这个 方案 气瓶 现场处置方案 .pdf气瓶 现场处置方案 .doc见习基地管理方案.doc关于群访事件的化解方案建筑工地扬尘治理专项方案下载 ,我 们先一步步照着做,每一次只介绍一两个知识点,并且把理论放在后面说明,希望大家能喜欢 这种方式,并对这种方式提出意见和建议,以便以后章节更适合大家的学习. 或许我写的还不能称之为教程,只是一些操作步骤及说明,通过这些练习,希望能使大家 对VC有一个新的认识.VC的功能是十分强大的,但我们一般并不会用到所有的功能,就像大 家熟悉的KeilC一样,其实Keil功能也是很多的,平时也没见谁把Keil的功能都用到了.基于 此,我写的这个教程可能在VC专业人士看来比较简单.不是要班门弄斧,而是希望推行上位机 技术,形成一个氛围让大家来学习交流,PC机与下位机结合毕竟也是发展的一个方向.望高手 谅解小辈的不自量力.同时,我们结合单片机编程,让大家明白上位机控制 方法 快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载 及原理,当然这 是今后章节的内容了. 本节内容:学会在文本框中显示文本,学会弹出式对话框使用. 学习目的:信息显示是人机交互的基础,同时,信息显示也为以后的程序调试打下基础. 1. 新建工程.打开VC6.0点击[文件]->[新建],弹出如下对话框. 在工程选项中选择MFC AppWizard[exe],选择好工程存入的位置,然后在工程名称中填入工 程的名称,例如Eg01,填入Eg01后.VC会在我们选择的路径后自动加入一个以工程名命名的 文件夹作为工程目录.完成按[确定]就会进入工程向导, 出现以下的提示. 在这个我们选择[基于对话框]就行了.单文档及多文档方式会在以后详细介绍.这里也简要说 明一下.?单文档.程序一次只能处理一个文档对象(文件).像写字 .?多文档. 板,记事本等 程序一次可以处理多个对象(文件),像word之类的,可以同时打开多个文件.?基本对话框. 程序一般用于处理一些比较小的,工具型的软件.像双龙的ISP下载软件, Easy 51Pro等都是 基于对话框.一些不是很复杂的工业软件也通常可以用基于对话框的形式,这种方式的优点 是所见即所得,一些控件可以像VB一样拖到工作区就行了.选好以后直接按[完成]就可以了. 至于[下一步]的向导,这里我们先不管.然后会进入以下的画面.其中红色及绿色是我的标注, 看不清图片可以下载图片放大. 选中静态文本框[TODO:在这里设置对话框控制],单击指标右键,在弹出的快捷菜单中选择 [属性Properties]菜单项,弹出如下对话框 修改IDC_STATIC为IDC_FIRSTLABEL,标题可以写为”第一个应用程序”然后关闭这个对话 框,就可以看到刚才的静态文本框内容变为”第一个应用程序”了. 在刚才的操作中,我们将静态文本框的ID改为了IDC_FIRSTLABEL,在VC中,ID是控件的标 志,不能有相同的ID出现,但允许多个名为IDC_STATIC的ID号.所以,一般并不把文件名命 为IDC_STATIC,除非以后我们不想在程序中改变这个控件的属性.有一些控制,例如 GroupBox一般并不在程序中改变什么,所以不改变其ID号.ID是程序访问控件的标识,所以 一个应用程序中不能有重复ID. 按下来我们要添加一个按钮. 用鼠标将控件条上的按钮拖到对话框上,可以看到对话框上多了一个按钮. 用上叙方法,改变 按钮ID为IDC_BTN1,标题改为”显示”,这样就做好一个按钮了.下面我们为按钮添加代码. 双击按钮,会出现以下的提示框. 按[OK]为按钮添加响应函数,出面以下画面 void CEg01Dlg::OnBtn1() { // TODO: Add your control notification handler code here } 这一段就是刚才按钮的函数了.当然,现在还没有响应.现在我们添加一个事件,让这个按钮按 下后上面的静态文本框的内容变为”串口号:COM1 波特率:57600”把上面的代码改为 void CEg01Dlg::OnBtn1() { // TODO: Add your control notification handler code here :COM1 波特率:57600”); SetDlgItemText(IDC_FIRSTLABEL,” 串口号 } 添加完成以后,按F7编译.如果编译通过,按F5运行,可以看到程序运行后的对话框 按下按钮,可以看到上面的静态文本框的内容改变了.那么恭喜你,完成我们这个例子的第一 步操作了. 当然,我这里的对话框看起来没那么高,大家可以调一下高度.怎么退回到控件编辑状态呢? 首先,我们单击选项卡中的[ResorceView],现在大家只能看到[Reso,]看到IDD_EG01_DIALG 了吧,这个其实也是一个ID,是我们对话框的ID,如果大家改一下对话框的ID,就可以看到这里 的ID也变了.在VC中,所有的控件都认为是窗体,只是形式不同.双击IDD_EG01_DIALG就 可以看到我们刚才的控制编辑状态了.双击[显示]按钮又可以回到代码状态. 刚才的例子很简单,却是我们以后会用得最多的.下面我们来分析一下代码 void CEg01Dlg::OnBtn1() { // TODO: Add your control notification handler code here :COM1 波特率:57600”); SetDlgItemText (IDC_FIRSTLABEL,” 串口号 } 看看SetDlgItemText这个函数,大家就会明白了, IDC_FIRSTLABEL是我们给静态文本框分 配的ID,后面的参数是要显示的内容.上面我们提到过,VC中的控件都认为是窗体,所以,对于 所有的文本显示,我们都可以用这个函数来做,大家可以试着加入一个编辑框(Edit),同样可以 使用用这个函数,只要ID号对了就可以了.VC对大小写敏感,大家要注意大小写. 下面我们用另一个方法来实现. 把代码改为 void CEg01Dlg::OnBtn1() { // TODO: Add your control notification handler code here CString a; a=” 串口号:COM1 波特率:57600”; SetDlgItemText (IDC_FIRSTLABEL,a); } 然后按F7编译,无误则按F5运行.可以看到运行结果是一样的. 大家可以看到,我们把变量a定义为CString类型,这个类型我们在C中可能没有见过.CString 其实是VC中的一个类.这里我们要引入类的概念了.类跟C语言结构体很像,结构体可以有成 员变量,但不能有成员函数,类不但可以有成员变量,还可以有成员函数,并且支持多种方法.在 VC中,类用class标识.下面是一个类的定义,我们来分析一下. class CEg01Dlg : public CDialog { public: CString GetPath(void); protected: int GetMax(int a,int b); public: int a; int b; }; 这个类是我改过的,为了大家看得明白一点.首先,我们从这句class CEg01Dlg : public CDialog 知道这个类名是CEg01Dlg,那么public后面的CDialog是什么意思呢?CDialog其实也是一个 类,是VC中 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 对话框类. public CDialog意思是我们定义的这个类CEg01Dlg从CDialog派 生,也叫从CDialog继承,从继承这个词语大家就会明白CEg01Dlg不但可以有自身函数,而且 还可以用到CDialog里的变量及函数,所以说是继承,就像我们说的继承财产一样,儿子不但可 以有自己的钱,还有从老爸那里继承的产财,这些钱都能花.所以,我们也叫CDialog为父类(不 是爸类啊),把CEg01Dlg称为子类.当然,这是相对的.如果再定义一个类从CEg01Dlg派生,那 么CEg01Dlg也就是父类,派生出来的类就是子类.这段可能有点难明白,大家可以慢慢体会. 我们再回到CString上面来,这个类不是派生的,所以也没有父类了.用CString定义一个变量 a,我们称为类变量,CString有很强大的功能,我们这里只用到一个功能,就是字符串,a可以是一 个不定长度的字符串,所以我们可以给a赋一个任意的字符串而不用关心长度.当然,我们也可 以用int StringLen=a.GetLength();来获得这个字符串的长度.从这句可以看到,GetLength()是类 CString里的一个函数.大家可能又会关心一个问题,怎么把长度显示出来呢?我们把代码改为 void CEg01Dlg::OnBtn1() { // TODO: Add your control notification handler code here CString a; :57600"; a=" 串口号:COM1 波特率 SetDlgItemText (IDC_FIRSTLABEL,a); a.Format("字符串的长度:%d",a.GetLength()); MessageBox(a); } 运行一下,效果如下 这里我们用到了类Cstring的另一个成员函数Format();这个函数用起来跟C语言里的Printf() 很像,我就不多说明了.格式化后的字符串还是存放在a中,在这里,我们还用到了一个新的函 数MessageBox();我们只是用了这个函数最简单的用法,显示一个字符串.MessageBox用于弹 出一个提示对话框.查查MSDN就知道,函数原型是 int MessageBox( LPCTSTR lpszText, LPCTSTR lpszCaption = NULL, UINT nType = MB_OK ); 在VC里面,函数可以有默认值,例如LPCTSTR lpszCaption = NULL, UINT nType = MB_OK变 量lpszCaption的默认值是NULL, nType的默认值是MB_OK,这些有默认值的变量我们可以 不传值,所以这个函数使用时,最简单的用法就是传lpszText就可以了.如果我们要显示一个固 定的字串,可以这样调用MessageBox(“大家好”);是不是很简单啊? 这一章就写到这里了,一些问题大家可以提.未尽知识点,会在以后再详细介绍. 从零开始学VC系列教程 二. 对话框及常用控件实验 恭喜你,进入VC学习的第二节了.这一节是人机交互的基础.所谓人机交互,说通俗点就 是与机器对话.然而我们现在的技术还不能像科幻片里一样与机器人直接说话就行了.所以, 我们的操作意图还得通过文本输入,命令按钮等等来实现. 本节内容:学会对话框调用及一些常用控件的使用方法. 学习目的:学习人机交互,为软件开发提供界面基础. 1. 新建工程.参考第一节的方法新建一个工程,名字为Eg02完成后如下图 细心的朋友一定会发现.新建的工程里还有一个对话框,ID名是IDD_ABOUTBOX这个是做 什么用的呢?我们用到的软件都会有一个版权声明.通过第一章的学习,大家应该知道怎么观 看这个IDD_ABOUTBOX对话框了吧.没错!双击IDD_ABOUTBOX就可以了.我们会看到如 下的一个对话框 这就是我们这个程序的关于对话框,一般用于版权声明及版本号标识.大家看到的这个对话框 里有两个静态文本框和一个图像框(Picture),静态文本框我们在前一节已经介绍过了.大家可 以修改一下版权所有这一行,填什么都可以,签个大名也行.完成以后你一定想看看效果,这个 对话框怎么打开呢?其实VC已经为我们做好了.先按F7编译,然后按F5运行.大家可以看到 程序运行了. 单击应用程序图标,就会出现一个菜单,选最后一个[关于Eg02],关于对话框就弹出来了. 当然,这是系统为我们做好的.自己怎么在程序中调用这个对话框呢?为了演示,首先参考第一 节的内容添加一个按钮,然后把按钮的ID改为IDC_BTN_ABOUTME,把标题,也就是Caption 改为[关于].最终效果如下 下面我们为按钮添加代码.相信大家一定还记得怎么进入代码吧..对了,双击[关于]按钮,在弹 出的对话框中点[确定]就可以了.为了让大家更好的理解下面的操作,我们先要解释一下关于 对话框的类.VC向导会为关于对话框建立一个类,大家看看下面的图 单击标签ClassView(这里显示的是[Class,])就可以看到Eg02这个应用程序的类.第一个 CAboutDlg就是关于对话框的类.CEg02Dlg对应IDD_EG02_DIALOG.中间的Ceg02App是应 用程序的基础类.所以,如果要对关于对话框进行操作,就要用到类CAboutDlg,因为与此有关 的函数及变量都封装在CAboutDlg中.看到这里大家可能又糊涂了,没关系,在以后的教程中, 通过一些练习大家就会慢慢领会到的.这里还是先为[关于]按钮添加代码. void CEg02Dlg::OnBtnAboutme() { // TODO: Add your control notification handler code here } [关于]按钮添加的响应函数.我们添加代码成以下所示 上面是VC为 void CEg02Dlg::OnBtnAboutme() { // TODO: Add your control notification handler code here CAboutDlg ADlg; ADlg.DoModal(); } 一共有两句,第一句是CAboutDlg ADlg;作用是定义一个变量Adlg.第二句是 ADlg.DoModal(); 功能是调用类CAboutDlg里的一个函数DoModal();这个函数在MSDN里的解 释是Call this member function to invoke the modal dialog box and return the dialog-box result when done. This member function handles all interaction with the user while the dialog box is active. This is what makes the dialog box modal; that is, the user cannot interact with other windows until the dialog box is closed.一般我们用于显示一个对话框.其实大家看看CAboutDlg这个类下 面,只有两个 函数 DoModal()这个函数并不在这个里面.第一章我们提到过类的派生和继承.其实 CAboutDlg这 个类是派生于CDialog类,DoModal()这个函数是CDialog的成员函数,由于 CAboutDlg是继承 父类CDialog的,所以CDialog里的函数在CAboutDlg中也可 以使用. 下面我们来说说几个常用控件的使用. 首先在IDD_EG02_DIALOG对话框中加入一个Edit(编辑框)控件.Edit一般用于 输入输出数 据文本.相当于VB里的TextBox.加入Edit控件后,编辑其属性为 然后,我们再加一个按钮(PushButton),并编辑其属性为 接下来,我们先说一下要实现的效果.很简单,在编辑框里输入一个文本,然后按显示,就把文本 显示在静态文本框中.所以,这里要把静态文本编辑框的ID改为IDC_DISPLABEL 下面我们为[显示]按钮添加代码 void CEg02Dlg::OnBtnShow() { // TODO: Add your control notification handler code here CString a; GetDlgItemText(IDC_EDIT_INPUT,a); SetDlgItemText(IDC_DISPLABEL,a); } 其实不复杂,也只有三句,第一句定义一个CString类变量a 我们来说说GetDlgItemText这个 函数吧.查查MSDN就知道函数原型了. int GetDlgItemText( int nID, LPTSTR lpStr, int nMaxCount ) const; int GetDlgItemText( int nID, CString& rString ) const; 大家看看就觉得奇怪了,怎么有两个原型啊?并且一个是传两个参数,另一个是传三个参数.在 VC里面,同一个类下是可以存在多个同名函数的,具体调用哪个函数要看参数的不同.在这里 我们传入了两个参数,所以VC会调用int GetDlgItemText( int nID, CString& rString ) const; 这个函数.第一个函数是控件的ID号,第二个是字串.第二个传了地址,所以我们在下一句中用 的a已经是获得IDC_EDIT_INPUT的文本了.运行效果如下 下面介绍一下进度条的使用以及定时器的使用. 我们要实现的效果是进度从0到满格,然后再从0到满格,依次循环.每跳一格间隔500ms,这个 时间我们用定时器来实现. 首先从控件条里拖出一个进程条到对话框,修改属性如下 然后我们要介绍一下VC的定时器.VC里面使用定时器有多种方式,我们先介绍 一种作为抛 砖引玉 首先添加一个Windows消息处理器.消息这个词语可能很陌生,我们会在后面很多次说明.这 里先照图做 在类管理器里选中Ceg02Dlg这个类,然后点右键,就会弹出一个菜单,选择[Add Windows Message Handler,],接下来会弹出另一个菜单 双击WM_TIMER然后按[确定]就可以了.大家会看到,CEg02Dlg类中多了一个函数 这个就是VC中的定时器响应函数.然后双击这个函数就进入代码了 然后我们为Timer事件添加代码. void CEg02Dlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default IDC_PROGRESS static int nPos=0; ((CProgressCtrl*)(GetDlgItem(IDC_PROGRESS)))->SetPos(nPos); if(nPos<100) nPos+=10; else nPos=0; CDialog::OnTimer(nIDEvent); } 首先定义一个整型的变量nPos用于 记录 混凝土 养护记录下载土方回填监理旁站记录免费下载集备记录下载集备记录下载集备记录下载 进度条的进度值.默认时,进度条0为空,100为满格. 从后的程序大家可以看到,这个变量自加到100就变为0.最难理解的就是 ((CProgressCtrl*)(GetDlgItem(IDC_PROGRESS)))->SetPos(nPos); 首先, GetDlgItem(IDC_PROGRESS)这个函数用来获取IDC_PROGRESS的句柄,在VC里面 引入了句柄这个词语,我们将在下一章中对消息和句柄进行详细的说明, 句柄是WINDOWS 用来标识被应用程序所建立或使用的对象的唯一整数,WINDOWS使用各种各样的句柄标 识诸如应用程序实例,窗口,控制,位图,GDI对象等等。WINDOWS句柄有点象C语言 中的文件句柄。在这里,GetDlgItem获取了窗体句柄,大家应该还记得,在VC里面,控制 就看成窗体。(CProgressCtrl*)这个地方是将返回的句柄强制转换为CProgressCtrl类型,与C 语言的中的强制转换是一样的.进度条的控制类是CprogressCtrl.而前面返回的是一个窗体类 型,所以先要强制转换.在第一章中,我们提到过,VC中的控件都认为是窗体,在这里就体现出 来了. SetPos(nPos);这个函数是类CprogressCtrl的成员函数,用来指定当前进度条的进度.最后 还有一步,就是激活这个定时器.像我们的C51或AVR一样,要初始化定时器. 而void CEg02Dlg::OnTimer(UINT nIDEvent)这个函数就像我们单片机的定时器中断服务函数一样. 时间到了就会自动执行. 参考上面的图,双击OnInitDialog(),就可以进入对话框初始化函数,只要添加一句就可以了.完 成后如下 BOOL CEg02Dlg::OnInitDialog() { CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application?s main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here SetTimer(0,500,NULL); //这里是添加的,别的都是自动生成的 return TRUE; // return TRUE unless you set the focus to a control } 在这里我们只添加SetTimer(0,500,NULL);其中,参数0代表定时器的ID号为0,.第二个 参数500是定时器的时间,单位为ms,后面的NULL是指不要回调函数. 按F7编译后运行就可以看到运行效果了. 或许大家有个问题,为什么开始时是添加了一个WM_TIMER的消息处理器呢?我们使用 SetTimer开始定时器后,如果没有回调函数,系统会在每次定时时间到后发送一个 WM_TIMER消息到窗体.窗体收到这个信息后,调用OnTimer()函数进行处理.系统定义这个 函数为afx_msg void OnTimer(UINT nIDEvent);可以看到是以afx_msg修饰的,这种函数会与 其中一个信息关联,或者说是消息影射到这个函数.每当有消息发过来,都会执行这个函数.大 家只要好好想想单片机的定时器中断就会明白的,原理一样,只是传输机制不同. 如果上面的内容你制作成功了,那么恭喜你,第二节就基本学完了. 下面我们也来说说多任务和消息机制吧. Windows是基于消息机制的,它是一个多任务的操作系统,也就是说,同一时间内,系统会挂起 多个任务.为了说明多任务,我们先来看一段单片机程序. Void main(void) { While(1) { TaskA(); } } 这个程序很简单,单片机工作后就进入while()循环了,单片机这个时候就干一件 是执行任务TaskA.这样的工作总是在一个主循环内实施,一次只执行一事,那就 个任务的我们称为单 任务系统.单片机程序只要不引入操作系统并且由一个主循环一直执行完毕的基本都是单任 务的.有些程序也是这样写的. Void main(void) { While(1) { TaskA(); TaskB(); TaskC(); } } 这样的程序看起来似乎是有三个任务了.这三个任务是顺序执行的,也就是说,必须让A 完成后,才能到B,B完成后才能到C.如果A有一个长时间的延时,系统就会在A 中空等,然而 B与A本来是无关的,这样空等的时间就算是浪费了.如果我们的windows也是顺序执行的就 麻烦了,那时我们不仅仅会说Windows有点慢,而是说Windows像蜗牛一样在爬.如果我们让 这些任务更合理的安排一下,在执行A的时候,有空就去执行B,而在B的空闲时间去执行C 或者A,那么时间就节省下 .所以,如来了.如果时间切换够快,那么我们可以认为A,B,C三个任务在同时 进行果我们把单片机的执行时间分成若干等份,每份1ms或者更小,这种时间等份我 们称为时间片,每次时间到了就换一个任务.也就是说,第一毫秒执行A,这时我们并不等A全 部执行完,并记住这个断点,到了第二毫秒执行B,第三毫秒执行C,第四毫秒又执行A并从原 来的断点开始执行,依次直至三个任务都完成.大家可以看到,任务A每三毫秒执行了一次.这 就是多任务的模型了. Windows其实就是这么干的.只是每次任务时间不一定是我们上面说的3ms,因为系统同 一时间内可能会有很多待执行的任务,这些任务就被系统按优先级排成队,一个个取出来执行. 比如我们现在打开VC,系统在加载VC,同时我们还可以移动鼠标,这些好像都是在同时进行, 其实它们也是按时间片分时执行的. 现在分析一下这个工作过程吧.现在我们要打开VC,系统开始加载VC了,可以看到VC 的LOGO界面,但这个时候我们还可以动一下鼠标,硬件首先会响应,并给软件发个通知,而软 件这个时候可能还没有轮到鼠标程序的执行,怎么办呢?系统会先把这个鼠标操作保存起来, 轮到鼠标时间片时再来处理鼠标操作.大家可能觉得,这样鼠标操作不是滞后了吗?确实是这 样的,但这个滞后时间是很短的,我们基本不会察觉出来. 我们来看看消息的定义. 消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。一个消息, 是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉 应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使 Windows发送一个消息给应用程序。 消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信 息。例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。 综合前面的分析,消息就好理解了,鼠标动一下会产生一个事件,这个事件必须要告诉系 统啊,怎么告诉系统呢?Windows把这个定义为Message也就是消息,所以,消息可以认为是一 个操作记号,记录上一步所发生的或者说是下一步所要处理的事件.系统为消息安排了一个队 列,里面可以存放很多个消息,我们称为消息队列.有键盘操作时,把键盘操作放到消息队列里 面,相当于做一个记号,我们动了键盘,有鼠标操作时,把鼠标操作放到消息队列,也便记住我们 在键盘后还动了鼠标,系统就把这些东西一个个按时间取出来执行.在Windows 里面,鼠标消 息可以有几种,例如单击落了左键,系统会产生一个消息为WM_LBUTTONDOWN如果我们 移动了鼠标,就像产生一个消息为WM_MOUSEMOVE消息入队后,系统会依次处理.当然,我 们这样的解释虽然容易懂,但不是非常专业,并且Windows内部处理消息也比这复杂多了,但 基本原理就是这样的了,并且,我们在进行操作时,并不用关心Windows的底层是怎么处理这 些事件的,除非我们的操作真的让Windows生气了,就必须去查查出什么问题了. 说了这么多,消息可能还是一个十分飘渺的概念,倒底要怎么用呢?我们来举个例子说明 一下.给系统发消息一般会用到两个函数,一个是SendMessage,另一个是PostMessage,这两个 是有点区别的,具体区别作为这次的作品,大家去网上查查.例如我们要退出当前的应用程序, 最简单的方法当然是按一下关闭的那个红X,如果我们要自己让程序退出,可以给系统发一个 消息,其实那个关闭程序的红X也是这么干的.为了演示,首先在上次的对话框中加一个按钮, 修改标题也就是Caption为”关闭程序”,ID号改为IDC_QUIT_ME双击为按钮添加以下代码. PostMessage(WM_CLOSE); 编译运行试试,如果没有写错代码就可以用那个按钮退出程序了. 这一节也就写到这里了,可以大家还是有点糊涂,慢慢体会一下就好了.还是不明白可以 来这里提问. 从零开始学VC系列教程之 三.串口通信与自定义消息 课程之前:首先请大家确认一下前面两章都已经熟悉,因为一些前面已经介绍过的基础操作在 这里将不再详细说明,如果有什么问题,可以翻看一下前面的两章或者留言提问.本章是基于 PC机与单片机的串口通信,用到了一个动态链接库和一个自定义消息. 学习目标:掌握VC下串口编程式的方法,掌握动态库的静态调用及自定义消息. 课程详解: 1. 参照第一章新建一个基于对话框的Vc工程,名称定义为Eg03. 2. 工程建立后,在对话框上加入一个组合框(ComboBox),ID号改为IDC_COMPORT 用于选 择使用PC机上的哪一个串口. 在组合框后加入一个按钮,标题(Caption)改为”打开”,ID号改为IDC_BTN_PORTOPEN 用 于打开串口,开始通信. 下面加入一个编程框(EDIT),ID号改为IDC_EDIT_RECMSG 用于显示接收到的数据. 在编程框下面再添加一个编程框(EDIT),.ID号改为IDC_EDIT_SEDMSG 用于添加要发 送的数据. 然后在这个编程框后加入一个 按钮.标题(Caption)为”发送”,ID号为 IDC_BTN_SEND 最后调整位置及大小如下 3. 添加Lib文件.这里介绍的串口通信用的不是VC自带的MSCOMM控件.原因有两个,一 是顺便介绍一下动态库和自定义消息的用法.二是MSCOMM控件使用时数据类型转换 比较复杂,并且使用也不是很方便.当然,以后也会介绍多线程串口通信给大家,我们会在 后面开设一章多线程编程方法,并在那里详细介绍基于多线程的串口通信.这里使用一个 动态库,其实也是别人封装好了的多线程通信,名字是Pcomm.在工程下载中,给出了三个 文件,分别是Pcomm.h, Pcomm.lib, Pcomm.dll,现在请大家把这三个文件拷到工程目录,也 就是Eg03这个文件夹中.至于什么是动态库,这三个文件倒底是什么作用,我们做完这个 例程后再解释,现在还是先按步就搬,营造一个感性认识.下面添加Lib文件到工程.首先点 击[工程](Project),选择下拉式菜单中的[设置](ProjectSettings) 然后会弹出一个对话框,在标签卡中选择[连接] 然后在[对象/模块]中添加Pcomm.lib,完成后如上面所示,单击[确定]退出.这样,我们就为 Pcomm.dll这个动态库添加了静态链接,同时,这也就是动态库的静态链接方法,当然,还有一步 就是包含Pcomm.h这个头文件.在刚才的步骤中,我们将Pcomm.Lib添加到工程,这个文件主 要用于指定Pcomm.dll中各个功能函数的入口及地址,Pcomm.Lib就像一个地图指出目的地 的路标,而真正的函数是在Pcomm.Dll中的.当然,为了方便调用,我们还要得到Pcomm.Dll中 的函数声明,这些函数声明就在Pcomm.h这个头文件中,所以,大家打开Pcomm.h这个文件,只 有函数及变量定义,并没有函数过程.下面我们来添加这个文件. 4. 打开左边的[工作空间](WorkSpace)中选择标签[ClassView](这里大家只能看到 [Class,] 这一步前两章已经详细介绍过了,大家可以参考.),然后双击[OnInitDialog]就可以打开代 码窗口了,在原有头文件包含后面加入串口头文件引用.输入#include ”Pcomm.h”就可以 了,完成后如下图 这一步我们加入了动态库的函数声明,后面就可以直接使用Pcomm.Dll中的函数了.下面我们 来添加事件响应. 单击工作空间中间的标签[ResourceView](大家看到的是[Reso,]),再双击 [IDD_EG03_DIALOG]就可以回到控件编辑状态. 首先为[打开]按钮添加代码.双击[打开]按钮,然后在按钮事件中添加.完成后如下 void CEg03Dlg::OnBtnPortopen() { // TODO: Add your control notification handler code here Port=GetDlgItemInt(IDC_COMPORT); if(SIO_OK!=sio_open(Port)) { MessageBox("串口打开错误"); } else { sio_ioctl(Port,BaudRate,DataBits | StopBits | Parity); sio_cnt_irq(Port,CntIrq,1); } } 其中, sio开头的变量及函数都是Pcomm中的,我们来解释一下. sio_open是打开某个串口,传 入的参数是串口号,如果我们要打开COM1,可以用sio_open(1),返回的参数在Pcomm里面定 义了,如果返回SIO_OK就表示串口打开没有问题,否则,就是打开串口失败. sio_ioctl用于设 置通信的相关信息,Port中串口号, BaudRate是波特率, DataBits是数据位数, StopBits是停止 位数, Parity是校验. sio_cnt_irq用于设定中断回调函数.中断回调函数其实前面 的Timer定时 器里也提到过,在这里,我们设定一个中断回调函数,每当串口接收到指定字节数据时,系统就 会自动调用这个中断回调函数,就像单片机中的串口中断函数一样.这里的回调函数名是 CntIrq,我们将在后面定义.细心的朋友一定会发现, BaudRate,DataBits | StopBits | Parity这些 都没定义过啊?所以要定义一下这些变量.参照前面的加入文件的方法,在头文件引用下一行 加入以下宏定义 #define BaudRate B57600 //波特率 #define DataBits BIT_8 //数据位 #define Parity P_NONE //效验位 #define StopBits STOP_1 //停止位 完成后如图 下面我们要定义sio_cnt_irq 一般来说,中断回调函数并不写在类里面,我们添加后如下 ///////////////////////////串口中断回调函数////////////////////////////////// VOID CALLBACK CntIrq(int port) { if(::AfxGetMainWnd()) { if(::AfxGetMainWnd()->m_hWnd) { ::PostMessage(::AfxGetMainWnd()->m_hWnd,WM_PCOMM,0,0); } } } 学习过前面两章我们知道,这个中断回调函数只做了一件事情,就是发送一个WM_PCOMM 消息到窗口. AfxGetMainWnd()这个函数用于获得主窗口,返回类型是CWnd的指针,主窗体句 柄我们是不知道的,用AfxGetMainWnd()->m_hWnd来获得.这样,消息就可到发到主窗体了. 有了这个函数,每当串口接收了数据,就会发一个消息到窗体.WM_PCOMM这个消息不是系 统的,也不是Pcomm本身的,它是我们自定义的一个消息,怎么定义呢?我们在前面说的宏定义 后面再加入一个定义 #define WM_PCOMM WM_USER+500 //自定义消息 WM_USER是一个消息地址,这个是系统定义好的,从这个地址开始可以自定义消息, 我们把 WM_PCOMM定义为WM_USER+500也就是说,我们定义的这个消息位于WM_USER后面 的偏移500,当然,这只是个地址,与执行先后无关.这个偏移大家可以自己随便设,不与别的自 定义消息冲突就行了.消息定义好了还要为消息添加关联.首先要定义一个消息响应函数,名 字随便,我们这里取名为OnPcomm(),双击[工作空间]中的Ceg03Dlg就可以打开窗体的文头 件,这里定义了Ceg03Dlg 这个类,我们在类定义里面添加一个成员函数. afx_msg void OnPcomm(); //这里是我们自定义的消息响应函数 完成后如图 此外,这里还顺便定义了一个变量,就是前面我们用到的Port 用于记录打开的串口号. public: int Port; 位置就放在DECLARE_MESSAGE_MAP() 的前面.函数声明就可以了.现在来添加函数体.双 击[OnInitDialog( )],然后在该文件的最后添加一个函数.写成如下形式. void CEg03Dlg::OnPcomm() { char buf[200]; int end=sio_read(Port,buf,100); if(end) { CString a,b=""; GetDlgItemText(IDC_EDIT_RECMSG,b); buf[end]=0; for(int i=0;i<end;i++) { a.Format("%X ",(unsigned char)buf[i]); b+=a; } SetDlgItemText(IDC_EDIT_RECMSG,b); } } 这一段其实不难理解,因为前面两章已经介绍过多次了. sio_read是Pcomm的函数,从串口读 取数据用. GetDlgItemText(IDC_EDIT_RECMSG,b);用于读出以前的历史记录,这样每次发上 来的数据都放在后面连接起来.end是返回的收到的数据个数.用十六进制形式显示出来. 做完了上面一些,我们差一步就可以收到数据了.因为数据发上来后,底层响应,并调用了回调 函数,在回调函数里面,发出一个消息WM_PCOMM 虽然我们在后面定义了一个Pcomm()函 数专门用于响应这个消息,但这个自定义消息并不是自动连接到Pcomm()的,需要添加一个消 息影射才能使WM_PCOMM消息影射到Pcomm()函数.双击左边[工作空间](WorkSpace)中的 DoDataExchange(CDataExchange* pDX) DoDataExchange这个函数的下面一般都这是用于定义消息影射的,将下面一段程序增加一行, 完成后如下. BEGIN_MESSAGE_MAP(CEg03Dlg, CDialog) //{{AFX_MSG_MAP(CEg03Dlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BTN_PORTOPEN, OnBtnPortopen) //}}AFX_MSG_MAP ON_MESSAGE(WM_PCOMM,OnPcomm) //这里是消息影射 END_MESSAGE_MAP() 完成以后就可以按F7编译一下,如果无误就可以接收到数据了.运行后,选择正确的串口号,按 一下[打开]按钮就可以了. 现在我们再来看看怎么发送数据 回到控件编辑状态,双击[发送]按钮,为该按钮添加代码. void CEg03Dlg::OnBtnSend() { // TODO: Add your control notification handler code here CString a; unsigned char b=0; GetDlgItemText(IDC_EDIT_SEDMSG,a); //取得编辑框内所有文本 a.MakeUpper();//全部转换为大写 for(unsigned char i=0;i<a.GetLength();i+=3) { if(a.GetAt(i)>=„A? && a.GetAt(i)<=„Z?) b=(a.GetAt(i)-55)*16; //判 断填入的是字母还 是数字,并把字符转换成十六进制数 else if(a.GetAt(i)>=„0? && a.GetAt(i)<=„9?) b=(a.GetAt(i)-0x30)*16; if(a.GetAt(i+1)>=„A? && a.GetAt(i+1)<=„Z?) b+=(a.GetAt(i+1)-55); else if(a.GetAt(i+1)>=„0? && a.GetAt(i+1)<=„9?) b+=(a.GetAt(i+1)-0x30); sio_putch(Port,b); //发送 } } 这一段主要是把获得的编辑框内的字串转换成十六进制的数字,转换一个发送一个.Cstring 类型以前已经提起来,应该际上是一个类, MakeUpper是一个成员函数,用于将字符串全部转 成大写.GetAt也是一个成员函数,可以取出字符串中任意下标的字符. sio_putch用于发送一 个字符. 填写待发送数据的时候要注意,每两位中间空格一下.填入的是十六进制数据. 下面再来 总结 初级经济法重点总结下载党员个人总结TXt高中句型全总结.doc高中句型全总结.doc理论力学知识点总结pdf 一下静态方式调用动态库的方法. 1. 拷贝Lib,H头文件到工程路径 2. 在工程->设置中加入Lib模块. 3. 加入.h头文件,用于函数声明 4. 将Dll文件拷入到工程目标路径中 总结一下自定义消息方法: 1. 用#define WM_NAME WM_USER+1 定义一个自定义消息,名称随便.一般用WM开 头.WM_USER+1中的1那个数字是自己定的,一个消息就无所谓了,喜欢多少都行,如果要 定义很多个消息,不要冲突就行了 2. 在类定义里面声明一个消息响应函数,写成 afx_msg void FunctionName();格 式. 3. 添加一个消息影射ON_MESSAGE(WM_NAME, FunctionName)注意这句后面是没有分 号的. 4. 写好FunctionName的函数. 很简单的四步就行了. .并口控制与类的使用 从零开始学VC系列教程之 四 课程之前:这一章让大家等很久了,一直在考虑并口操作应该怎么写.其实这是一个很简单的 课题,因为是基于成熟操作类的基础上的,我们主要是学习如果使用网上查到的资料,这一点 很关键,只有学会了利用各种资源,才能自主学习取得进步,同时,也可以有效的提高开发效率. 学习目标:掌握VC下并口程序的方法及类的使用. 课程详解: 参照第一章新建一个基于对话框的Vc工程,名称定义为Eg04. 类型选基于对话框. 在本例中,我们要用到一些资源,现列出如下 WinIo.sys WinIo.dll 这是一个动态库,与WinIo.sys完成同并口的连接. WinIo.lib 这是为静态调用提供的引入库文件.虽然引入库文件和静态库文件都是以Lib为扩 展名的,但实际上有本质的区别. WinIo.h 这是IO操作的头文件. ParallelPort.cpp 这是一个并口操作类,用于同WinIo接口,并定义了相关的操作函数.虽然这个 类用于同WinIo的操作接口,但这个类并不是从WinIO派生的,只是一个独立的自定义类.至于 类的自定义,以前我们也提到过. ParallelPort.h 这是并口类的头文件. 这几个文件是可以从网上下载到的,并不是我做的,所以这个动态库大家就不用问我要源码了, 因为我也没有. 首先,把WinIo.lib WinIo.h ParallelPort.cpp ParallelPort.h拷贝到工程目录中,后面马上就要用到. 再把WinIo.Dll WinIo.sys拷贝到工程目录的Debug目录中,以后工程发布后,这两个文件要跟 随工程一起. 下面添加引入库到系统中,点击[工程]->[设置]在弹出的对话框中选择[连接]标签,然后在 [对 象/库模块]中加入WinIo.lib完成后如下图. 然后加入并口操作类的头文件.一般来说,网上可以下载到的源码都是以类的形式给出 的,VC的好处也在于可以把一个操作封装成类,以便在不同的工程中调用.如图,双击类管理器 中的Ceg04Dlg在弹出的文件中加入#include "ParallelPort.h" //添加并口类头文件 同时,还要定义一个类变量,用于并口类的操作. 在刚打开的类定义文件中找到CEg04Dlg类, 并加入一个类变量m_Port,完成后如下图所示 为了操行方便,把并口操作类也加入到工程中,点击[FileView]标签,标签位置在类管理类下面, 见上图中的[FileVi,]然后在[Source Files]中加入ParallelPort.cpp在[Header Files] 中加入 ParallelPort.h这样,就可以在类管理中看到我们所用到的并口操作类了,同时也可以方便的查 阅类函及成员变量. 下面在界面中加入六个按钮和两个文本编辑框,方法前面已经介绍多次.完成后如下图 其中,各控件ID如下 上面的编辑框 IDC_EDIT_READ 用于显示收到的数据 下面的编辑框 IDC_EDIT_WRITE 用于填写要发送的数据 读数据口 IDC_BTN_DREAD 用于读取并口数据总线 写数据口 IDC_BTN_DWRITE 用于写并口数据口数据 读控制口 IDC_BTN_CREAD 用于读取并口控制总线 写控制口 IDC_BTN_CWRITE 用于写并口控制口数据 读状态口 IDC_BTN_SREAD 用于读并口状态总线 并口流水灯 IDC_BTN_LED 用于在数据线上输出流水灯效果 先要介绍一下并口,以便理解接下来的操作是什么意思.以下一些关于并口的介 绍,摘自互联 网. 并口SPP模式寄存器定义 数据寄存器(基地址) 位 引脚:D-sub 信号名 信号源 是否在连接器 处倒相 引脚:Centronics 2 数据位0 PC 否 2 1 3 数据位1 PC 否 3 2 4 数据位2 PC 否 4 3 5 数据位3 PC 否 5 4 6 数据位4 PC 否 6 5 7 数据位5 PC 否 7 6 8 数据位6 PC 否 8 7 9 数据位7 PC 否 9 注:控制寄存器的第5位控制数据位是否能够输出。 状态寄存器(基地址+1) 位 引脚:D-sub 信号名 信号源 是否在连接器 处倒相 引脚: Centronics 3 15 nError 外设 否 32 4 13 Select 外设 否 13 5 12 Paper Out 外设 否 12 6 10 nAck 外设 否 10 7 11 Busy 外设 是 11 注:1,2位未定义。 控制寄存器(基地址+2) 位 引脚:D-sub 信号名 信号源 是否在连接 器处倒相 引脚: Centronics 1 nStrobe PC 是 1 1 14 nAutoLF PC 是 14 2 16 nInit PC 否 31 3 17 nSelectIn PC 是 36 注: 连接器中没有提供的附加位: 4:4:中断启用,此位为1时,IRQ从nAck送往系统的中断控制器;为0时, IRQ 不送往中断控制器。 5:5:双向控制端口的方向控制位,此位为0时,输出启动;为1时,不能输出; 控制端口可以读取外部逻辑电平。 :未定义。 6、7 学过单片机或相关知识的应该可以看懂表格中的意思了,上面罗列的是除去电源及地线后可 用到的IO口,数据口8位,由状态寄存器第五位决定能否输出,状态口共5位,控制口共4位. 一般我们只用到数据口来传输数据.一般来说,并口基地址是0x378,也就是数据寄存器的地址, 在并口类中默认.明白了这些以后,就可以对并口进行相应的操作了. 接下来要在程序初始化时初始化并口,在前面的操作中,定义了一个类变量m_Port,这个类变 量在后面我们将多次用到.首先展开类管理器中的CEg04Dlg双击OnInitDialog()在窗口初始 化中加入以下代码. //在这里初始化并口 if(m_Port.InitPort()==TRUE) { SetDlgItemText(IDC_EDIT_READ,"并口初始化成功,并口地址:0x378"); } else { SetDlgItemText(IDC_EDIT_READ,"并口初始化失败!"); } 完成后如下图所示 m_Port.InitPort()是并口操作类中的并口初始化函数,传入参数是并口地址,由于类定义时默认 了0x378为并口地址,这里可以不用传参数了.如果初始化成功,传回布尔变量TRUE,运行一下, 应该可以看到并口初始化成功的信息. 接下来为读数据口添加代码. 双击[读数据口]按钮,在弹出的代码中加入程序如下 void CEg04Dlg::OnBtnDread() { // TODO: Add your control notification handler code here BYTE nPortData=m_Port.ReadData(); //从并口读到数据 CString a,b; GetDlgItemText(IDC_EDIT_READ,b); //获取编辑框中原有的文本 a.Format("\r\n读到数据口数据:%2.2X",nPortData); b+=a; SetDlgItemText(IDC_EDIT_READ,b); //写入文本到编辑框 //以下用于将滑块自动移到最后一行 unsigned char nLine=((CEdit*)GetDlgItem(IDC_EDIT_READ))->GetLineCount(); ((CEdit*)GetDlgItem(IDC_EDIT_READ))->GetLineCount(); ((CEdit*)GetDlgItem(IDC_EDIT_READ))->LineScroll(nLine); } 完成后如图所示(图8) 这样就可以从并口读到数据了.同样的,可以添加控制口,状态口的按钮响应代码, 用于读到控 制口及状态口,方法相同,这里就不再详叙了.不同的是读控制口时,用到的函数 是 m_Port.ReadCtrl();读状态口用到的函数是m_Port.ReadState(); 再有一个问题要说一下,编辑框默认状态下是单行的,也就是说,我们的换行操作 是没有用的. 可以鼠标右击编辑框,在弹出的菜单中选[属性],并修改其属性如下 图所示(图7) 下面来添加写数据操作.在写数据操作中,我们要换一种方法来读写文本编辑框了. 首先鼠标右击下面的写操作编辑框,在弹出的菜单中选择[建立类向导],英文版的VC请根据 翻译. 然后会弹出一个类向导对话框, 选择[MemberVariables]标签卡后显示如下. ,在ID列表中,我们可以看到窗口中为控件添加的ID号,双击[IDC_EDIT_WRITE]会弹出另一 个对话框,将各框中的内容修改如下图 第一个框中的m_WriteEdit是我们为IDC_EDIT_WRITE这个编辑器定义的一个类变量名,这 个类变量会自动与编辑框IDC_EDIT_WRITE关联,在以后的操作中用到. 确定后双击[写数据口]按钮,为写数据口按钮添加代码,完成后如下 void CEg04Dlg::OnBtnDwrite() { // TODO: Add your control notification handler code here CString a; unsigned char b=0; m_WriteEdit.GetWindowText(a); //获取编辑框中文本 a.MakeUpper();//全部转换为大写 if(a=="") { MessageBox("发送内容不能为空!"); return; } for(unsigned char i=0;i<a.GetLength();i+=3) { if(i+2>a.GetLength()) { MessageBox("数据格式填写不正确!"); return; } if(a.GetAt(i)>=„A? && a.GetAt(i)<=„Z?) b=(a.GetAt(i)-55)*16; //判断填入的是字母还 是数字,并把字符转换成十六进制数 else if(a.GetAt(i)>=„0? && a.GetAt(i)<=„9?) b=(a.GetAt(i)-0x30)*16; else { MessageBox("数据格式填写不正确!"); return; } if(a.GetAt(i+1)>=„A? && a.GetAt(i+1)<=„Z?) b+=(a.GetAt(i+1)-55); else if(a.GetAt(i+1)>=„0? && a.GetAt(i+1)<=„9?) b+=(a.GetAt(i+1)-0x30); else { MessageBox("数据格式填写不正确!"); return; } m_Port.WriteData(b); } } 这里可能有点长,其实与第三章的发送是一样的,增加了一点容错处理而已.在这 m_WriteEdit.GetWindowText(a);来获取编辑框中文本, m_WriteEdit是里,我们用 开始时候在类向导中定 义的变量,GetWindowText是一个窗口函数,因为文本编辑框也认为是窗口,所以窗口操作函数 是通用的,由于m_WriteEdit与文本框IDC_EDIT_WRITE关联,获取的是文本框中的内容.同 样,由于m_WriteEdit是CEdit变量,于是就可以有所有CEdit的所有属性,这与结构变量有点 相似,大家可以慢慢体会.其余口的操作类似,不再重复说了. 这个并口类还有很多功能函数,这里也清一下. #define PIN_STROBE 1 #define PIN_AUTO 14 #define PIN_D0 2 #define PIN_D1 3 #define PIN_D2 4 #define PIN_D3 5 #define PIN_D4 6 #define PIN_D5 7 #define PIN_D6 8 #define PIN_D7 9 #define PIN_ERROR 15 #define PIN_INIT 16 #define PIN_SELIN 17 #define PIN_ACK 10 #define PIN_BUSY 11 #define PIN_PE 12 #define PIN_SLCT 13 这些是对IO口的定义,用于单个IO口的操作,操作函数有以下几个 BOOL SetPinLogic(int nPin,BOOL bLogic); //设置指定引脚,bLogic=1高电平,bLogic=0低电平 BOOL GetPinLogic(int nPin);//得到指定引脚的电平 BOOL SetPinL(int nPin);//设置指定引脚为低电平 BOOL SetPinH(int nPin);//设置指定引脚为高电平 以上些都是这个并口类的函数了,有了这些,我想大家也就可以随便做点东西了,记得曾有过 用并口直接驱动LCD的例子,建议也试一下. 最后一个按钮是流水灯,其实这个对于大家来说并不难,我们也提供一个例子.双击[并口流水 灯]添加流水灯代码.IO口低电平有效. void CEg04Dlg::OnBtnLed() { // TODO: Add your control notification handler code here unsigned char nDataIO=0x01; CString a,b; unsigned char nCData=m_Port.ReadCtrl(); //读控制寄存器数据 nCData&=0xDF; //保证第五位为低,使能IO输出 m_Port.WriteCtrl(nCData); //写控制口数据 for(char i=0;i<8;i++) { m_Port.WriteData(0xff-nDataIO); GetDlgItemText(IDC_EDIT_READ,b); //获取编辑框中原有的文本 a.Format("\r\n流水灯数据[%d]:%2.2X",i+1,0xff-nDataIO); b+=a; SetDlgItemText(IDC_EDIT_READ,b); //写入文本到编辑框 //以下用于将滑块自动移到最后一行 unsigned char nLine=((CEdit*)GetDlgItem(IDC_EDIT_READ))->GetLineCount(); ((CEdit*)GetDlgItem(IDC_EDIT_READ))->GetLineCount(); ((CEdit*)GetDlgItem(IDC_EDIT_READ))->LineScroll(nLine); nDataIO<<=1; Sleep(200); //延时200ms } } 接上硬件可以看到,按一下出现一次流水灯效果,由于不方便按硬件,就没有试过了. 这一章就先到这里了.建议大家自己想个作品来试一下,同时也希望大家分享一下自己的学习 及实验感想,把作品也发上来庆祝一下. 从零开始学VC系列教程 五.消息机制与自定义消息 题外话:我们一再重复的讲Windows的消息,因为这的确是一个关键,会用,并且用好是非常必要的。 这一章我们主要介绍一下消息机制及消息传输,做几个小小的实验,让大家在概念上明白怎么用。 本节内容:学会消息发送接收及自定义消息 学习目的: 撑握消息机制的概念,学会基于消息的操作. 这一章我们要做的例子看起来很简单,只是实现的方式不一样。按[开始]时,在文本框内显示当前的 ]后,显示一个结束信息,按[关闭]则退出程序. 鼠标坐标,按[结束 1.新建工程.首先建一个基于MFC对话框的工程,名字为Eg05。前面四章我们已经介绍过很多次建立工程 的方法了,这里不再重复了,如果还有不明白的,可以看一下以前的记录,或者给我留言。 2.在对话框上放一个EditBox,改ID称为IDC_EDIT_SHOW然后再添加三个按钮,分别设置标题为[开始],[结 束],[关闭]。设置ID分别为IDC_BTN_STAR,IDC_BTN_OVER,IDC_BTN_CLOSE。通过前面几章的学习, 我们知道ID号大家可以自己随便设定,当然最好是接近控件的作用简称或接近控件名的简称。完成后如下 图所示。 首先我们来为[关闭]按钮添加响应,双击[关闭]按钮,添加响应程序。只有一行,呵呵。完成后如下图所示: void CEg05Dlg::OnBtnClose() { // TODO: Add your control notification handler code here PostMessage(WM_CLOSE); } 编译运行,按[关闭]就可以看到程序退出了,晕死,是不是太简单了,下面我们来说一下为什么是这样。 在这里,我们用PostMessage函数发送了一个名为WM_CLOSE的消息,这是一个系统消息,用于关闭程 序。查一查MSDN,可以看到这个函数的一些说明。原文放在下面,方便没有下载MSDN的朋友看看. CWnd::PostMessage BOOL PostMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 ); Return Value Nonzero if the message is posted; otherwise 0. Parameters message Specifies the message to be posted. wParam Specifies additional message information. The content of this parameter depends on the message being posted. lParam Specifies additional message information. The content of this parameter depends on the message being posted. Remarks Places a message in the window’s message queue and then returns without waiting for the corresponding window to process the message. Messages in a message 从原型我们可以看到,其实这个函数还有两个参数,分别为wParam和lParam, 分别默认为0了。这两个 参数是什么意思呢, wParam和lParam 这两个是Win16系统遗留下来的产物,在Win16API中 WndProc有两个参数: 一个是WORD类型的16位整型变量;另一个是LONG 类型的32位整型变量。因此根据匈牙利命名法, 16位的变量就被命名为wParam, 32位的变量就被命名为lParam。 到了Win32API中,原来的16位变量也被扩展为32位,因此此时wParam和lParam的大小完全相同。 在Win32API的早期,为了保证和Win16API的代码可移植性MS定义了WPARAM和LPARAM两个宏。 当时保留了w前缀的原因一方面是由于WPARAM宏也已W开头,还有也因为要提醒程序员注意到可移植 性,当然到了现在Win16早已退出历史舞台,这个前缀也就约定俗成的沿用下 LPARAM 来了。WPARAM 和 本质上没有什么区别:都是32位数, 但是区别也还是有的:除了上面说的关于16位的的历史问题外, MICROSOFT在使用时两种参数分别代表不同的含义和内容,WPARAM常常代表一些控件的ID或者高位 低位组合起来分别表示鼠标的位置,如果消息的发送者需要将某种结构的指针或者是某种类型的句柄时, 习惯上用LPARAM来传递,可以参考各种控件的通知消息。 在WM_CLSOE中,我们使用了不带参数的调用,其实这个消息本来也没有参数,所以后面的两个参数就 默认了。 为了更好的说明这两个参数,下面我们来添加本例程的另一个功能,显示鼠标的坐标。其实显示坐标不用 这么麻烦,在这里是为了更好的展示消息的参数。首先添加一个类变量,从类管理器中双击 [CEg05Dlg]这 个类,会打开这个类的头文件,在类定义中添加一个布尔变量,名称为m_bMouseMoveDisp完成后如下 图所示 如上图,我们添加了两行代码。 public: BOOL m_bMouseMoveDisp; 然后要对这个变量初始化,双击类管理器中的[OnInitDialog]函数,可以打开对话框的初始化函数,我们在 后面添加一行代码:m_bMouseMoveDisp=false; //这里就是变量初始化 下面要添加一个函数,选择[CEg05Dlg],点右键会弹出一个菜单,如图所示 选择[Add Virtual Function,]弹出一个对话框 然后在左边的列表中双击[PreTranslateMessage],可以看到这个函数添加到右边 了,然后选择这个函数, ]就可以添加到类中了。完成后如图: 双击或点最后一个按钮[编辑存在 当然上面的图中,我们还添加了一些响应代码, BOOL CEg05Dlg::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if(pMsg->message==WM_MOUSEMOVE && m_bMouseMoveDisp==TRUE) { CString a; a.Format("X坐标:%d,Y坐 标:%d",pMsg->lParam&0x0000ffff,(pMsg->lParam&0xffff000 0)>>16); SetDlgItemText(IDC_EDIT_SHOW,a); } return CDialog::PreTranslateMessage(pMsg); } 重载这个函数可以捕获本窗体的所有经过消息队列的消息,在win32程序中, 关于消息有两 种传递方式: a.MFC消息,MFC会把所有的消息一条条放到一个AFX_MSGMAP_ENTRY结构中,形成一个 数组,该数组存放了所有的消息和与它们相关的参数。也可 以说是放到消息队列里去。 b. 采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列 的消息。 这两种方式中只有第一种(穿过消息队列的消息)才受PreTranslateMessage()影响, 第二种消息并不会理睬PreTranslateMessage()的存在。 通过函数参数可以看到,传进来的是一个消息结构,我们查MSDN可以得到消息结构的原型: typedef struct tagMSG { // msg HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG; 很明显,可以从message得到消息名称,所以我们写成 if(pMsg->message==WM_MOUSEMOVE && m_bMouseMoveDisp==TRUE)在这里,m_bMouseMoveDisp是前面一步我们定义的类变量。当我 们移动鼠标时,系统会发出一个名为WM_ MOUSEMOVE的消息到所有窗口,这个消息会经 过消息队列,所以可以在这里捕获到,这个消息带两个参数,分别为 fwKeys = wParam; // key flags xPos = LOWORD(lParam); // horizontal position of cursor yPos = HIWORD(lParam); // vertical position of cursor 可以看到,lParam这个参数包括了光标所在处的坐标,而wParam则包含了鼠标键的事件, 包括 MK_CONTROL Set if the ctrl key is down. MK_LBUTTON Set if the left mouse button is down. MK_MBUTTON Set if the middle mouse button is down. MK_RBUTTON Set if the right mouse button is down. MK_SHIFT Set if the shift key is down. ->lParam&0x0000ffff取得参数lParam的低字节16位 所在我们用pMsg 为X坐标, 用,pMsg->lParam&0xffff0000)>>16的高16位数据,并移至低位作为Y坐标,然后嘛,大 家都看得懂了,显示在IDC_EDIT_SHOW中。当然,这里还有一个条件,就是 m_bMouseMoveDisp要为TRUE,这个就是按钮的事了,我们双击[开始]按钮,添加一行代 码。bMouseMoveDisp=TRUE;完成后编译运行,点[开始]就可以看到框内显示了鼠标在窗 体中的坐标了。实时效果如下: 要关闭坐标显示就很简单了,把m_bMouseMoveDisp置为FALSE就行了,所以大家可以
本文档为【手把手教你学VC经典教程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_471618
暂无简介~
格式:doc
大小:94KB
软件:Word
页数:0
分类:工学
上传时间:2017-10-13
浏览量:13