第三章

3.1创建MFC AppWizard

如何利用vs2019创建MFC应用见参考文献[1]
需要注意的地方有
[1] 创建MFC单文档应用程序

[2]开启类视图窗口

3.2基于MFC的程序框架剖析

在MFC中,类的命名都以C开头;对于单文档应用程序,都有:

  • CAboutDlg帮助类,同于说明这个工程的开发信息;
  • CMainFrame主框架类;
  • C工程名App应用程序入口;
  • C工程名Doc文档类,用来管理、存放数据;
  • C工程名View用来将文档中的数据可视化。

CMainFrame类和CTestView类都有一个共同的基类:Cwnd类,其封装了与窗口相关的操作。

3.2.1 MFC中的WinMain函数

文件路径(在安装路径下直接搜索MFC,找到mfc):D:\Program Files (x86)\visualstudio\VC\Tools\MSVC\14.29.30037\atlmfc\src,打开appmodul.cpp 查找WinMain

extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,     //右键_tWinMain,单击转到定义:#define _tWinMain   WinMain_In_ LPTSTR lpCmdLine, int nCmdShow)
#pragma warning(suppress: 4985)
{// call shared/exported WinMainreturn AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}

3.2.1.1 theApp全局对象

以简单的C++源程序为例,在入口函数main()加载之前,就已经为全局变量(对象)分配了内存空间,并为其赋了初值。对于一个全局对象来说,此时就会调用该对象的构造函数构造该对象并进行初始化操作,然后才是进入main()函数。(P72例3-4已标出先后顺序)

对于MFC来说,通过产生一个应用程序类的对象来唯一标识应用程序的实例。每个MFC程序有且仅有一个从应用程序类(CWinApp)派生的类;每个MFC程序实例有且仅有一个该派生类的实例化对象,即theApp全局对象,theApp表示了该应用程序本身。theApp的定义如程序3.1所示,在test.cpp中查看。
程序3.1 theApp全局对象

/*test.cpp*/
// 唯一的 CtestApp 对象
CtestApp theApp;    //theApp是CtestApp的一个对象,注意其是一个全局对象/*test.h*/
class CtestApp : public CWinApp   //CtestApp继承于CWinApp,后者表示应用程序类
{......
}

文件路径(在安装路径下直接搜索MFC,找到mfc):D:\Program Files (x86)\visualstudio\VC\Tools\MSVC\14.29.30037\atlmfc\src,打开appcore.cpp 查找CWinApp(184行)
其中,
程序3.2

CWinApp::CWinApp(LPCTSTR lpszAppName)  //注意此处有参数
{ ...
pModuleState->m_pCurrentWinApp = this; //此处this代表子类CTestApp的对象,即theApp...
}

程序3.3

class CWinApp : public CWinThread
{...explicit CWinApp(LPCTSTR lpszAppName = NULL);     // app name defaults to EXE name;构造函数形参有默认值默认值...
};

补充:如果某个函数的参数有默认值,那么在调用该函数时可以传参,也可以不传参直接使用默认值。

由程序3.3可见,来CWinApp类的定义时,CWinApp的构造函数的形参有默认值NULL。因此,在调用CWinApp类的构造函数时,不用显式地传参。

3.2.1.2 AfxWinMain函数

WinMain函数实际上是通过调用AfxWinMain函数来完成它的功能的。
其中,Afx前缀的函数代表应用程序框架函数,辅助我们生成应用程序框架的应用模型。在MFC中,Afx为前缀的函数都是全局函数,可以在程序的任何位置调用。

在AfxWinMain函数的定义中,有
程序3.4

/*pThread和pApp这两个指针是一致的,这两个指针都指向CTestApp类的对象,即theApp全局对象*/ CWinThread* pThread = AfxGetThread();CWinApp* pApp = AfxGetApp();/*MFC内部管理所调用的函数*/if (pApp != NULL && !pApp->InitApplication())goto InitFailure;/*调用的是子类InitInstance():因为在父类CWinApp中的InitInstance()是虚函数*/if (!pThread->InitInstance()){}/*消息循环*/nReturnCode = pThread->Run();

3.2.1.3 InitInstance函数

见程序3.4的第九行。

3.2.2 MFC框架窗口

3.2.2.1 设计和注册窗口

窗口类的注册是由wincore.cpp的AfxEndDeferRegisterClass函数完成的。AfxEndDeferRegisterClass函数预定义了几种缺省的窗口类,首先判断窗口类的类型,再赋予其相应的类名。部分代码如书本p79所示。

接着调用AfxRegisterClass函数注册从窗口类,该函数首先获得窗口类信息,窗口已经注册,返回真。否则注册该窗口类。

注意:AfxRegisterClass实际上就是AfxEndDeferRegisterClass(宏定义);

3.2.2.2 创建窗口

窗口的创建是由CWnd类中的CreateEx函数完成的。定义:afxwin.h,实现:wincore.cpp。(以Ex结尾的函数表示扩展函数。)

CreateEx函数不是虚函数,CFrameWnd类的Create函数内调用的实际上就是CWnd类的CreatEx函数。

CreateEx函数内部调用的PreCreateWindow函数是一个虚函数,在产生窗口之前有机会修改窗口外观。

3.2.2.3 显示和更新窗口

CTestApp中名为m_pMainWnd的成员变量保存了应用程序框架窗口(CMainFrame)对象的指针,在InitInstance函数(初始化工作:注册、显示、更新)内部:
程序3.5

// 唯一的一个窗口已初始化,因此显示它并对其进行更新m_pMainWnd->ShowWindow(SW_SHOW);//显示m_pMainWnd->UpdateWindow();//更新

3.2.3 消息循环

见程序3.4的第12行。
在thrdcore.cpp中/*消息循环*/ nReturnCode = pThread->Run();

书本p85例3-16,该函数主要结构是一个for循环,在收到WM_QUIT时退出。在循环中的PumpMessage()与第一章的SDK编程的消息处理代码一致。

3.2.4 MFC运行过程梳理

3.3 窗口类、窗口对象与窗口

3.3.1 三者之间的关系

:: 前面没有东西,表示所使用的函数是一个全局函数。如果当前定义的成员函数与内部调用的API函数重名,后者必须加 :: ,否则报错。

C++窗口类对象与窗口并不是一回事,他们之间唯一的关系是C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个C++窗口类对象相关的那个窗口的句柄。窗口销毁时,与之对应的C++窗口类对象销毁与否要看其生命周期是否结束。但C++窗口类对象销毁时,与之相关的窗口也将销毁。

换句话说,在窗口销毁后,CWnd的成员变量m_hWnd设为NULL,并没有被销毁(也有可能被销毁:对象生命周期结束(函数运行到右大括号“}”));而在C++窗口类对象析构时,窗口被销毁。

