VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米
http://www.jizhuomi.com/software/145.html

 

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

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

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

SDK应用程序

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

#include <windows.h>
LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
const static TCHAR appName[] = TEXT("Hello world");
WNDCLASSEX myWin;
myWin.cbSize = sizeof(myWin);
myWin.style = CS_HREDRAW | CS_VREDRAW;
myWin.lpfnWndProc = myWndProc;
myWin.cbClsExtra = 0;
myWin.cbWndExtra = 0;
myWin.hInstance = hInstance;
myWin.hIcon = 0;
myWin.hIconSm = 0;
myWin.hCursor = 0;
myWin.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
myWin.lpszMenuName = 0;
myWin.lpszClassName = appName;
//Register
if (!RegisterClassEx(&myWin)) return 0;
const HWND hWindow = CreateWindow(
appName,
appName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
hInstance,
0);
ShowWindow(hWindow,iCmdShow);
UpdateWindow(hWindow);
{
MSG msg;
while(GetMessage(&msg,0,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
}
LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg==WM_PAINT)
{
PAINTSTRUCT ps;
const HDC hDC = BeginPaint(hWindow,&ps);
RECT rect;
GetClientRect(hWindow,&rect);
DrawText(hDC,TEXT("HELLO WORLD"),-1,&rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hWindow,&ps);
return 0;
}
else if (msg==WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWindow,msg,wParam,lParam);
}

       上面的程序运行的流程是:进入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中)。

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

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

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)
{
.............略
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
if (!pThread->InitInstance())
{
.........略
}
// Run函数位于THRDCORE.cpp中,由此函数进入消息循环
nReturnCode = pThread->Run();
..............略
return nReturnCode;
}

       上面InitInstance函数的代码如下:

BOOL CTestApp::InitInstance()
{
.............略
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CTestView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
//ProcessShellCommand位于AppUI2.cpp中,注册并创建窗口
if (!ProcessShellCommand(cmdInfo))
return FALSE;
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}

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

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

int CWinThread::Run()
{
.............略
// phase2: pump messages while available
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));
..............略
}
BOOL CWinThread::PumpMessage()
{
return AfxInternalPumpMessage();
}
BOOL AFXAPI AfxInternalPumpMessage()
{
_AFX_THREAD_STATE *pState = AfxGetThreadState();
if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
{
.............略
}
...............略
if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))
{
::TranslateMessage(&(pState->m_msgCur));
::DispatchMessage(&(pState->m_msgCur));
}
return TRUE;
}

       我们看到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开发有很多好处。如果有问题请在鸡啄米博客留言交流。谢谢。

除非特别注明,鸡啄米文章均为原创

转载请标明本文地址:http://www.jizhuomi.com/software/145.html

