MFC框架程序剖析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MFC框架程序剖析相关的知识,希望对你有一定的参考价值。

一、           MFC

MFC(Microsoft Foundation Class,微软基础类库)是微软为了简化程序员的开发工作所开发的一套C++类的集合,是一套面向对象的函数库,以类的方式提供给用户使用。利用这些类,可以有效发帮助程序员完成Windows应用程序的开发

 

二、           theAPP

  1. theApp代表应用程序实例。
  2. 在C×××App类中 ,有 theApp 这个变量,它是CWinApp类的派生类的对象,是一个全局变量。
  3. 全局变量在WinMain()前被创建。

 

三、           InitInstance()

  1. InitInstance()与WinMain

BOOL InitInstance()是MFC的CWinApp类的成员函数,而WinMain才是真正的入口点,但是MFC不允许程序中有WinMain这个函数,因为MFC自己编写了WinMain函数,如果程序中再定义就重复定义了,而MFC编写的的WinMain函数则调用了CWinApp::InitInstance函数,所以InitInstance看起来似乎就是MFC程序的入口点。

  1. InitInstance是虚函数,继承类必须提供它的实现代码。通常,重载InitInstance以构造主窗口对象并设置CWinThread::m_pMainWnd数据成员,使其指向这个窗口。
  2. WINDOWS环境下面可以运行同一程序的多个实例,函数InitInstance的作用就是在生成的一个新的实例的时候,完成一些初始化的工作。
  3. InitApplication和InitInstance

它们是MFC的CWinApp的两个虚函数,前者负责“每一个程序只做一次”的操作,后者负责“每一个例程都得做一次”的操作。“每一个程序”指的是进程的概念。“每一个例程”指的是线程的概念。所以InitApplication负责“每一个程序只做一次”的操作。而InitInstance是每一个线程都具有的函数,只不过很多情况下,自己开的线程没有重载该函数。使用CWinThread或者AfxBeginThread创建线程,新的线程会执行新建CWinThread对象的InitInsatnce函数一次。不过这和CWinApp没有关系。

  1. InitInstance() 和 OnInitDialog()

OnInitDialog()是初始化对话框,响应WM_INITDIALOG消息。Dialog相关子窗口就可以放在这里初始化。

 

四、           MFC程序的运行步骤

  1. CXXApp中的全局变量定义CXXApp  theApp;
  2. 调用CXXApp构造函数,从而调用其基类CwinApp的构造函数,后者完成应用程序的初始化工作,将应用程序的指针保存起来。
  3. 进入Winmain函数(_tWinMain为宏),即MFC程序的入口函数。实际调用的是AfxWinMain函数。

1)        调用全局AfxGetApp()函数获得应用程序对象。

2)        调用CWinApp::InitApplication成员函数,这个成员函数用来初始化应用程序对象当中的关于文档部分的内容。

3)        调用CWinApp::InitInstance成员函数。

完成窗口类的注册:AfxEndDeferRegisterClass;

完成窗口的创建:CWnd::CreateEx;

完成窗口的显示与更新:CWnd::ShowWindow;CWnd::UpdateWindow。

开启消息循环消息处理并且分发给相关的窗口对象的DefWindowProc成员函数:CWinApp::Run。

  1. 当接收到WM_QUIT消息时,结束消息循环,退出程序。

 

五、           窗口类的对象和窗口

C++窗口类对象与窗口并不是一回事, C++窗口类对象内部定义了一个窗口句柄变量m_hWnd,保存了与这个C++窗口类对象相关的那个窗口的句柄。

窗口销毁时,与之对应的C++窗口类对象销毁与否,要看其生命周期是否结束。

窗口类对象销毁时,与之相关的窗口也将销毁。

 

六、           快捷键

 

Ctrl+H

替换

Ctrl+]

       寻找下一半括弧

F7

Build(编绎并链接成exe文件)

Ctrl+F7

Compile(编译)

Ctrl+F5

Execute(编译+链接+运行)

F5

Go(顺序执行)

F11

step into (顺序执行,进入循环或函数)

F10

step over(顺序执行,不进入循环或函数)

Ctrl+F10

Run to cursor(自动执行到用户光标所指的语句前)

Shift+F5

Stop Debugging(停止调试)

 

七、           窗口创建相关的函数

  1. 窗口创建过程:

在窗口的CWnd::Create()/CreateEx()调用返回前,要调用PreCreateWindow(),对Create()/CreateEx()中的风格参数进行预处理设置。之后调用OnCreate进行实际的窗口创建,返回后,Create()/CreateEx()也返回,窗口创建结束。

  1. 当一个应用程序通过Create函数请求创建窗口时发送WM_CREATE消息。产生时间是窗口创建之后显示之前,必须是不进队列消息。
  2. OnCreate是它的消息响应函数。用来“表示一个窗口正在生成”。可以在OnCreate函数里实现我们要在窗口里面增加的东西,例如按扭,状态栏,工具栏等。这些子窗口一般是定义成类中的一个成员变量,因为要保证生命周期。

OnCreate()不产生窗口,只是在窗口显示前设置窗口的属性如风格、位置等,Create()负责注册并产生窗口。

  1. PreCreateWindow

PreCreateWindow()是一个虚函数,顾名思义就是在窗口创建之前执行的,它的主要任务是对所要创建的窗口进行一些风格或者其他方面的初始化或者说是设置。

  1. OnCreate是对话框在被创建时的消息,这时候对话框还没有被显示在屏幕上。而且对话框中的控件都还没有被创建。

OnInitDialog()是对话框创建完成,即对话框上的控件也全部被创建后第一次激活显示在屏幕上产生的消息。在此时可以对话框中的控件进行初始化操作。

 