在系统文件afxwin.h中,CWnd已有一个用于保存句柄的成员变量m_hWnd,ShowWindow()和UpdateWindow()不需要再传递这个句柄,因为它已经是成员变量。
程序3.6

/*afxwin.h*/
class CWnd : public CCmdTarget
{DECLARE_DYNCREATE(CWnd)
protected:static const MSG* PASCAL GetCurrentMessage();// Attributes
public:HWND m_hWnd;            // must be first data member......};/*lesson3:\\wainmain.cpp*/
int WINAPI WinMain(  HINSTANCE hInstance,      // handle to current instanceHINSTANCE hPrevInstance,  // handle to previous instanceLPSTR lpCmdLine,          // command lineint nCmdShow              // show state
)
{//设计窗口类...//注册窗口类...//创建窗口类CWnd wnd;wnd.CreateEx(...);wnd.ShowWindow(SW_SHOWNORMAL);wnd.UpdateWindow();/*对比第一章:创建窗口HWND hwnd;hwnd = CreateWindowEx();显示及刷新窗口::ShowWindow(hwnd, SW_SHOWNORMAL);::UpdateWindow(hwnd);注意ShowWindow和UpdateWindow的参数,原因是:CWnd类定义过了一个HWND类型的成员变量m_hWnd用于保存这个窗口的句柄,在调用CWnd类中的ShowWindow显示窗口时,就不在需要传递这个句柄了,因为它已经是成员变量了,该函数可以直接使用它。在窗口销毁后,CWnd的成员变量m_hWnd设为NULL,并没有被销毁;而在C++窗口类对象析构时,窗口被销毁。*///消息循环...   return 0;
}

3.3.2 在窗口中显示按钮

CButton的Create函数声明:
BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

