上一讲鸡啄米讲的是VS2010应用程序工程中文件的组成结构,可能大家对工程的运行原理还是很模糊,理不出头绪,毕竟跟C++编程入门系列中的例程差别太大。这一节鸡啄米就为大家分析下MFC应用程序框架的运行流程。

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

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

SDK应用程序

首先,给出WindowsSDK应用程序“HelloWorld”的源码:

C++代码

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函数,窗口过程函数处理完消息就完成了某项操作或任务。本例是要显示“HELLOWORLD”字符串,UpdateWindow函数会发送WM_PAINT消息,但是此消息不经过消息队列而是直接送到窗口过程处理,在窗口过程函数中最终绘制了“HELLO WORLD”字符串。

MFC应用程序

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

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

C++代码

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中)。

C++代码

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函数的代码如下:

C++代码

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函数的代码如下:

C++代码

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形式如下:

C++代码

1.  LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam)

2.  {

3.        ……

4.        CWnd*pWnd=CWnd::FromHandlePermanent(hWnd);

5.        ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);

6.  }

两者运行过程对比

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

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

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

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

VS2010/MFC编程入门之三(MFC应用程序框架分析)相关推荐

  1. (转载)VS2010/MFC编程入门之三十六(工具栏:工具栏资源及CToolBar类)

    上一节中鸡啄米讲了菜单及CMenu类的使用,这一节讲与菜单有密切联系的工具栏. 工具栏简介 工具栏一般位于主框架窗口的上部,菜单栏的下方,由一些带图片的按钮组成.当用户用鼠标单击工具栏上某个按钮时,程 ...

  2. VS2013/MFC编程入门之三十四(工具栏:工具栏资源及CToolBar类)

    上一节中讲了菜单及CMenu类的使用,这一节讲与菜单有密切联系的工具栏. 工具栏简介 工具栏一般位于主框架窗口的上部,菜单栏的下方,由一些带图片的按钮组成.当用户用鼠标单击工具栏上某个按钮时,程序会执 ...

  3. VS2010/MFC编程入门之三十(常用控件:树形控件Tree Control 上)

    前面两节为大家讲了列表视图控件List Control,这一节开始介绍一种特殊的列表--树形控件Tree Control. 树形控件简介 树形控件在Windows系统中是很常见的,例如资源管理器左侧的 ...

  4. Visual Studio/MFC编程入门之MFC常用类:CString类

    上一节讲了分割窗口的有关知识,本节开始讲解MFC的一些常用类,先来说说CString类.在内容开始前为大家介绍一款MFC界面开发控件: Xtreme Toolkit Pro | 免费下载:是屡获殊荣的 ...

  5. vs2010c语言窗口程序,VS2010/MFC编程入门之四十一(文档、视图和框架:分割窗口)...

    上一节中鸡啄米讲了文档.视图和框架结构中各对象之间的关系,本节主要讲讲在MFC中如何分割窗口. 分割窗口概述 分割窗口,顾名思义,就是将一个窗口分割成多个窗格,在每个窗格中都包含有视图,或者是同一类型 ...

  6. VS2010/MFC编程入门教程之目录和总结

    写这套VS2010/MFC编程入门教程历时八个月,时间比较长,都是鸡啄米在工作之余加班加点完成的.虽然有点累,但看到有这么多人过来支持鸡啄米,很是值得. 写每节教程我都力求讲明白,深入浅出的阐述每个知 ...

  7. VS2010/MFC编程入门

    近来鼓捣2D小游戏的过程中发现鸡啄米大神很好的MFC系列资料,在此汗颜将地址扒拉了过来,留待以后慢慢学习. VS2010/MFC编程入门之一(VS2010与MSDN安装过程图解) (http://ww ...

  8. VS2010/MFC编程入门教程

    一.VS2010/MFC编程入门教程之目录       第一部分:VS2010/MFC开发环境       VS2010/MFC编程入门之前言        VS2010/MFC编程入门之一(VS20 ...

  9. VS2010/MFC 编程入门教程之目录和总结

    [原地址]:http://www.jizhuomi.com/software/257.html 鸡啄米这个网站不是很了解,但确实有很多好文章.自己工作需要,在vs2010 下学习mfc的知识,发现了这 ...

  10. VS2019/MFC编程入门之对话框:向导对话框的创建及显示

    上一讲讲了属性页对话框和相关的两个类CPropertyPage类和CPropertySheet类,对使用属性页对话框做准备.本节将为大家演示如何创建向导对话框. 仍然以前面的"加法计算器&q ...

最新文章

  1. 在CentOS7上部署Apache Mesos
  2. 本地Yum仓库搭建部署
  3. mysql正则提取字符串_mysql字符串查找截取与正则表达式的联合应用
  4. Failed to create the Java Virtual Machine
  5. 堆、栈及静态数据区详解 转
  6. dataset的去重计数 g2_向工程渣土运输车辆计数 漏洞损失说“不”
  7. centos7 修改默认yum源为国内的阿里云
  8. java uuid静态方法_Java UUID nameUUIDFromBytes()方法及示例
  9. 计算机设备及维护招标公告,列“State”不属于表 。
  10. python接口自动化(二)--什么是接口测试、为什么要做接口测试(详解)
  11. MySQL千万级大表优化解决方案
  12. 为什么说传统分布式事务不再适用于微服务架构?
  13. 波兰表达式 逆波兰表达式
  14. excel两列数据对比找不同_莫斯科地标百年前后对比图,快来“找不同”
  15. maple 求特征值
  16. 局域网内其他电脑连接NAT模式虚拟机
  17. java jws web_java 用jws发布一个简单的webservice
  18. python cmd环境输入python打开应用商店解决
  19. CFD(计算流体动力学)在直升机中的应用
  20. spring源码解析--环境搭建

热门文章

  1. struts环境搭建以及登陆功能的书写
  2. java中排序函数sort()使用,Arrays.sort()和Collections.sort()
  3. Web负载均衡与分布式架构
  4. React基础----ReactJS的使用(一)
  5. 扩展GeoServer数据源
  6. HDU4681 String(dp)
  7. 前端学习JQuery篇03——基本操作(效果)
  8. sizeof和strlen区别
  9. python 详解re模块
  10. 图像预处理第1步:将256色图像转化为灰度图像