八、           对话框程序

  1. 包含三个类:分别是CAboutDlg, CXXXApp, CXXXDlg。
  2. CXXXApp:派生于CwinApp;负责管理整个应用程序,定义全局对象 the App,重写InitInstance()函数。
  3. CXXXDlg:派生于CDialog;

写了以下函数:

(1)        CXXXDlg (CWnd* pParent = NULL); 自定义的构造函数;

(2)        virtual BOOL OnInitDialog(); 对话框初始化消息操作函数;在对话框创建之后,第一次显示之前调用。

(3)        afx_msg void OnSysCommand(UINT nID, LPARAM lParam); 系统菜单消息响应函数;

(4)        afx_msg void OnPaint(); 对话框重绘响应函数;

(5)        afx_msg HCURSOR OnQueryDragIcon(); 最小化图标询问响应函数

 

在CDialogDlg类的定义体中有这么一个枚举的定义:  

enum { IDD = IDD_DIALOG_DIALOG };

它表明这个CDialogDlg类使用的对话框模板是:IDD_DIALOG_DIALOG。

 

九、           单文档程序

  1. 包含五个类:分别是CAboutDlg,CMainFrame, CXXXApp, CXXXDoc,CXXXView。
  2. CXXXApp:派生于CwinApp;负责管理整个应用程序,定义全局对象 the App,重写InitInstance()函数。

有这样一段代码,用来注册单文档的文档窗口、SDI架构主窗体和视图窗口:

CSingleDocTemplate* pDocTemplate;

pDocTemplate = new CSingleDocTemplate(

    IDR_MAINFRAME,

    RUNTIME_CLASS(CXXXDoc),

    RUNTIME_CLASS(CMainFrame),       // main SDI frame window

    RUNTIME_CLASS(CXXXView));

AddDocTemplate(pDocTemplate);

这段代码指明了文档类、架构类和视图类,从而构造了一个单文档视图结构的应用程序。

  1. CXXXDoc:派生于CDocument,文档类是用来存取数据的。其中的Seriallize()方法可以存取数据。
  2. CXXXView:派生于CView,视图类负责显示程序数据及用户才操作。

在这个类中,最常用的函数是OnDraw(),这个函数的作用是重绘文档显示的内容,当窗口首次生成、改变大小、拖动的时候,程序都会调用这个方法来重绘界面。

另外一个常用的函数是GetDocumnet,这个函用来获取文档的指针,便于访问文档中的数据。函数OnDraw的代码如下:

void CWriterView::OnDraw(CDC* pDC)

{

    CWriterDoc* pDoc = GetDocument();

    ASSERT_VALID(pDoc);

    // TODO: add draw code for native data here

}

在这个函数中,首先调用GetDocument函数获取到一个文档指针,从而通过pDoc 访问文档中的数据。

  1. CMainFrame:派生于CFrameWnd,框架窗口类负责管理窗口中的菜单、工具栏、状态栏等。通常在OnCreat函数中创建工具栏、状态栏。

 

十、           多文档程序

  1. 包含六个类:分别是CAboutDlg,CMainFrame,CChildFrame,CXXXApp, CXXXDoc,CXXXView。
  2. CXXXApp:派生于CwinApp;负责管理整个应用程序,定义全局对象 the App,重写InitInstance()函数。

有这样一段代码,用来注册多文档的文档窗口、SDI架构主窗体和视图窗口:

CMultiDocTemplate* pDocTemplate;

pDocTemplate = new CMultiDocTemplate(IDR_MDTYPE,

RUNTIME_CLASS(CMDDoc),

RUNTIME_CLASS(CChildFrame), // custom MDI child frame

RUNTIME_CLASS(CMDView));

if (!pDocTemplate)

     return FALSE;

AddDocTemplate(pDocTemplate);

  1. CChildFrame:派生路线:CChildFrame >CMDIChildWnd------->CFrameWnd->CWnd->CCmdTarget
  2. CMainFrame派生路线:

CmainFrame- >CMDIFrameWnd------->CFrameWnd->CWnd->CCmdTarget.

  1. 主窗口与子窗口的关系:

没有子窗口ChildFrame(包括视图)时,显示的是主窗口MainFrame的菜单及工具栏。如果用户新建了一个视图(如打开一个文件),则显示的是子窗口ChildFrame的菜单及工具栏,而主窗口MainFrame的菜单及工具栏被子窗口ChildFrame覆盖了。

  1. App是应用域,所有的域中的东西都可以通过全局函数访问到它。

MainFrame是主框架,也基本可以用全局函数访问到。

MainFrame下是若干个ChildFrame,ChildFrame中若干个View和Document(可能不成对),ChildFrame管理着View,View和Document进行互操作。

因此整体框架就出来了,一般除了直接应用的关系都可以通过MainFrame-->Active ChildFrame-->Active View-->Document这条线进行访问。

  1. 不同类中获取指针的方法:

 技术分享

 

 

十一、   其他

  1. 以Afx为前缀的代表应用程序框架函数,也是全局函数,可以在程序的任意位置调用。

以Ex结尾的都表示扩展函数。

  1. CAboutDlg定义了ABOUT对话框。帮助对话框,提供程序的相关信息,如版本号。
  2. 主框架窗口就是整个应用程序外框所包含的部分。

视类窗口指的是主框架窗口中的空白部分。

 

以上是关于MFC框架程序剖析的主要内容,如果未能解决你的问题,请参考以下文章

MFC程序执行过程剖析(转)

MFC实现原理剖析

二. MFC框架程序分析--Windows编程课程学习笔记

二. MFC框架程序分析--Windows编程课程学习笔记

DIY一个MFC程序

SpringBoot整合SSM三大框架源码剖析之SpringBoot源码剖析