  • lpszCaption:按钮文本;
  • dwStyle:按钮风格+窗口风格;
  • rect:定义一个矩形区域;
  • pParentWnd:指定父窗口。MFC不再通过窗口句柄,而是通过一个与窗口相关的C++窗口类对象指针来传递窗口对象。
  • nID:按钮控件标识。可取整数随机值。在框架窗口产生之后,再创建该标识,否则没地方放置。

如果将按钮创建在CMainFrame::OnCreate()函数内,按钮的父窗口是主框架窗口,此时按钮遮挡住了保存等按钮。

改为在CTestView.cpp中创建button,首先在testView.cpp中创建OnCreate函数,步骤如问题及反思[3]所示。运行结果如下。

而将m_btn.Create()中的this改为GetParent(),运行结果变为

可见,按钮的位置与其父窗口有关,与创建它的代码所在的类无关。

将按钮窗口销毁,m_btn并没有销毁,因为m_btn是CTestView类的一个成员变量,其生命周期与CTestView对象一致。

课后程序

/*testview.h*/
class CtestView : public CView
{...
private:CButton m_btn;  //在定义类的成员变量时都以"m_"为前缀,表明这个变量是类的成员变量
};/*testview.cpp*/
int CtestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{if (CView::OnCreate(lpCreateStruct) == -1)return -1;/*CButton的Create函数声明:BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );*/// m_btn.Create(_T("button"), WS_CHILD | BS_DEFPUSHBUTTON, CRect(0, 0, 100, 100), this, 123);
//  m_btn.ShowWindow(SW_SHOWNORMAL);//窗口显示m_btn.Create(_T("button"), WS_CHILD | BS_DEFPUSHBUTTON |WS_VISIBLE , CRect(0, 0, 100, 100), this, 123);
//  m_btn.Create(_T("button"), WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, CRect(0, 0, 100, 100), GetParent(), 123);/*"按钮":名称; CRect(0,0,100,100):矩形区域; 123:ID号。*WS_CHILD(窗口风格):The window is a child window. A window with this style cannot have a menu bar.BS_DEFPUSHBUTTON(按钮风格):下按按钮风格WS_VISIBLE:The window is initially visible.*this指针(代表对象本身)GetParent():Call this function to get a pointer to a child window's parent window*//*C2664 “BOOL CButton::Create(LPCTSTR,DWORD,const RECT &,CWnd *,UINT)”: 无法将参数 1 从“const char [7]”转换为“LPCTSTR”   test    E:\VCProject\Lesson3\test\test\MainFrm.cpp  68解决方法:方法1、"button"改为_T("button")[2]方法2、调试>>XXX调试属性>>配置属性>>高级>>高级属性>>字符集,改为:使用多字节字符集[3]*/return 0;
}

运行结果:

问题及反思

[1]如何利用vs2019创建MFC应用见参考文献[1]
[2]C2664 “BOOL CButton::Create(LPCTSTR,DWORD,const RECT &,CWnd *,UINT)”: 无法将参数 1 从“const char [7]”转换为“LPCTSTR”
解决方法:
方法1、"button"改为_T(“button”)
方法2、调试>>XXX调试属性>>配置属性>>高级>>高级属性>>字符集,改为:使用多字节字符集
[3]vs2019为一个类添加某函数的方法如下所示。第四步单击最右侧向下小三角,选择Add OnCreate。

参考文献

[1] <https://blog.csdn.net/m0_37062716/article/details/113827243?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control >.安装MFC,创建MFC工程文件
[2]<https://blog.csdn.net/huijie4728/article/details/50487315> . 问题及反思[2]
[3]<https://blog.csdn.net/feilong911hao/article/details/39231533?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control> . 问题及反思[2]
[4]孙鑫.VC++深度详解修订版[M]. 北京:电子工业出版社, 2012. 63-99.


孙鑫VC++深入详解第三章学习笔记相关推荐

  1. 孙鑫VC++深入详解第二章学习笔记

    第二章 掌握C++ 2.1 从结构到类 2.1.1 结构体的定义 C++相比于C的特性:封装性.继承性.多态性: 对象具有状态和行为,状态保存在成员变量中,行为通过函数实现: 标准输入输出流对象:ci ...

  2. 孙鑫VC++深入详解第一章学习笔记

    第一章 Windows程序内部运行机制 1.1 API和SDK API:Windows操作系统提供给应用程序编程的接口. SDK(软件开发包):用于开发的所有资源的集合. 1.2 窗口和句柄 窗口 句 ...

  3. 孙鑫VC++深入详解笔记

    前言:最近感觉技术提升提来很吃力,主要还是因为以前的基础没有打牢,特别是多线程和数据库方面,所有准备重新学习一下基础. 如下文章转载自:http://www.cnblogs.com/gaojun/ar ...

