鸡啄米MFC教程笔记之二:MFC应用程序框架分析

  这一节鸡啄米就为大家分析下MFC应用程序框架的运行流程

.SDK应用程序与MFC应用程序运行过程的对比

程序运行都要有入口函数,在之前的C++教程中都是main函数,而Windows应用程序的入口函数是WinMain函数MFC程序也是从WinMain函数开始的。下面鸡啄米就给出用Windows SDK写的“HelloWorld”程序,与MFC应用程序框架进行对比,这样能更好的了解框架是怎样运行的。Windows SDK开发程序就是不使用MFC类库,直接用Windows API函数进行软件开发。鸡啄米不是要讲解SDK开发,只是为了对比而简单介绍,至于SDK开发可以在大家学完MFC以后选择是否要研究,一般来说有简单了解就可以了。



SDK应用程序

首先,给出Windows SDK应用程序“HelloWorld”的源码

 1 #include <windows.h>
 2
 3 LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);
 4
 5 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
 6 {
 7   const static TCHAR appName[] = TEXT("Hello world");
 8   WNDCLASSEX myWin;
 9   myWin.cbSize = sizeof(myWin);
10   myWin.style = CS_HREDRAW | CS_VREDRAW;
11   myWin.lpfnWndProc = myWndProc;
12   myWin.cbClsExtra = 0;
13   myWin.cbWndExtra = 0;
14   myWin.hInstance = hInstance;
15   myWin.hIcon = 0;
16   myWin.hIconSm  = 0;
17   myWin.hCursor = 0;
18   myWin.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
19   myWin.lpszMenuName = 0;
20   myWin.lpszClassName = appName;
21   //Register
22   if (!RegisterClassEx(&myWin)) return 0;
23   const HWND hWindow = CreateWindow(
24     appName,
25     appName,
26     WS_OVERLAPPEDWINDOW,
27     CW_USEDEFAULT,
28     CW_USEDEFAULT,
29     CW_USEDEFAULT,
30     CW_USEDEFAULT,
31     0,
32     0,
33     hInstance,
34     0);
35   ShowWindow(hWindow,iCmdShow);
36   UpdateWindow(hWindow);
37   {
38     MSG msg;
39     while(GetMessage(&msg,0,0,0))
40     {
41       TranslateMessage(&msg);
42       DispatchMessage(&msg);
43     }
44     return (int)msg.wParam;
45   }
46 }
47
48 LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
49 {
50   if (msg==WM_PAINT)
51   {
52     PAINTSTRUCT ps;
53     const HDC hDC = BeginPaint(hWindow,&ps);
54     RECT rect;
55     GetClientRect(hWindow,&rect);
56     DrawText(hDC,TEXT("HELLO WORLD"),-1,&rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
57     EndPaint(hWindow,&ps);
58     return 0;
59   }
60   else if (msg==WM_DESTROY)
61   {
62     PostQuitMessage(0);
63     return 0;
64   }
65   return DefWindowProc(hWindow,msg,wParam,lParam);
66 }  

  上面的程序运行的流程是:进入WinMain函数->初始化WNDCLASSEX,调用RegisterClassEx函数注册窗口类->调用ShowWindow和UpdateWindow函数显示并更新窗口->进入消息循环。关于消息循环再简单说下,Windows应用程序是消息驱动的,系统或用户让应用程序进行某项操作完成某个任务时会发送消息,进入程序的消息队列,然后消息循环会将消息队列中的消息取出,交予相应的窗口过程处理,此程序的窗口过程函数就是myWndProc函数,窗口过程函数处理完消息就完成了某项操作或任务。本例是要显示“HELLO WORLD”字符串,UpdateWindow函数会发送WM_PAINT消息,但是此消息不经过消息队列而是直接送到窗口过程处理,在窗口过程函数中最终绘制了“HELLO WORLD”字符串。



MFC应用程序

下面是MFC应用程序的运行流程,通过MFC库中代码进行分析:

首先在HelloWorld.cpp中定义全局对象theApp:CHelloWorldApp theApp;。调用CWinApp和CHelloWorldApp的构造函数后,进入WinMain函数(位于appmodul.cpp中)。

1 extern "C" int WINAPI
2 _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
3     _In_ LPTSTR lpCmdLine, int nCmdShow)
4 #pragma warning(suppress: 4985)
5 {
6     // call shared/exported WinMain
7     return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
8 }  

  在TCHAR.h中,有此定义:#define _tWinMain   WinMain,所以这里的_tWinMain就是WinMain函数。它调用了AfxWinMain函数(位于WinMain.cpp中)。

 1 int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)
 2 {
 3        .............略
 4        // App global initializations (rare)
 5        if (pApp != NULL && !pApp->InitApplication())
 6               goto InitFailure;
 7
 8        if (!pThread->InitInstance())
 9        {
10               .........略
11        }
12
13        // Run函数位于THRDCORE.cpp中,由此函数进入消息循环
14        nReturnCode = pThread->Run();
15
16        ..............略
17
18        return nReturnCode;
19 }  

  上面InitInstance函数代码如下:

 1 BOOL CTestApp::InitInstance()
 2 {
 3        .............略
 4        CSingleDocTemplate* pDocTemplate;
 5        pDocTemplate = new CSingleDocTemplate(
 6               IDR_MAINFRAME,
 7               RUNTIME_CLASS(CTestDoc),
 8               RUNTIME_CLASS(CMainFrame),      // main SDI frame window
 9               RUNTIME_CLASS(CTestView));
10        if (!pDocTemplate)
11              return FALSE;
12        AddDocTemplate(pDocTemplate);
13        // Parse command line for standard shell commands, DDE, file open
14
15        CCommandLineInfo cmdInfo;
16        ParseCommandLine(cmdInfo);
17
18        //ProcessShellCommand位于AppUI2.cpp中,注册并创建窗口
19        if (!ProcessShellCommand(cmdInfo))
20              return FALSE;
21
22        m_pMainWnd->ShowWindow(SW_SHOW);
23        m_pMainWnd->UpdateWindow();
24
25        return TRUE;
26 }      

  InitInstance中的ProcessShellCommand函数又调用了CMainFrame的LoadFrame函数注册并创建了窗口,执行完ProcessShellCommand函数以后,调用了m_pMainWnd的ShowWindow和UpdateWindow函数显示并更新框架窗口。这些是不是与上面的SDK程序十分类似?