VS2010/MFC编程入门之四(MFC应用程序框架分析)的更多相关文章

  1. VS2010/MFC编程入门之四十七(字体和文本输出:CFont字体类)

    上一节中鸡啄米讲了MFC异常处理,本节的主要内容是字体CFont类. 字体简介 GDI(Graphics Device Interface),图形设备接口,是Windows提供的一些函数和结构,用于在 ...

  2. VS2010/MFC编程入门之四十一(文档、视图和框架:分割窗口)

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

  3. VS2010/MFC编程入门之四十四(MFC常用类:定时器Timer)

    前面一节鸡啄米讲了CTime类和CTimeSpan类的使用,本节继续讲与时间有关的定时器.定时器并不是一个类,主要考虑到,提起时间的话就不能不说定时器,所以就把它放到CTime和CTimeSpan之后 ...

  4. VS2010/MFC编程入门之四十四:定时器Timer

    前面一节鸡啄米讲了CTime类和CTimeSpan类的使用,本节继续讲与时间有关的定时器.定时器并不是一个类,主要考虑到,提起时间的话就不能不说定时器,所以就把它放到CTime和CTimeSpan之后 ...

  5. VS2010/MFC编程入门之四十五(MFC常用类:CFile文件操作类)

    上一节中鸡啄米讲了定时器Timer的用法,本节介绍下文件操作类CFile类的使用. CFile类概述 如果你学过C语言,应该知道文件操作使用的是文件指针,通过文件指针实现对它指向的文件的各种操作.这些 ...

  6. VS2010/MFC编程入门之四十六(MFC常用类:MFC异常处理)

    上一节中鸡啄米讲了CFile文件操作类,本节主要来说说MFC异常处理. 在鸡啄米C++编程入门系列的最后一节鸡啄米:C++编程入门系列之五十(异常处理)中,鸡啄米讲了C++标准异常的处理机制,如果你还 ...

  7. MFC编程入门之五(MFC消息映射机制概述)

    在MFC软件开发中,界面操作或者线程之间通信都会经常用到消息,通过对消息的处理实现相应的操作.比较典型的过程是,用户操作窗口,然后有消息产生,送给窗口的消息处理函数处理,对用户的操作做出响应. 一.什 ...

  8. VS2010/MFC编程入门之四十八(字体和文本输出:文本输出)

    鸡啄米在上一节中讲了CFont字体类,本节主要讲解文本输出的方法和实例. 文本输出过程 在文本输出到设备以前,我们需要确定字体.字体颜色和输出的文本内容等信息.Windows窗口的客户区由应用程序管理 ...

  9. VS2010/MFC编程入门之四十九(图形图像:CDC类及其屏幕绘图函数)

    上一节中鸡啄米讲了文本输出的知识,本节的主要内容是CDC类及其屏幕绘图函数. CDC类简介 CDC类是一个设备上下文类. CDC类提供了用来处理显示器或打印机等设备上下文的成员函数,还有处理与窗口客户 ...

随机推荐

  1. [shell基础]——算术运算

    shell只支持整数运算.一般可用let.expr.declare.$[]实现. 更精准的运算建议使用Linux下的bc工具——一款高精度计算语言. 1. let是shell内建的整数运算命令 ## ...

  2. C++设计模式系列

    该系列主要总结了使用C++来实现各种设计模式,并结合实际的案例来分析如何使用,以及在什么场合下使用设计模式.以下是该系列所有文章的链接.希望对大家有帮助. C++设计模式——简单工厂模式 C++设计模 ...

  3. 关于 Google Chrome 中的全屏模式和 APP 模式

    前言:我一直在纠结这篇文章是否应该归类在「前段开发」的范围内,哈哈! 前段时间做了一个项目,涉及到一个要全屏模式去访问网页的需求,因为 Google Chrome 的效率不错,而且专门为 Chrome ...

  4. javascript各种模式解析

    1.工厂模式: 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程(后面还将讨论其他设计模式及其在JavaScript 中的实现).考虑到在ECMAScript 中无法创建 ...

  5. 使用Npoi向Excel中插入图片

    先把数据库中的数据都导入到Excel表格中,把图片地址的路径全部转成绝对路径. 使用Npoi读取刚导出的Excle表格,把图片那个单元格的图片路径读出来,然后用文件流读取图片,然后通过Npoi把图片放 ...

  6. hdu 2853 Assignment KM算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2853 Last year a terrible earthquake attacked Sichuan ...

  7. 日志:slf4j+log4j+maven配置

    代码的日志输出,前前后后折腾了我好几次. 本着会用,快速配置的原则,还是将配置过程记录下来,以便复用. 参考:http://blog.csdn.net/anialy/article/details/8 ...

  8. SQL 基本(Head First)

    CREATE TABLE my_contacts( last_name VARCHAR(30), first_name VARCHAR(30), email VARCHAR(50), gender C ...

  9. 【bzoj1002】[FJOI2007]轮状病毒

    1002: [FJOI2007]轮状病毒 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 4381  Solved: 2393[Submit][Statu ...

  10. 如何优化C语言代码(程序员必读)

    1.选择合适的算法和数据结构 应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料,有很多计算机书籍上都有介绍.将比较慢的顺序查找法用较快的二分查找或乱序查找法代替,插入排序或冒泡排序 ...