社区
界面
帖子详情
关于mfc单文档中获取工具栏按钮句柄
思考者Jack
2018-05-17 07:22:26
请问一下大家,怎么获取单文档工具栏中某一个按钮的句柄,我这边自定义了多个按钮,现在想处理鼠标UP和DOWN的消息,要获取到对应按钮的句柄才能进行下一步的处理。
...全文
793
4
打赏
收藏
关于mfc单文档中获取工具栏按钮句柄
请问一下大家,怎么获取单文档工具栏中某一个按钮的句柄,我这边自定义了多个按钮,现在想处理鼠标UP和DOWN的消息,要获取到对应按钮的句柄才能进行下一步的处理。
复制链接
扫一扫
分享
转发到动态
举报
写回复
配置赞助广告
用AI写文章
4 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
思考者Jack
2018-06-15
打赏
举报
回复
该问题已使用其他方案解决,获取句柄按照大家的说法是获取不到的。
思考者Jack
2018-05-18
打赏
举报
回复
引用 2 楼 schlafenhamster 的回复:
工具条 里 本身的 ‘按钮’ , 是 无 句柄 的 , 你加的 按钮 在 Create 时 可以 保留句柄。
好的,我知道了。
schlafenhamster
2018-05-17
打赏
举报
回复
工具条 里 本身的 ‘按钮’ , 是 无 句柄 的 , 你加的 按钮 在 Create 时 可以 保留句柄。
schlafenhamster
2018-05-17
打赏
举报
回复
点击TB时: CButton *pBt=(CButton *)WindowFromPoint(point);
MFC
教程入门知识全集.rar
说明:本书稿为张孝祥、袁龙友两位老师在2000 年依据张孝祥的vc 讲课记录整理,由于时间关系,仅仅是写成了草稿,欢迎大家使用! 第1 章 掌握C 1.1 类的定义与应用 1.1.1 结构的定义 1.1.2 类的定义 1.1.2.1 类与结构 1.1.2.2 类的使用 (例子代码EX01-00) 1.2 函数的重载 1.2.1 重载参数个数不同的函数 (例子代码EX01-01) 1.2.2 重载参数数据类型不同的函数 (例子代码EX01-02) 1.3 构造函数与析构函数 1.3.1 构造函数 1.3.2 析构函数 (图1,没有)(图2,没有) 1.4 this 指针的引用 1.5 类的继承与protected 访问修饰符 1.5.1
单
一继承 (例子代码EX01-03) (图x,没有) 1.5.2 多重继承 1.6 虚函数与多态性 1.7 类的书写规范 1.8 小结 第2 章 Windows 程序内部运行原理 2.1 Windows 应用程序,操作系统,计算机硬件之间的相互关系 2.1.1 关于API (图1,没有) 2.1.2 关于消息及消息队列 (图1、图2 没有) 2.2 什么是
句柄
2.3 谈谈WinMain 函数 (例子代码EX02-00) 2.3.1 WinMain 函数的定义及功能 2.3.2 窗口及其生成 2.3.2.1 如何设计一个窗口类——步骤1 2.3.2.2 注册设计好的窗口类——步骤2 2.3.2.3 创建窗口——步骤3 2.3.2.4 显示创建的窗口——步骤4 2.3.3 消息循环 2.3.4 完成回调函数 2.4 程序编写操作步骤与实验 2.5 小结 第3 章 VC 集成开发环境介绍 3.1 Visual C 开发环境 3.1.1 工作区窗格 3.1.2 输出窗格 3.1.3 编辑区 3.1.4 菜
单
栏、
工具栏
、状态栏 3.2 系统菜
单
功能介绍 3.2.1 File 菜
单
3.2.2 Edit 菜
单
3.2.3 View 菜
单
3.2.4 Insert 菜
单
3.2.5 Project 菜
单
3.2.6 Build 菜
单
3.2.6 Tools 菜
单
3.2.7 Window 菜
单
3.2.8 Help 菜
单
3.3 Visual C 重要工具介绍 3.3.1 C/C 编译器 3.3.2 资源编辑器 3.3.3 资源编译器 3.3.4 链接器和调试器 3.3.5 AppWizard 和ClassWizard 3.3.6 资源浏览器 3.3.7 Microsoft 活动模板库、仓库 3.4 小结 第4 章
MFC
应用程序框架剖析 4.1 什么是
MFC
以及
MFC
的特点 (例子代码EX04-00) 4.2
MFC
应用程序框架 (例子代码EX04-01) 4.3 应用程序框架说明 4.4
文档
-视图体系结构 4.4.1
文档
-视图相互作用的函数 4.4.2
单
文档
-视图应用程序结构 4.4.2 多
文档
-视图应用程序结构 4.5
MFC
消息映射机制 4.5.1 消息的种类 4.5.2 应用程序的Run 函数 4.5.3 消息映射表 4.5.4 如何添加消息映射 4.6 ClssWizard 的使用 4.6.1 ClssWizard 概貌 4.6.2 如何添加消息处理函数 4.6.3 如何添加成员变量 4.6.4 如何添加一个新类 第5 章 图形与文本 5.1 理解图形设备接口 5.2 设备描述表 5.2.1 什么是设备描述表 5.2.2
MFC
中
的设备描述表类 5.2.3
获取
设备描述表 5.3 Windows 的GDI 对象 5.4 GDI 对象的创建 5.4.1 自定义画刷(CBrush) 5.4.2 自定义画笔(CPen) 5.4.3 自定义字体(CFont) 5.5 GDI 对象的使用及示例 5.5.1 画笔的使用 5.5.1.1 在
MFC
程序
中
画线 5.5.1.2 在Windows Application 程序
中
画线 5.5.1.3 实现橡皮筋功能 5.5.2 画刷的使用 5.5.2.1 带颜色的画刷的使用 5.5.2.2 带位图的画刷的使用 5.5.3 字体的使用 5.5.3.1 一个简
单
的文字处理程序 5.5.3.2 模拟卡拉OK 程序 5.5.3.3 剪切区和路径层 第六章 菜
单
、
工具栏
和状态栏 6.1 菜
单
6.1.1 菜
单
介绍 6.1.2 创建一个菜
单
6.1.2.1 添加一个菜
单
资源 6.1.2.2 利用菜
单
编辑器编辑菜
单
资源 6.1.2.3 将菜
单
加入到应用程序
中
6.1.2.4 给菜
单
项添加COMMAND 消息处理 6.1.2.5 给菜
单
项添加UPDATE_COMMAND_UI 消息处理 6.1.2.6 一个简
单
的绘图程序 6.1.3 在应用程序
中
控制菜
单
6.1.3.1 在应用程序
中
取得菜
单
6.1.3.2 在应用程序
中
修改菜
单
的状态 6.1.3.3 在应用程序
中
添加、删除、插入菜
单
或菜
单
项 6.1.3.4 一个简易的电话本程序 6.1.4 创建快捷方式菜
单
6.2
工具栏
6.2.1
工具栏
介绍 6.2.1.1 熟悉CToolBar 类 6.2.1.2 AppWizard 是如何创建
工具栏
6.2.1.3 利用
工具栏
编辑器设计
工具栏
按钮
6.2.2 新建一个
工具栏
6.2.3 显示/隐藏
工具栏
6.3 状态栏 6.3.1 状态栏介绍 6.3.1.1 熟悉CStatusBar 类 6.3.1.2 AppWizard 是如何创建状态栏 6.3.2 修改状态栏 6.3.2.1 指示器数组 6.3.2.2 修改状态栏窗格 6.3.3 在状态栏上显示鼠标坐标、时钟和进度条 6.3.3.1 在状态栏上显示鼠标坐标 6.3.3.2 在状态栏上显示时钟 6.3.3.3 在状态栏上显示进度条 第七章 对话框和控件 7.1 对话框及控件的介绍 7.1.1 常用控件介绍 7.1.2 对话框介绍 7.1.2.1 对话框的组成 7.1.2.2 对话框的种类 7.1.2.3 使用对话框编辑器设计对话框 7.1.3 创建一个对话框 7.1.3.2 创建非模态对话框 7.1.3.3 对话框的数据交换机制 7.1.3.4 创建模态对话框 7.1.4 模态对话框和非模态对话框的区别 7.1.5
按钮
逃跑小程序 7.2 属性页和向导对话框 7.2.1 创建属性页对话框 7.2.1 创建向导对话框 7.3 公用对话框 7.3.1 增加设置对话框来完善绘图程序 7.3.2 颜色对话框的使用 7.3.3 字体对话框的使用 7.3.4 控制控件颜色做漂亮界面 第8 章
文档
序列化 8.1 序列化 8.1.1 CArchive 类和Serialize 函数 8.1.2 使自己的类支持序列化 8.1.3 实例:保存和显示图形 8.2 CFile 类 8.2.1 CFile 类的构造函数 8.2.2 打开文件 8.2.3 读写数据 8.2.4 关闭文件 8.3 文件I/O 处理 8.3.1 利用
MFC
类来实现 8.3.2 利用C 函数来实现 8.3.3 利用C 函数来实现 8.3.4 利用API 函数来实现 8.3.5 创建保存、打开对话框 8.4 注册表操作 8.4.1 什么是注册表 8.4.2 注册表结构 8.4.3 修改注册表 第9 章 修改框架程序的显示效果 9.1 修改Windows 应用程序外观样式 9.1.1 在框架类
中
修改程序外观 9.1.2 在视图类
中
修改程序外观 9.2 制作动画图标 9.3 将图片作为窗口显示的背景 第10 章 网络编程 10.1 计算机网络的基本概念 10.1.1 计算机网络的分类 10.1.2 网络拓扑结构 10.2 网络体系结构和网络协议 10.2.1 ISO/OSI 参考模型 10.2.2 TCP/IP 参考模型 10.2.3 TCP/IP 协议 10.2.3.1 协议概述 10.2.3.2 TCP/IP 协议层次结构及各种协议介绍 10.2.3.3 IP 地址 10.2.3.4 端口号 10.2.4 专业术语解释 10.3 Socket 套接字 10.3.1 Socket 介绍 10.3.2 关于协议族和地址族 10.3.3 使用Socket 10.3.3.1 创建Socket 10.3.3.2 指定本地地址及端口号 10.3.3.3 建立连接 10.3.3.4 监听连接 10.3.3.5 发送数据 10.3.3.6 接收数据 10.3.3.7 关闭套接字 10.3.4 套接字一般调用过程 10.4 WinSock 编程机制 10.4.1 认识Windows Socket 10.4.2 Windows Sockets 库函数介绍 10.4.2.1 Berkeley socket 函数 10.4.2.2 数据库函数 10.4.2.3 Windows 专有扩展函数 10.5 WinSock 编程实例 10.5.1 实例一:面向连接服务的socket 调用 10.5.2 实例二:面向非连接服务的socket 调用 10.5.3 实例三:基于字符界面的聊天程序 第11 章 线程间的同步 11.1 进程和线程的概念 11.2 Win32 的线程 11.2.1 线程的创建 11.2.2 线程的终止 11.2.3 实例:通过创建多线程来编写网络聊天程序 11.3
MFC
的线程处理 11.3.1 创建工作者线程 11.3.2 创建用户界面线程 11.4 线程同步 11.4.1 为什么要同步 11.4.2 等待函数 11.4.3 同步对象 11.4.3.1 关键代码段 11.4.3.2 互斥对象 11.4.3.3 信标对象 11.4.3.4 事件对象 11.4.4 如何选择同步对象 第12 章 进程间的通讯 12.1 进程控制 12.1.1 进程的创建 12.1.2 进程的终止 12.2 进程间通讯 12.2.1 剪贴板通讯方式 12.2.2 邮槽通讯方式 12.2.3 管道通讯方式 12.2.3.1 命名管道通讯 12.2.3.2 匿名管道通讯 第14 章 ActiveX 技术 14.1 ActiveX 简介 14.2 ActiveX 控件和普通Windows 控件 14.2.1 ActiveX 控件和普通Windows 控件的相同点 14.2.2 ActiveX 控件和普通Windows 控件的相同点 14.3 ActiveX 控件的属性、方法和事件 14.3.1 ActiveX 控件的属性 14.3.2 ActiveX 控件的方法 14.3.3 ActiveX 控件的事件 14.4 创建ActiveX 控件
MFC
Windows程序设计(第2版修订版)--源代码
此代码是随书光盘拷贝。 此版本是网上最详细的书签版本,也是最清晰的版本。
MFC
Windows程序设计(第2版修订版) (Programming Windows with
MFC
, 2nd Edition) 基本信息 作者: (美)Jeff Prosise [作译者介绍] 译者: 北京博彦科技发展有限责任公司 出版社:清华大学出版社 ISBN:9787302150428 上架时间:2007-5-22 出版日期:2007 年5月 开本:16开 页码:1166 版次:1-1 内容简介 《
mfc
windows程序设计(第2版)》是对其极为经典的第1版的全面更新,本书不仅扩展了已被认为是权威的关于microsoft用于windows api的功能强大的c++类库的阐述,还新增了有关com、ole和activex的内容。本书的作者,jeff prosise,用其无与伦比的技巧向读者讲述了
mfc
程序设计
中
的基本概念和主要技术——再次阐释了在32位windows平台上进行了快速的面向对象开发的完美方法。 本书涵盖了以下专题: 事件驱动程序设计和mpc的基础知识
文档
/视图体系结构 位图、调色板和区域 多线程和线程同步
mfc
与组件对象模型(com) activex控件 《
mfc
windows程序设计(第2版)》见解深刻,并附带有辅助学习的资源——包括在随附的cd-rom
中
提供了大量代码实例。 要理解如何利用
mfc
库
中
提供的成千上万的预编译、预测试的代码的优点,本书——及其作者——将是您独一无二的选择。 作译者 本书提供作译者介绍 Jeff Prosise是一位作者、教员和讲师,他以Windows编程和教授别人如何进行Windows为生。作为一位在Windows程序设计、
MFC
和COM领域世界知名的权威,他还是《PC Magazinge》和《Microsoft Systems Journal》杂志的组稿编辑。 目录 鸣谢 序言 第ⅰ部分 windows和
mfc
基础 第1章 hello,
mfc
第2章 在窗口
中
绘图 第3章 鼠标和键盘 第4章 菜
单
第5章
mfc
集合数 第6章 文件i/o和串行化 第7章 控件 第8章 对话框和属性表 第ⅱ部分
文档
/视图体系结构 第9章
文档
、视图和
单
文档
界面 第10章 滚动视图、html视图以及其他视图类型 第11章 多
文档
和多视图 第12章
工具栏
、状态栏和组合栏 第13章 打印和打印预览 第ⅲ部分 高级篇 第14章 计时器和空闲处理 第15章 位图、调色板以及区域 . 第16章 公用控件 第17章 线程和线程同步化 第ⅳ部分 com,ole和activex 第18章
mfc
和组件对象模型 第19章 剪贴板和ole拖放 第20章 automation 第21章 activex控件 序言 像我的许多同行一样,我学习Windows编程是从读Petzold的书《Windows程序设计》——一本所有Windows程序员都使用的Windows编程圣经——开始的。在刚刚成为一名
MFC
程序员时,那天我冲进书店,想买一本能够与ProgrammingWindows相媲美的
MFC
编程书籍,但是却没有结果。于是我决定自己写一本这样的书。它正是您手
中
所拿到的这本书,也正是当初我在初学
MFC
时,希望拥有的那本书。. 正如您所知,
MFC
是用于Windows编程的Microsoft C++类库。本书并不是一本关于C++的书,而是一本关于使用
MFC
而不是Windows APl,进行32位Windows应用程序编程,以访问操作系统的基本特性和服务的书。它原本为以下两种编程人员所写: 曰打算学习
MFC
的WindowsAPl程序员 曰未进行过Windows编程的程序员 无论您属于哪种人,我假设您已经了解C++,而且熟悉C++语法,如派生类及虚函数。如果是这样的话,您已在征肠
MFC
编程之山的历险
中
迈出了坚实的第一步。 即使是有经验的Windo;。程序员在初读
MFC
代码时也会感到迷惑。一部分是由于VisualC++代码生成向导所生成的代码所致,另一个原因是因为无数行代码隐藏在
MFC
类库
中
。这正是本书使用特殊的方法来写
MFC
的缘由。本书从让您亲自手写代码开始(不用向导),使用
MFC
l.0的应用程序结构风格,也就是说,既不用
文档
也不用视图。只有在您掌:握了上述的基础知识,初步认识了简
单
的
MFC
类库如CWnd和CWinApp之后,我才开始介;绍向导并教您使用
MFC
的
文档
视图结构的方法。您将逐渐理解Windows的关键组件及它的消息驱动机制,如图形设备接口(GDl)。我相信用这种方法可以使
MFC
的学习变得生动有趣而不是令人气馁。我认为一旦您认真地学完这本书,然后再站在老练的Windows程序员的角度来回顾学习过程
中
的甜酸苦辣,您将同意我的观点。 本书分为4个部分。第1部分,介绍了Windows及
MFC
编程的主要原则,以一个示例程序"Hello,
MFC
"开始,然后逐一简要讲解菜
单
、控件、对话框以及其他用于组建应用程序的模块。第2部分,在第1部分的基础之上,对
文档
视图结构做详细讲解,特别是第9、10、11章,揭示了实现
文档
视图的神奇之处,不仅介绍了如何编写简
单
的
文档
视图程序,而且教您一些高级功能,如打印预览和拆分窗口视图。第3部分涵盖了一些Windows和
MFC
的更鬲级的功能,如颜色选项板、位图
句柄
及多线程程序。在第4部分,您将了解到
MFC
如何包容COM、OLE和ActiveX,如何编写支持COM的组件及应用程序。在读完第21章之后,您将更加精通使用
MFC
进行Windows 32位编程的技术。您也将拥有丰富的源代码用于您的第一 第2版的新特点 读过本书第1版的朋友将会注意到第2版的两个相当明显的变化。首先,本版包括7个新章节,1章用于讲解
MFC
视图类,1章涵盖
MFC
集合类;1章介绍
MFC
文件I/0和串行化机制,4章讨论了
MFC
和COM的关系。
MFC
不是类似活动模板库(ATL)的通用COM框架,但是
MFC
使编写某些COM程序更容易,使编写ActiveX控件的过程更简
单
,而且它使编写自动化(Automation)服务器程序(使用COM技术来供脚本客户调用的程序)变成了二件轻而易举的事。.. 第2版的主要变化是关于向导的知识。在第1版
中
并没有提到向导。在第2版
中
1-3章使用手写的示例程序,但到了第4章就使用AppWizard和ClassWizard来生成代码。这样做的原因是,我始终认为代码生成向导会影响
MFC
的学习,它只应由老练的程序员来使用。但我也逐渐认识到,实际上,有很多
MFC
程序员在使用
MFC
向导来做某些工作,如编写ActiveX控件,在此时不用向导是很不明智的。经过深思熟虑之后,我决定加上这些内容。 尽管这些新内容是关于向导的使用,然而它现在不是,以后也不会是一本仅仅介绍如何在向导
中
单
击
按钮
的书。在介绍了一个像如何用ClassWizard编写消息
句柄
之类的基本技巧之后,我在给出代码时,将不再赘述这些内容。当然,。向导永远不能超越您的能力去做任何事,因此键人所有手写的代码是完全可行的。 在讲述
MFC
编程的书
中
使用向导的缺点是,向导生成的程序不适合发表。本书的第一版印刷了每个程序文件的代码。本版书没有这样做,而只是包括了“相关”的源代码文件,其他的都放在CD-ROM
中
,为什么?因为印刷这些代码会使本书增加一倍的厚度而没有相应增加内涵。其
中
一些代码是由Visual C++6.0的
MFC
向导产生的,甚至没有被编译(详细内容请参见第4章)。我并不为本书
中
向导生成的代码而得意,因为其
中
充斥着随意生成的空行、不连贯的注释和无用的函数。对那些以编写清晰易读的示例源代码为骄傲的作者来说,向导生成的东西是一剂苦药。 然而,向导代表了Windows编程的新法则,它们是你、我及所有人都必须习惯的重要事物。但令人遗憾的是,VisualC++开发小组没有给我们一个真正的向导,而只是一个冒充向导的玩具。在他们能够做到以前,我们得习惯现有的向导。 CD.ROM
中
的内容 本书随附的CD-ROM包括书
中
全部示例程序的源代码,它们都在Visual C十十6.0和
MFC
6.0环境下编写、编译而成,并在Win32平台上测试通过。如果没有特别说明它们都与Windows 98、WindowsNT4.0及Windows 2000兼容,其
中
大部分也与Windows 95及WindowsNT 3.51兼容。 您可用CD—ROM的根目录下的安装程序来将CD-ROM
中
,的内容复制到硬盘里,也可只复制其
中
\Code目录
中
的代码。我们在此目录
中
为书
中
每章各建了一个子目录——Chap01、Chap02,依次类推。在这些子目录
中
存放示例程序。每个程序的源代码文件都有一个相应的发布版本的EXE文件,以及您可使用Visual C++的Open Workspace命令来打开的workspace(DSW)文件。 JeffProsise 1999年3月12日...
从新手小白入门
MFC
框架视频精讲
微软基础类库(英语:Microsoft Foundation Classes,简称
MFC
)是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。...
MFC
Windows程序设计(第2版修订版)--详细书签版2卷
此版本是网上最详细的书签版本,也是最清晰的版本。
MFC
Windows程序设计(第2版修订版) (Programming Windows with
MFC
, 2nd Edition) 基本信息 作者: (美)Jeff Prosise [作译者介绍] 译者: 北京博彦科技发展有限责任公司 出版社:清华大学出版社 ISBN:9787302150428 上架时间:2007-5-22 出版日期:2007 年5月 开本:16开 页码:1166 版次:1-1 内容简介 《
mfc
windows程序设计(第2版)》是对其极为经典的第1版的全面更新,本书不仅扩展了已被认为是权威的关于microsoft用于windows api的功能强大的c++类库的阐述,还新增了有关com、ole和activex的内容。本书的作者,jeff prosise,用其无与伦比的技巧向读者讲述了
mfc
程序设计
中
的基本概念和主要技术——再次阐释了在32位windows平台上进行了快速的面向对象开发的完美方法。 本书涵盖了以下专题: 事件驱动程序设计和mpc的基础知识
文档
/视图体系结构 位图、调色板和区域 多线程和线程同步
mfc
与组件对象模型(com) activex控件 《
mfc
windows程序设计(第2版)》见解深刻,并附带有辅助学习的资源——包括在随附的cd-rom
中
提供了大量代码实例。 要理解如何利用
mfc
库
中
提供的成千上万的预编译、预测试的代码的优点,本书——及其作者——将是您独一无二的选择。 作译者 本书提供作译者介绍 Jeff Prosise是一位作者、教员和讲师,他以Windows编程和教授别人如何进行Windows为生。作为一位在Windows程序设计、
MFC
和COM领域世界知名的权威,他还是《PC Magazinge》和《Microsoft Systems Journal》杂志的组稿编辑。 目录 鸣谢 序言 第ⅰ部分 windows和
mfc
基础 第1章 hello,
mfc
第2章 在窗口
中
绘图 第3章 鼠标和键盘 第4章 菜
单
第5章
mfc
集合数 第6章 文件i/o和串行化 第7章 控件 第8章 对话框和属性表 第ⅱ部分
文档
/视图体系结构 第9章
文档
、视图和
单
文档
界面 第10章 滚动视图、html视图以及其他视图类型 第11章 多
文档
和多视图 第12章
工具栏
、状态栏和组合栏 第13章 打印和打印预览 第ⅲ部分 高级篇 第14章 计时器和空闲处理 第15章 位图、调色板以及区域 . 第16章 公用控件 第17章 线程和线程同步化 第ⅳ部分 com,ole和activex 第18章
mfc
和组件对象模型 第19章 剪贴板和ole拖放 第20章 automation 第21章 activex控件 序言 像我的许多同行一样,我学习Windows编程是从读Petzold的书《Windows程序设计》——一本所有Windows程序员都使用的Windows编程圣经——开始的。在刚刚成为一名
MFC
程序员时,那天我冲进书店,想买一本能够与ProgrammingWindows相媲美的
MFC
编程书籍,但是却没有结果。于是我决定自己写一本这样的书。它正是您手
中
所拿到的这本书,也正是当初我在初学
MFC
时,希望拥有的那本书。. 正如您所知,
MFC
是用于Windows编程的Microsoft C++类库。本书并不是一本关于C++的书,而是一本关于使用
MFC
而不是Windows APl,进行32位Windows应用程序编程,以访问操作系统的基本特性和服务的书。它原本为以下两种编程人员所写: 曰打算学习
MFC
的WindowsAPl程序员 曰未进行过Windows编程的程序员 无论您属于哪种人,我假设您已经了解C++,而且熟悉C++语法,如派生类及虚函数。如果是这样的话,您已在征肠
MFC
编程之山的历险
中
迈出了坚实的第一步。 即使是有经验的Windo;。程序员在初读
MFC
代码时也会感到迷惑。一部分是由于VisualC++代码生成向导所生成的代码所致,另一个原因是因为无数行代码隐藏在
MFC
类库
中
。这正是本书使用特殊的方法来写
MFC
的缘由。本书从让您亲自手写代码开始(不用向导),使用
MFC
l.0的应用程序结构风格,也就是说,既不用
文档
也不用视图。只有在您掌:握了上述的基础知识,初步认识了简
单
的
MFC
类库如CWnd和CWinApp之后,我才开始介;绍向导并教您使用
MFC
的
文档
视图结构的方法。您将逐渐理解Windows的关键组件及它的消息驱动机制,如图形设备接口(GDl)。我相信用这种方法可以使
MFC
的学习变得生动有趣而不是
MFC
的程序框架剖析
什么是
句柄
?
句柄
,是整个Windows编程的基础。一个
句柄
是指使用的一个唯一的整数值,即一个4字节(64位程序
中
为8字节)长的数值,来标识应用程序
中
的不同对象和同类对象
中
的不同的实例,诸如,一个窗口,
按钮
,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过
句柄
访问相应的对象的信息,但是
句柄
不是一个指针,程序不能利用
句柄
来直接阅读文件
中
的信息。如果
句柄
不用在I/O文件
中
,它是毫无用处的。
句柄
是Windows用来标志应用程序
中
建立的或是使用的唯一整数,Windows使用了大量的
句柄
来标志很多对象。 一、
MFC
AppWizard 1、
MFC
(Microsoft Foundation Class,微软基础类库)是微软为了简化程序员的开发工作所开发的一套C++ 类的集合,是一套面向对象的函数库,以类的方式提供给用户使用 2、
MFC
AppWizard是一个辅助我们生成源代码的向导工具,它可以帮助我们自动生成基于
MFC
框架的源代码 二、基于
MFC
的程序框架剖析 1、
MFC
程序的ClassView标签页(图) 2、继承关系 (1)CMainFrame继承于CFrameWnd (2)CTestApp继承于CWinApp (3)CTestDoc继承于CDocument (4)CTestView继承于CView 注:CFrameWnd和CView都继承于CWnd 3、CWnd类是
MFC
中
一个非常重要的类,它封装了与窗口相关的操作 4、
MFC
类的简化组织结构图(图) 5、
MFC
程序也有一个WinMain函数,程序是在编译时,由链接器将它链接到程序
中
6、
MFC
程序具有一个CTestApp类的全局对象theApp,在
MFC
程序运行时,程序执行的顺序为:theApp全局对象定义 处->CTestApp构造函数->WinMain函数 7、对于普通的VC++控制台程序,无论全局变量还是全局对象,程序运行时,在加载main函数之前,就已经为它们 分配了内存空间。对于一个全局对象来说,此时就会调用该对象的构造函数,构造该对象,并进行初始化操作 8、实例
句柄
与全局对象 (1)对于Win32 SDK程序,应用程序的实例是由实例
句柄
(WinMain函数的hInstance参数)来标识的 (2)对于
MFC
程序,应用程序的实例是由全局对象(每一个
MFC
程序有且仅有一个从应用程序类CWinApp派生的类, 如CTestApp,它实例化theApp全局对象)来标识的 9、基类构造函数
中
this指针的指向问题 在构造子类对象时,会自动调用父类的构造函数,此时在父类的构造函数
中
的this指针所指向的是子类对象地址 10、AfxWinMain函数
MFC
程序的WinMain函数是通过调用AfxWinMain函数来完成它的功能的 注:Afx前缀的函数代表应用程序框架(Application Framework)函数,它们可以在程序的任何地方被调用 11、CTestApp::InitInstance函数 在AfxWinMain函数
中
,通过调用InitInstance函数来完成
MFC
内部管理方面的工作 12、AfxEndDeferRegisterClass函数
MFC
提供了一些默认的标准窗口类,我们只需要选择所需的窗口类就行。然后,调用AfxEndDeferRegisterClass 函数来注册窗口类 13、CMainFrame::PreCreateWindow函数
MFC
程序具有两个窗口(框架窗口和视类窗口),在框架窗口产生之前会调用PreCreateWindow函数 14、CWnd::CreateEx函数 在
MFC
程序
中
,窗口的创建是由CreateEx函数实现的 15、CWnd::CreateWindowEx函数 主要作用是当修改了CreateEx函数的CREATESTRUCT参数时,CreateWindowEx函数会根据参数发生的相应变化来创 建一个符合我们要求的窗口 注:
MFC
中
后缀名为Ex的函数都是扩展函数 16、CMainFrame::ShowWindow函数和CMainFrame::UpdateWindow函数 用于显示应用程序框架窗口和更新这个窗口 17、CWinThread::Run函数和CWinThread::PumpMessage函数 用于完成消息循环 18、DefWindowProc函数 默认的窗口过程,但
MFC
程序对消息的处理实际上是通过消息映射机制来完成的 19、
MFC
程序的运行过程 (1)首先利用全局应用程序对象theApp启动应用程序 (2)调用全局应用程序对象的构造函数,从而就会调用其基类CWinApp的构造函数,以完成应用程序的一些初始化 (3)进入WinMain函数 (4)进入消息循环 20、
MFC
程序的主要过程 theApp-> CTestApp::CTestApp构造函数-> CWinApp::CWinApp构造函数-> _tWinMain(WinMain函数的宏)-> AfxWinMain函数-> CTestApp::InitInstance函数-> AfxEndDeferRegisterClass函数-> CMainFrame::PreCreateWindow函数-> CFrameWnd::PreCreateWindow函数-> AfxDeferRegisterClass(AfxEndDeferRegisterClass函数的宏)-> CFrameWnd::Create函数-> CWnd::CreateEx函数-> CMainFrame::PreCreateWindow函数-> CWnd::CreateEx函数-> CMainFrame::ShowWindow函数-> CMainFrame::UpdateWindow函数-> CWinThread::Run函数-> CWinThread::PumpMessage函数 21、框架窗口(整个应用程序外框所包括的部分)是视类窗口(框架窗口
中
空白的地方)的一个父窗口 22、
MFC
提供了一个
文档
/视类的结构,
文档
是指CDocument类,视类是指CView类。前者用于数据的存储和加载, 后者用于数据的显示和修改 23、框架对象、
文档
对象和视类对象是通过一个
单
文档
模板指针来有机地组织在一起,并利用AddDocTemplate函数 把这个
单
文档
模板添加到
文档
模板
中
,从而把这三个类组织成为一个整体 24、
MFC
程序的CAboutDlg类继承于CDialog类,用于为用户提供一些与程序相关的帮助信息 三、窗口类、窗口类对象与窗口 1、以“::”开始的函数是一个全局函数,表示调用的是Platform SDK的函数 2、如果我们关闭了一个窗口,这个窗口就销毁了,那么该窗口对应的C++窗口类对象销毁了吗? (1)当一个窗口销毁时,它会调用CWnd::DestroyWindow函数,该函数销毁窗口后,将CWnd::m_hWnd设为NULL (2)窗口的生命周期和C++窗口类对象的声明周期不是一致的。当一个窗口销毁时,与C++窗口类对象没有关系,它 们之间的纽带仅仅在于这个C++窗口类内部的成员变量m_hWnd,该变量保存了与这个C++窗口类对象相关的哪个窗口 的
句柄
(3)但是,当C++窗口类对象销毁时,与之相关的窗口也将销毁,因为它们之间的纽带m_hWnd已经断了 3、示例---在窗口
中
显示
按钮
(1)CButton
按钮
类继承于CWnd (2)对于一个CButton对象,在定义之后就可以使用了;但是,如果要显示这个
按钮
的话,还需调用 CButton::Create函数,把
按钮
窗口与CButton对象关联起来 (3)
MFC
程序的窗口创建时都会产生WM_CREATE消息,该消息通过OnCreate函数来捕获。对于框架窗口来说,
MFC
直 接把OnCreate函数提供到了CMainFrame
中
;而在视类窗口
中
没有提供该函数,如需使用,要用户自行添加 (4)通常对
MFC
程序的操作,都是在CTestView视类窗口
中
进行的 (5)在窗口创建之后,要显示该窗口可以通过调用ShowWindow函数或指定窗口风格为WS_VISIBLE来实现 (6)实现过程 A:在CTestView类
中
,添加CButton类型的私有成员m_btn B:在CTestView类
中
,添加WM_CREATE消息的OnCreate处理函数 C:在CTestView类
中
,通过GetParent函数可以获得CMainFrame框架窗口对象的指针 D:实现一(在视类窗口
中
通过ShowWindow函数显示
按钮
) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("
按钮
",WS_CHILD|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); m_btn.ShowWindow(SW_SHOWNORMAL); return 0: } E:实现二(在视类窗口
中
通过WS_VISIBLE风格显示窗口) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("
按钮
",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); return 0: } F:实现三(在框架窗口
中
显示
按钮
) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("
按钮
",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),GetParent(),123); return 0: } 即便是基于
MFC
的应用程序,建立窗口类也是会遵循如下的过程: 设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口->消息循环->消息路由到窗口过程函数处理。下面就剖析一下在
MFC
中
是如何完成上述过程的。 (1)每个应用程序都有且仅有一个应用类的全局变量theApp,全局变量先于WinMain函数进行处理。 (2)WinMain函数体在APPMODUL.CPP文件
中
,定义如下: extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 其
中
#define _tWinMain WinMain (3)AfxWinMain函数体在WINMAIN.CPP文件
中
,里面有如下两句话: CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); 其实这里得到的这两个指针都是指向全局的对象theApp的; 接下来有函数调用pThread->InitInstance(),根据多态性,会调用CXXXApp类
中
的InitInstance()函数。该函数很重要,在对该函数的调用
中
就会完成:设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口。 接下来,该函数
中
会继续调用pThread->Run(),这就完成了:消息循环->消息路由到窗口过程函数处理。 (4)进入CXXXApp::InitInstance()函数体
中
,对于
单
文档
应用程序,调用ProcessShellCommand(cmdInfo),通过调用该函数就会完成:设计窗口类->注册窗口类->生成窗口。 再接下来就会调用m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();这就完成了:显示窗口->更新窗口。 (5)在函数CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)
中
会进入到如下的case分支:case CCommandLineInfo::FileNew: if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)) (6)进入函数CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo),调用_AfxDispatchCmdMsg(this, nID, nCode, lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo); (7)进入函数AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode, AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo),调用 case AfxSig_vv: // normal command or control notification ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED ASSERT(pExtra == NULL); (pTarget->*mmf.pfn_COMMAND)(); (8)进入CWinApp::OnFileNew(),调用m_pDocManager->OnFileNew();这个函数很特殊,它本身是个消息响应函数,当我们点击ID为ID_FILE_NEW的菜
单
时,会产生一个命令消息,由于命令消息可以被CCmdTarget类及其派生类来捕获,而CWinApp是从CCmdTarget派生出来的,因此可以捕获这个消息。当应用程序创建完成并成功显示后,当我们点击文件菜
单
下的新建菜
单
项时,就会首先进入这个函数,然后再依次执行下去,最后就会执行到pDocument->OnNewDocument()
中
,往往我们会对这个函数不解,不知道它为什么会响应ID_FILE_NEW的命令消息,至此真相大白了。顺便说一句,为什么程序在刚启动的时候,我们并没有点击菜
单
项,为什么会自动的产生这个消息呢?这是因为在CXXXXApp::InitInstance()函数
中
有“CCommandLineInfo cmdInfo;”这个类的构造函数是这样的:CCommandLineInfo::CCommandLineInfo() { m_bShowSplash = TRUE; m_bRunEmbedded = FALSE; m_bRunAutomated = FALSE; m_nShellCommand = FileNew; },因此就会在第(5)步骤的时候进入到“case CCommandLineInfo::FileNew:”这个分支
中
,就相当于产生了这样一个FileNew的消息。同理对于ID为ID_FILE_OPEN(在CWinApp::OnFileOpen()
中
响应)、ID_FILE_SAVE(在CDocument::OnFileSave()
中
响应)等等在
MFC
向导为我们生成的
单
文档
类
中
找不到消息响应的入口时,其实都是在基类CWinApp或者CDocument类
中
进行了响应。对于CXXXXDoc::Serialize(CArchive& ar)函数也是通过ID_FILE_SAVE和ID_FILE_OPEN产生命令消息后就行响应从而才调用该函数的。 (9)进入CDocManager::OnFileNew(),CDocManager类有一个成员变量是CPtrList m_templateList;该变量保存了一个
文档
模版链表指针,在CDocManager::OnFileNew()函数体
中
会调用CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();得到链表
中
的头,也就是第一个
文档
模版,后面就会用得到的这个指针去调用pTemplate->OpenDocumentFile(NULL);紧接着就会有一个判断,用来确定该链表
中
是否只有一项,如果链表
中
保存了多个
文档
模版,则会弹出一个对话框,来让我们选择到底是使用哪一套
文档
模版来构建应用程序,相信大家也都见到过这种情况吧。对了,还有一点要说明的是:pTemplate是一个CDocTemplate的指针,但接下来程序为什么会进入到CSingleDocTemplate::OpenDocumentFile的函数体内呢,这是因为CDocTemplate类
中
的OpenDocumentFile函数被定义为纯虚函数,而CSingleDocTemplate类又是从CDocTemplate类派生出来的,并且实现了该函数,因此就会进入到子类的函数体
中
了。 (10)进入CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible),先调用CreateNewDocument()创建
文档
类,再调用pFrame = CreateNewFrame(pDocument, NULL);创建框架类和视图类,从这里也可以看出
MFC
体系结构
中
文档
、框架、视图“三位一体”的模式,在这一个函数
中
同时创建三个类;再会调用pDocument->OnNewDocument();因此就会进入到子类的
文档
类
中
的pDocument->OnNewDocument()
中
了。 (11)进入CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther),调用if (!pFrame->LoadFrame(m_nIDResource, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, // default frame styles NULL, &context)) (12)进入BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); (13)进入BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister),该函数内部就完成了:设计窗口类->注册窗口类。
MFC
通过给我们提供好一些已经订制好的窗口类,我们不需要自己再设计窗口类,只需要到那些订制好的窗口类“仓库”
中
寻找一种适合我们需要的窗口类就可以了,然后通过AfxRegisterClass函数注册窗口类。还需要说明的是,再后续的跟踪过程
中
,我们会发现还会进入到AfxEndDeferRegisterClass函数
中
进行设计和注册窗口类,这主要是因为
单
文档
应用程序比较特殊,它提前通过这样的一种途径进行了窗口类的设计和注册步骤,其实是应该在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)函数的调用
中
完成窗口类的设计和注册的,这一点我们要清楚,也就是说设计和注册窗口类的正宗发源地应该是PreCreateWindow(CREATESTRUCT& cs)。此外,我们还会注意到在该函数体的前部分有一语句为“wndcls.lpfnWndProc = DefWindowProc;”因此所有窗口类的窗口过程函数都是DefWindowProc,这一点在后面的跟踪
中
可以看到,每次生成窗口之后都会调用几次DefWindowProc函数。也就是说
MFC
都是让我们采用默认的窗口过程函数,这并不是说我们因此就不能使用自己的窗口过程函数实现个性化的消息处理了,
MFC
采用了一种基于消息映射的机制完成了消息个性化处理。 (14)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)
中
,调用LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource); (15)进入LPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource),调用PreCreateWindow(cs); (16)进入BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs),调用CFrameWnd::PreCreateWindow(cs) (17)进入BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));又一次设计和注册窗口类 (18)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)
中
,调用if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext)) (19)进入BOOL CFrameWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext* pContext),调用if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext)) (20)BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),调用if (!PreCreateWindow(cs)) ,接下来调用HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);好了,终于让我们找到生成窗口的地方了——函数::CreateWindowEx! (21)进入int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct),调用if (CFrameWnd::OnCreate(lpCreateStruct) == -1) (22)进入int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs),调用return OnCreateHelper(lpcs, pContext); (23)进入int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (CWnd::OnCreate(lpcs) == -1) (24)进入_AFXWIN_INLINE int CWnd::OnCreate(LPCREATESTRUCT),调用return (int)Default(); (25)进入LRESULT CWnd::Default(),调用return DefWindowProc(pThreadState->m_lastSentMsg.message, pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam); (26)进入LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam),调用return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam); (27)回到int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (!OnCreateClient(lpcs, pContext)) (28)进入BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext),调用if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL) (29)进入CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID),调用if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, pContext)) (30)进入BOOL CWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext),调用return CreateEx(0, lpszClassName, lpszWindowName, dwStyle | WS_CHILD, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext); (31)进入BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),重复生成框架类CMainFrame的过程来生成CXXXView,因为它也是一个窗口类,因此也需要进行那一系列过程才能最终显示更新出来。 调用的顺序是这个样子的:PreCreateWindow(cs)->BOOL CXXXView::PreCreateWindow(CREATESTRUCT& cs)->CView::PreCreateWindow(cs)->VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));->::CreateWindowEx(...)->CWnd::DefWindowProc->::CallWindowProc(...)->...->CXXXView::OnCreate->CView::OnCreate->CWnd::OnCreate->... 写到这里,基本上就清楚了,
中
间的省略号表示的部分大多数都是在与窗口过程函数有关的,因为在生成窗口的时候需要响应一些消息,因此需要调用一些窗口过程函数,每次在调用::CreateWindowEx(...)函数后都会调用一些窗口过程函数,然后再去调用该窗口类对应的OnCreate函数,其实在调用OnCreate函数之前调用CreateWindowEx只是生成了一个窗口,至于这个窗口里面要放置些什么东西,以及该如何装饰该窗口,则就需要由OnCreate来完成了,往往我们都会在OnCreate函数的后面(这样做是为了不影响窗口本身应该布置的格局)添加一些代码,创建我们自己的东西,比如我们通常会在CMainFrame类的OnCreate函数后面放置一些Create代码,来创建我们自己的可停靠的
工具栏
或者
按钮
之类的东西,当然我们也可以在CXXXView类的OnCreate函数的后面添加一些代码,来创建我们需要的东西,比如
按钮
之类的东西。在完成了从设计、注册到生成窗口的过程之后,往往还需要显示更新,有些时候,我们不必要每次都显示的调用CWnd的ShowWindow和UpdateWindow两个函数,我们可以在创建的时候,给窗口风格
中
添加WS_VISIBLE即可,因此有些时候会跟踪不到ShowWindow和UpdateWindow两个函数这两个函数,因为窗口在创建的时候就可见了。 总的来说,先初始化应用类,然后注册生成框架类,然后再注册生成视图类,然后注册生成视图类OnCreate函数后面用户添加的、用Create来准备创建的窗口,然后再注册生成框架类的OnCreate函数后面需要生成的m_wndToolBar、m_wndStatusBar以及我们自己添加的要创建的窗口类,最后在回到应用类的初始化的函数体
中
,调用框架类的显示和更新函数,然后再进入由框架类定义的窗口的消息循环
中
。 消息循环的过程是这个样子的: (1)调用int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)函数
中
的pThread->Run() (2)进入int CWinApp::Run(),调用return CWinThread::Run(); (3)进入int CWinThread::Run(),调用if (!PumpMessage()) (4)进入BOOL CWinThread::PumpMessage(),调用if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) (5)回到BOOL CWinThread::PumpMessage(),调用::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur); (6)回到int CWinThread::Run(),调用while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); (7)再重复(4)-(6)的步骤 下面给出int CWinThread::Run()
中
消息循环的部分代码: do { // pump message, but quit on WM_QUIT if (!PumpMessage()) return ExitInstance(); // reset "no idle" state after pumping "normal" message if (IsIdleMessage(&m_msgCur)) { bIdle = TRUE; lIdleCount = 0; } } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); 这段代码其实本质上与我们基于Win32 SDK手写的代码: //消息循环 MSG msg; while(GetMessage(&msg,NULL,0,0)) { //简
单
的说,函数TranslateMessage就是把WM_KEYDOWN和WM_KEYUP翻译成WM_CHAR消息,没有该函数就不能产生WM_CHAR消息。 TranslateMessage(&msg); ::DispatchMessage(&msg); } 是一致的。 1,寻找WinMain人口: 在安装目录下找到
MFC
文件夹下的SRC文件夹,SRC下是
MFC
源代码。 路径:
MFC
|SRC|APPMODUL.CPP: _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 注意:(#define _tWinMain WinMain) 2,对于全局对象或全局变量来说,在程序运行即WINMAIN函数加载的时候,已经为全局对象或全局变量分配了内存和赋初值。 所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_tWinMain(){} 说明:每一个
MFC
程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。在WIN32程序当
中
,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于
MFC
应用程序
中
,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。 3,通过构造应用程序对象过程
中
调用基类CWinApp的构造函数,在CWinApp的构造函数
中
对程序包括运行时一些初始化工作完成了。 CWinApp构造函数:
MFC
|SRC|APPCORE.CPP CWinApp::CWinApp(LPCTSTR lpszAppName){...}//带参数,而CTEApp构造函数没有显式向父类传参,难道CWinApp()有默认参数?见下: (在CWinApp类定义
中
, CWinApp(LPCTSTR lpszAppName = NULL); ) 注意:CWinApp()函数
中
: pThreadState->m_pCurrentWinThread = this; pModuleState->m_pCurrentWinApp = this (this指向的是派生类CTEApp对象,即theApp) 调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){} 4,_tWinMain函数
中
通过调用AfxWinMain()函数来完成它要完成的功能。(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,我们可根据这些类函数来设计自己的应用程序)。 AfxWinMain()函数路径:
MFC
|SRC|WINMAIN.CPP: 在AfxWinMain()函数
中
: CWinApp* pApp = AfxGetApp(); 说明:pApp存储的是指向WinApp派生类对象(theApp)的指针。 //_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() // { return afxCurrentWinApp; } 调用pThread->InitInstance() 说明:pThread也指向theApp,由于基类
中
virtual BOOL InitApplication()定义为虚函数,所以调用pThread->InitInstance()时候,调用的是派生类CTEApp的InitInstance()函数。 nReturnCode = pThread->Run(); 说明:pThread->Run()完成了消息循环。 5,注册窗口类:AfxEndDeferRegisterClass(); AfxEndDeferRegisterClass()函数所在文件:
MFC
|SRC|APPCORE.CPP BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...} 说明:设计窗口类:在
MFC
中
事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。 调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}//进入程序 ->AfxWinMain();->pApp->InitApplication();->pThread->InitInstance()//父类InitInstance虚函数;->CTEApp::InitInstance()//子类实现函数;->AfxEndDeferRegisterClass(LONG fToRegister)//注册所选择的窗口类(出于
文档
管理,注册提前,正常的应在PreCreateWindow
中
进行注册)//之后进入创建窗口阶段(以下再不做调试) 6,PreCreateWindow()://主要是注册窗口类 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; return TRUE; } 说明: CFrameWnd::PreCreateWindow()函数所在文件:
MFC
|SRC|WINFRM.CPP BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) { if (cs.lpszClass == NULL) { VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); //判断AFX_WNDFRAMEORVIEW_REG型号窗口类是否注册,如果没有注册则注册 cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background //把注册后的窗口类名赋给cs.lpszClass } if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4) cs.style |= FWS_PREFIXTITLE; if (afxData.bWin4) cs.dwExStyle |= WS_EX_CLIENTEDGE; return TRUE; } 其
中
: virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//PreCreateWindow()是个虚函数,如果子类有则调用子类的。 #define VERIFY(f) ASSERT(f) #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass) define AFX_WNDFRAMEORVIEW_REG 0x00008 const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP文件
中
,定义为全局数组。 //#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView") 7,创建窗口: Create()函数路径:
MFC
|SRC|WINFRM.CPP: CFrameWnd::Create(...){ ... CreateEx(...);//从父类继承来的,调用CWnd::CreateEx(). ... } CWnd::CreateEx()函数路径:
MFC
|SRC|WINCORE.CPP BOOL CWnd::CreateEx(...){ ... if (!PreCreateWindow(cs))//虚函数,如果子类有调用子类的。 { PostNcDestroy(); return FALSE; } ... HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); ... } 说明:CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。PreCreateWindow(cs))//是虚函数,如果子类有调用子类的。 HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); typedef struct tagCREATESTRUCT { // cs LPVOID lpCreateParams; HINSTANCE hInstance; HMENU hMenu; HWND hwndParent; int cy; int cx; int y; int x; LONG style; LPCTSTR lpszName; LPCTSTR lpszClass; DWORD dwExStyle; } CREATESTRUCT; 8,显示和更新窗口: CTEApp类,TEApp.cpp
中
m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口,m_pMainWnd指向框架窗口 m_pMainWnd->UpdateWindow();//更新窗口 说明: class CTEApp : public CWinApp{...} class CWinApp : public CWinThread{...} class CWinThread : public CCmdTarget { ... public: CWnd* m_pMainWnd; ... ... } 9,消息循环: int AFXAPI AfxWinMain() { ... // Perform specific initializations if (!pThread->InitInstance()){...} //完成窗口初始化工作,完成窗口的注册,完成窗口的创建,显示和更新。 nReturnCode = pThread->Run(); //继承基类Run()方法,调用CWinThread::Run()来完成消息循环 ... } //////////////////////////////////////////////////////////////// CWinThread::Run()方法路径:
MFC
|SRC|THRDCORE.CPP int CWinThread::Run() { ... // phase2: pump messages while available do//消息循环 { // pump message, but quit on WM_QUIT if (!PumpMessage())//取消息并处理 return ExitInstance(); ... } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); ... } 说明: BOOL PeekMessage(,,,,)函数说明 The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure. If a message is available, the return value is nonzero. If no messages are available, the return value is zero. ///////////////////////////////////////////////////////////// BOOL CWinThread::PumpMessage() { ... if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取消息 {...} ... // process this message if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) { ::TranslateMessage(&m_msgCur);//进行消息(如键盘消息)转换 ::DispatchMessage(&m_msgCur);//分派消息到窗口的回调函数处理(实际上分派的消息经过消息映射,交由消息响应函数进行处理。) } return TRUE; } 9,
文档
与视结构: 可以认为View类窗口是CMainFram类窗口的子窗口。 DOCument类是
文档
类。 DOC-VIEW结构将数据本身与它的显示分离开。
文档
类:数据的存储,加载 视类:数据的显示,修改 10,
文档
类,视类,框架类的有机结合: 在CTEApp类CTEApp::InitInstance()函数
中
通过
文档
模板将
文档
类,视类,框架类的有机组织一起。 ... CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CTEDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CTEView)); AddDocTemplate(pDocTemplate);//增加到模板
界面
15,978
社区成员
115,896
社区内容
发帖
与我相关
我的任务
界面
VC/MFC 界面
复制链接
扫一扫
分享
社区描述
VC/MFC 界面
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章