接下来该是消息循环了,上面的AfxWinMain函数中调用了pThread的Run函数(位于THRDCORE.cpp中),在Run中包含了消息循环。Run函数的代码如下:

 1 int CWinThread::Run()
 2 {
 3         .............略
 4         // phase2: pump messages while available
 5         do
 6         {
 7               // pump message, but quit on WM_QUIT
 8               if (!PumpMessage())
 9                      return ExitInstance();
10
11               // reset "no idle" state after pumping "normal" message
12               if (IsIdleMessage(&m_msgCur))
13               {
14                      bIdle = TRUE;
15
16                      lIdleCount = 0;
17
18               }
19        } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
20        ..............略
21 }
22
23 BOOL CWinThread::PumpMessage()
24 {
25        return AfxInternalPumpMessage();
26 }
27
28 BOOL AFXAPI AfxInternalPumpMessage()
29 {
30        _AFX_THREAD_STATE *pState = AfxGetThreadState();
31
32        if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
33        {
34              .............略
35        }
36        ...............略
37        if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))
38        {
39              ::TranslateMessage(&(pState->m_msgCur));
40              ::DispatchMessage(&(pState->m_msgCur));
41        }
42
43        return TRUE;
44 }       

   我们看到PumpMessage中通过调用GetMessage、TranslateMessage、DispatchMessage等建立了消息循环并投递消息

窗口过程函数AfxWinProc形式如下:

LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam)
{
      ……
      CWnd*pWnd=CWnd::FromHandlePermanent(hWnd);
      ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);
}

  



两者运行流程对比

到此,通过对比可以发现,MFC应用程序的运行流程与SDK程序是类似的,都是先进行一些初始化过程,再注册并创建窗口,然后显示、更新窗口,最后进入消息循环,消息都由窗口过程函数处理。现在大家是不是觉得有些头绪了?在运行流程上有基本的掌握即可。



二.MFC应用程序框架主要类之间的关系

给大家演示了如何利用应用程序向导生成单文档应用程序框架,可以看到程序的基本框架和必要的代码都自动生成了,上一讲又讲解了文件组成结构,实际上在前面自动生成的框架中比较重要的类包括以下几个:CHelloWorldApp、CMainFrame、CHelloWorldDoc和CHelloWorldView,至于其他的类比如CClassView、CFileView等都是在框架窗口(CMainFrame)上创建的面板等,不是必要的。

鸡啄米就四个主要类的关系简单讲下,CHelloWorldApp类处理消息,将收到的消息分发给相应的对象。CMainFrame是视图CHelloWorldView的父窗口,视图CHelloWorldView就显示在CMainFrame的客户区中。视图类CHelloWorldView用来显示文档类CHelloWorldDoc中的数据,并根据对视图类的操作修改文档类的数据。一个视图类只能跟一个文档类相联系,而一个文档类可以跟多个视图类相联系。关于视图类和文档类的关系后面会详细讲解。

本节VC++/MFC编程入门教程内容比较多,主要是让大家对MFC应用程序的运行原理有大概的了解。对于以后的MFC开发有很多好处。

我们看到PumpMessage中通过调用GetMessage、TranslateMessage、DispatchMessage等建立了消息循环并投递消息。

窗口过程函数AfxWinProc形式如下:

时间: 2024-10-24 04:32:26

鸡啄米MFC教程笔记之二:MFC应用程序框架分析的相关文章

鸡啄米MFC教程笔记之一:MFC和VC++

VC++简介 VC++全称是Visual C++,是由微软提供的C++开发工具,它与C++的根本区别就在于,C++是语言,而VC++是用C++语言编写程序的工具平台.VC++不仅是一个编译器更是一个集成开发环境,包括编辑器.调试器和编译器等,一般它包含在Visual Studio中.Visual Studio包含了VB.VC++.C#等编译环境.当然我们在使用VC++ 6.0的时候为了轻便,总是只单独安装VC++ 6.0.但自微软2002年发布Visual Studio.NET以来,微软建立了在

鸡啄米:C++编程之十四学习之构造函数和析构函数

1. 本人学习鸡啄米课程的笔记记录,用来记录学习的历程和进度 2. 构造函数 我们在声明一个变量时,如果对它进行了初始化,那么在为此变量分配内存空间时还会向内存单元中写入变量的初始化.声明对象有相似的过程,程序执行时遇到对象声明语句时会向操作系统申请一定的内存空间来存放这个对象,但是它能像一般变量那样初始化时写入指定的初始值吗?类的对象太复杂了,要实现这一点不太容易,这就需要构造函数来实现. 构造函数的作用就是在对象被创建时利用特定的初始值构造对象,把对象置于某一个初始状态,它在对象被创建的时候

鸡啄米MFC教程笔记之七:对话框:为控件添加消息处理函数

MFC为对话框和控件等定义了诸多消息,我们对它们操作时会触发消息,这些消息最终由消息处理函数处理.比如我们点击按钮时就会产生BN_CLICKED消息,修改编辑框内容时会产生EN_CHANGE消息等.一般为了让某种操作达到效果,我们只需要实现某个消息的消息处理函数. 一.添加消息处理函数 鸡啄米仍以前面的加法计算器的程序为例,说明怎样为“计算”按钮控件添加消息处理函数.添加方法列出4种: 1.使用Class Wizard添加消息处理函数 用过的VC++6.0的朋友应该对Class Wizard很熟