  4. 孙鑫VC++深入详解:Lesson6 Part2 -- MFC菜单更新机制 用该机制实现 Enable or Disable MenuItem

    MFC菜单命令更新机制---用该机制实现 Enable or Disable  MenuItem 方法: 1)用资源中的菜单项"剪切"的ClassWizard添加一个UPDATE_ ...

  5. 孙鑫VC++深入详解:Lesson6 Part3 ---创建右键弹出菜单 TrackPopupMenu

    1 -----增加右键弹出菜单方法一:      Project-->Add to Project-->Components and Controls -->Visual C++ C ...

  6. 工程伦理第三章学习笔记2020最新

    工程伦理第三章学习笔记2020最新 因为之前自己在网上找答案总是觉得费劲,一道一道的找,很慢,突然找到了前两章的答案,感觉有一种前人种树后人乘凉的感觉,于是自己在艰难找完第三章习题并全对的情况下,将题 ...

  7. 多维随机变量及其分布——《概率论及其数理统计》第三章学习笔记

    多维随机变量及其分布--<概率论及其数理统计>第三章学习笔记 文章目录 多维随机变量及其分布--<概率论及其数理统计>第三章学习笔记 前言 MindMap 二维随机变量 定义与 ...

  8. 周志华西瓜书第三章学习笔记

    第三章学习笔记 文章目录 第三章学习笔记 1.知识脉络 2.我的笔记 参考 1.知识脉络 2.我的笔记 这一章公式推导实在太多了,需要补充的推导过程也有很多,就不写电子档了.扩展公式推导和LDA部分补 ...

  9. 各种音视频编解码学习详解之 编解码学习笔记(三):Mpeg系列——Mpeg 1和Mpeg 2

    最近在研究音视频编解码这一块儿,看到@bitbit大神写的[各种音视频编解码学习详解]这篇文章,非常感谢,佩服的五体投地.奈何大神这边文章太长,在这里我把它分解很多小的篇幅,方便阅读.大神博客传送门: ...

最新文章

  1. IO-5(InputStreamReader、OutputStreamWriter、序列化流、反序列化流、Serializable、transient)
  2. 反向代理与Real-IP和X-Forwarded-For(转)
  3. iPhone 12无线充电模块曝光:AirPower有戏了!
  4. bytebuf池_netty源码解析(4.0)-26 ByteBuf内存池:PoolArena-PoolSubpage
  5. 证明CLIQUE(团问题)是NP完全
  6. linux卸载java rpm_详解Linux中查看jdk安装目录、Linux卸载jdk、rpm命令、rm命令参数...
  7. Linux 实现 Google Authenticator 动态密码 + SSH 密码双重认证
  8. matlab powergui在哪儿,powergui模块在哪
  9. 《使用机器视觉从照片中确定西瓜质量》论文笔记
  10. 【数学】用C语言实现函数的定积分—— 把 “定积分定义计算出的值” 和 “牛顿-莱布尼兹公式计算出的值” 两者进行误差比较
  11. 基于贪婪的高效Lidar-SLAM特征选择(ICRA2021)
  12. 兔子繁殖为例 c语言,用斐波那契数列解答兔子的繁殖
  13. group normalization
  14. py实现外星人入侵(二次开发)——2.添加音乐
  15. POJ - 1723 Soldiers 士兵站队 排序+中位数
  16. 【JAVA】 容纳对象 Set
  17. 计算机教室怎么样布置,如何布置计算机教室
  18. Jmeter+Jenkins+Dingding遇到的问题(终极篇)
  19. win10下anaconda+spyder+keras 下载与设置大全(汉化、环境变量设置、DPI缩放等)
  20. 三极管和MOS管驱动电路的正确用法

热门文章

  1. MAC系统字体库存放目录
  2. php单位有哪些,css中的角度单位有哪些?
  3. 活体检测Face Anti-spoofing前世今生:作者(Fisher Yu )
  4. Unity游戏开发客户端面经——Unity(初级)
  5. html 好看的数据表格,CSS制作好看的网页表格
  6. torch.Tensor.requires_grad属性的使用说明
  7. 矩阵论笔记(二)——线性变换
  8. java获取输入的地点的经纬度和编码等信息
  9. python判断是工作日还是休息日
  10. TI-Nspire CX CAS图形计算器模拟器+操作指南