鸡啄米MFC教程笔记之四:对话框:创建对话框模板和修改对话框属性

---恢复内容开始--- 本节开始为大家讲解偏应用的知识-创建对话框. 对话框,大家应该很熟悉了,在我们常用的软件中大多都有对话框界面,例如,360安全卫士的主界面其实就是个对话框,只是它做了很多美工方面的工作,将其大大美化了. 创建对话框主要分两大步,第一,创建对话框资源,主要包括创建新的对话框模板.设置对话框属性和为对话框添加各种控件:  第二,生成对话框类,主要包括新建对话框类.添加控件变量和控件的消息处理函数等. 这里鸡啄米就再创建一个基于对话框的应用程序,用来实现加法运算的功能.如下:

《鸡啄米C++编程入门系列》系列技术文章整理收藏

<鸡啄米C++编程入门系列>系列技术文章整理收藏 收藏整理鸡啄米C++编程入门系列文章,供个人和网友学习C++时参考 1鸡啄米:C++编程入门系列之前言 2鸡啄米:C++编程入门系列之一(进制数) 3鸡啄米:C++编程入门系列之二(原码.反码与补码) 4鸡啄米:C++编程入门系列之三(VS2010的使用介绍) 5鸡啄米:C++编程入门系列之四(数据类型) 6鸡啄米:C++编程入门系列之五(运算符和表达式) 7鸡啄米:C++编程入门系列之六(算法的基本控制结构之选择结构) 8鸡啄米:C++编程入

简明Python教程笔记(二)----用户交互raw_input()

raw_input() python内建函数 将所有输入看做字符串,返回字符串类型 input()对待纯数字输入时具有自己的特性,它返回所输入的数字的类型( int, float ) input() 本质上还是使用 raw_input() 来实现的,只是调用完 raw_input() 之后再调用 eval() 函数 例子: #!/usr/bin/env pythonthis_year = 2014name = raw_input('please input your name:')age1 =

鸡啄米:C++编程之十三学习之类与对象,类的声明,成员的访问控制

1. 本次学习鸡啄米课程第13篇,把比较重要的学习记录下来,以敦促自己更好的学习.推荐他们的网址学习:http://www.jizhuomi.com/school/c/97.html 2. 在面向过程的设计中,程序的模块是函数构成的,而面向对象设计中程序模块是类构成的,函数只是语句和数据的封装,而类是函数和数据的封装,做了一段C++之后,才明白这句话的意思.其实类就是一种自定义的数据类型.可以定义某个类类型的变量,用类定义的变量叫做类的对象,这种定义对象的过程叫做实例化. 3. 不过一直有个疑问

Cocos2d-x学习笔记(二)Cocos2d-x整体框架描述

原创文章,转载请注明出处:http://blog.csdn.net/sfh366958228/article/details/38680123 上一节我们简单分析了HelloWorld工程,这一讲我们将进一步来看看Cocos2d-x的整体框架. 在了解Cocos2d-x的整体框架之前,我们不妨想想拍摄一部电影需要些什么? 导演.舞台(场景).布景.演员,有了这些基本条件后,在这些元素都有了之后,演员就可以根据剧本,表现不同的动作行为. 同理,做一个游戏就像是拍摄一部电影,你同样需要这些元素,而C

Hadoop学习笔记(二)——zookeeper使用和分析

分布式架构是中心化的设计,就是一个主控机连接多个处理节点,因此保证主控机高可用性十分关键.分布式锁是解决该问题的较好方案,多主控机抢一把锁.Zookeeper就是一套分布式锁管理系统,用于高可靠的维护元数据. 一.应用 1.集群模式 集群模式下配置多个Zookeeper节点,启动Zookeeper集群,Zookeeper会根据配置投票选举一个节点获得分布式锁. 关键配置举例: # The Cluster servers #server.1=192.168.1.10:2887:3887 #serv