mfc及应用程序开发,mfc程序开发参考大全
对于程序员来说,如果想要提高编程效率,一个好用、强大、方便的编程工具往往会给我们程序员带来极大的便利。其实对于现在的编程工具来说,用哪个工具并不是关键。重要的是你能用到什么程度。毕竟现在的工具都很强大,没有不胜任一般编程任务的工具。否则,恐怕他即使只有一个月的生命,也无法存在于这个世界上。但是我们都是根据个人喜好和周围人的影响来使用一个或者几个工具。对比Visual Basic、C Builder、Delphi等编程工具,用VC编写Windows应用程序是最具挑战性和艰巨性的。在本文中,我无意比较各种工具的质量。我只把自己学习Visual C的MFC的经验和心得分享和交流给大家,希望能交到更多志同道合的朋友。
就我个人的偏见,学习VC应该是基于他的类库MFC(微软基础类)。可能有些人听到MFC会有点害怕,这可以理解。毕竟微软虽然给了我们一个强大的非常复杂的类库,但是并没有给我们带来从中学习的便利。回想当初学MFC时的无奈和迷茫,走过的弯路,现在想起来还是心有余悸。虽然我还处于非常初级的入门阶段,但我还是很乐意和大家分享我的经验。也希望得到大家的指点。
一、SDK应用结构
我学习MFC的路是从windows编程开始的(可能一开始就是走了弯路)。首先请大家跟着我看一个带有SDK应用结构的Windows应用。当然也是经典的“Hello world!”,写作的过程不必多言。下面是他的主要源代码(我是用向导生成的,为了方便阅读稍微编辑了一下):LResult回调WndProc (HWND,UINT,WParam,LPARAM);
int API entry WinMain(h instance h instance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
//TODO:在此处放置代码。
味精味精;
………………………………
MyRegisterClass(h instance);
如果(!InitInstance (hInstance,nCmdShow))
{
返回FALSE
}
//主消息循环:
while (GetMessage( msg,NULL,0,0))
{
如果(!TranslateAccelerator(msg.hwnd,hAccelTable,msg))
{
翻译消息(msg);
DispatchMessage(消息);
}
}
返回msg.wParam
}
BOOL InitInstance(h instance h instance,int nCmdShow)
{
……………………
返回TRUE
}
//窗口函数WndProc(),回调函数
LRESULT回调WndProc(HWND hWnd,UINT消息,WPARAM wParam,LPARAM lParam)
{
int wmId,wmEvent
……………………
开关(消息)
{
案例WM_COMMAND:
………………
打破;
案例WM_PAINT:
hdc=BeginPaint(hWnd,PS);
//TODO:在此添加任何绘图代码.
RECT rt;
GetClientRect(hWnd,rt);
DrawText(hdc,szh hello,strlen(szh hello),rt,DT _ CENTER);
EndPaint(hWnd,PS);
打破;
案例WM_DESTROY:
……
默认值:
返回DefWindowProc(hWnd,message,wParam,lParam);
}
返回0;
}
SDK的“Hello World”源程序
首先,我们来看看这个程序的结构。WinMain()是函数的入口点。这个函数的主要任务是完成一些初始化工作,维护一个消息循环。它们的工作流程如下:entry(winmain())-myregisterclass()-initinstance()-while消息循环。函数从入口开始执行,然后调用MyRegisterClass()注册窗口类,然后InitInstance()生成并显示窗口,这样之后就完成了一个窗口的初始化(当然在MyRegisterClass()和InitInstance()中,具体实现需要调用相应的API函数,不过,我这里重点是他的结构,所以不考虑他的具体实现细节),然后维护消息循环。至此,这个程序的基本架构差不多建立起来了。将来,程序的运行将由一个消息周期来驱动。
现在,让我们再次看看那个消息循环的结构。在示例程序中,我们希望程序在窗口中输出“Hello World”。在主程序中,我们似乎已经完整的分析了应用的框架,但是还没有看到要求程序输出‘Hello World’?这就是Windows消息的作用。当然,我们还记得,刚才我们说主程序也维护一个消息循环。是的,这个循环里有很多文章。窗口应用程序的特点是消息驱动的。当系统或用户请求应用程序完成某项任务时,它依赖于该消息。系统会将用户的请求或系统的请求放入一个消息结构中,然后发送给应用程序进行处理。现在让我们看看应用程序如何完成我们的任务。应用程序初始化完成后,调用显示窗口的API函数,这样系统就知道程序要显示窗口了。这时(注意这是生成消息的时间),消息队列中会生成一个WM_PAINT消息,让应用程序的消息循环捕捉这个消息并发送给窗口函数(注意这个函数是系统调用的),然后窗口函数处理这个消息,我们就到了。从这个过程中我们可以看出,如果要和程序进行交互,我们需要做的就是选择合适的时间让系统生成消息。现在,我们终于可以全面了解SDK程序的运行过程了。
SDK应用程序的框架就这样建立起来了。我们来看看如何建立一个MFC应用框架,以及它们之间的对应关系。
二、MFC应用程序结构
前面我分析了一个经典SDK应用的结构,现在我要进入主题“MFC应用结构”。MFC应用程序有很多种。为了与上一篇文章形成明显的对比,我们在这里看一个SDI应用程序。当然,经典的《Hello World》就是例子。使用向导生成应用程序后,您会发现几个文件。首先,不管有哪些文件,我们都会按照程序执行的主线提取主要源程序,并进行分析。(因为MFC生成的应用不是很好读,所以我在这里重新编辑了它们)。神州大地采用了theApp
int afx API afx winmain(h instance h instance,HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,int nCmdShow)
{
CWinThread * pThread=AfxGetThread();
cwin app * pApp=AfxGetApp();
//AFX内部初始化
如果(!AfxWinInit(hInstance,hPrevInstance,lpCmdLine,nCmdShow))
转到InitFailure
//应用程序全局初始化(罕见)
如果(pApp!=NULL!pApp- InitApplication())
转到InitFailure
//执行特定的初始化
如果(!pThread- InitInstance())
{
if (pThread- m_pMainWnd!=空)
{
TRACE0(Warning:销毁非空m _ pMainWnd n );
pThread-m _ pMainWnd-destroy window();
}
nReturnCode=pThread-exit instance();
转到InitFailure
}
nReturnCode=pThread-Run();
初始化失败:
……………………
afx winterm();
返回nReturnCode
}
BOOL CWinApp:InitApplication()
{
if(CDocManager:pStaticDocManager!=空)
{
if (m_pDocManager==NULL)
m _ pDocManager=CDocManager:pStaticDocManager;
CDocManager:pStaticDocManager=NULL;
}
if (m_pDocManager!=空)
m_pDocManager- AddDocTemplate(空);
其他
cdoc manager:bStaticInit=FALSE;
返回TRUE
}
BOOL CHelloWorldApp:InitInstance()
{
AfxEnableControlContainer();
………………………………
//更改存储我们的设置的注册表项。
//TODO:您应该将此字符串修改为适当的形式
//例如您的公司或组织的名称。
SetRegistryKey(_T(本地AppWizard-生成的应用程序));
LoadStdProfileSettings();//加载标准INI文件选项(包括MRU)
//注册应用程序的文档模板。文档模板
//作为文档、框架窗口和视图之间的连接。
CSingleDocTemplate * PDO template;
pDocTemplate=new CSingleDocTemplate(
IDR _大型机,
RUNTIME_CLASS(CHelloWorldDoc),
RUNTIME_CLASS(CMainFrame),//主SDI框架窗口
RUNTIME _ CLASS(CHelloWorldView));
AddDocTemplate(pDocTemplate);
//解析标准shell命令、DDE、文件打开的命令行
CCommandLineInfo cmdInfo
ParseCommandLine(cmd info);
//调度命令行上指定的命令
如果(!ProcessShellCommand(cmdInfo))
返回FALSE
//唯一的窗口已经初始化,所以显示并更新它。
m _ pMainWnd-SHOW window(SW _ SHOW);
m _ pMainWnd-update window();
返回TRUE
}
BOOL CWinApp:InitInstance()
{
返回TRUE
}
MFC应用程序的“Hello World”
乍一看,这个程序似乎无法分析,甚至连程序的入口都找不到。其实上面的程序做完之后还是这个样子。好,让我们看看这个程序是如何以同样的方式工作的。(注意,上面的程序来自不同的文件,这里的布局只是为了更清楚地展示程序的结构。至于MFC的文件组织,我会在后面的题目中具体分析,所以暂时可以不做)。
首先,在程序开始时,定义了一个全局变量theApp。现在我们只需要知道它代表了整个程序的存在,然后程序开始介入入口点。你在开玩笑吗?切入点在哪里?不是,其实int AFXAPI AfxWinMain()是这个程序的入口。奇怪!不过没关系。就像我们第一次看到C语言中的main()函数一样,只要我们知道。在AfxWinMain()中,分别调用了一些类的成员函数。模仿前面的分析方法,还可以画出程序执行路径图。entry-> afxgetthread()-> afxgetapp()-afxwininit()-PAPP-init application()-pthread-init instance()-。如你所见,程序有线索可循。但是和SDK相比,现在已经面目全非了。过去清晰的程序结构是否也在这些程序中找到?答案是肯定的,但是它们的具体实现都是用MFC包装的。那么,让我们来看看这个应用程序是如何启动和运行的。
AfxWinMain()启动程序后,调用AfxGetApp()获取应用程序的对象指针pApp,然后通过这个指针调用相关的成员函数,完成初始化和启动。最后调用Run()函数,其中Run()函数代表SDK中的消息循环。
事情正在按预期进行,但似乎缺少了什么。是的,我们确实有一项未完成的工作,这正是我们所需要的。《Hello World》好像还没出口呢!这不是我的疏忽,而是刻意的安排,因为MFC采用了全新的(当然是相对于SDK而言的)消息处理机制,至少表面上是这样的。然而,我不打算在这里马上解决这个问题。毕竟有点复杂。我会在我们了解MFC文件之间的关系后回答这个问题。
没想到这篇文章这么长。当初我以为一下子就能把这个问题解释清楚,但其实我的写作思路和分析程序的时候一样长。所以我有必要提醒我自己和读这篇文章的朋友,他们应该休息一下。现在很庆幸自己把这个问题分成几个小问题解决了。我将在下一个主题中继续讨论MFC程序生成的文件以及它们之间的调用关系。
3.MFC程序结构分析
前面我分别给出了SDK和MFC应用的框架,梳理了它们之间的对应关系。但是对于MFC程序来说,真正理解它的框架是不够的。我现在想做的是继续分析上面的MFC程序,希望能如我的题目所说,把MFC的应用框架看透。
首先看一下应用程序向导生成的MFC应用程序的主文件,包含了哪些类,相关类对应的函数以及它们之间的关系(还是那句话,因为我在这里关注的是程序的框架,所以省略了一些和题目关系不大的东西)。
在上面的SDI应用程序中,向导为我们生成了四个主要的类。这些类都是MFC类的派生类,分别包含在对应的头文件和实现文件中。
1.框架窗口类及其相关文件
window类对应于应用程序的主窗口。了解了这一点,就可以建立对这个阶层的感性认识。其定义在头文件MainFrm.h中,而其实现在MainFrm.cpp文件中。框架窗口照明的所有功能都在这里定义和实现。
2.文档类及其相关文件
应用中的类之间没有直观的对应关系,但是有一点我们要知道,MFC框架的一个特点就是文档/视图结构。这里可以抽象理解。比如我们在Word中打开一个文件,其实这个文件就是一个文档,我们看到的只是这个文件的视图。因此,文档提供了对应用程序显示的支持,但我们真正看到的是一个视图。文档类的定义在“Hello WorldDoc.h”中,它的实现在“Hello WorldDoc.cpp”文件中。
3.查看类及其相关文件
view类用于显示文档对象的内容。我们在Word程序中看到的界面是视图,我们需要修改它。要绘制的第一个对象是视图。因此,正如其名称所指出的,视图类提供了用户从视点看到的内容。类的定义在“Hello WorldView.h”中,实现在文件“Hello WorldView.h”中。
4.应用程序类及其相关文件
MFC应用程序的初始化、启动和结束都是由应用程序对象完成的。他对应的文件是“Hello World.cpp”和“Hello World.h”。
对应用对象的功能有了大致的了解之后,现在我们来看看它们之间的关系(如下图所示)。
从上图中,我们可以大致看出MFC应用程序对象之间的关系。在应用程序开始的时候(这里的内容参考上一篇文章),会生成应用程序对象,然后在InitInstance中会创建文档模板对象(由CdocManager管理),这样应用程序就可以通过创建的模板对象来管理文档、视图和框架窗口。
至此,我的文章终于可以暂时告一段落了。在本文中,我主要分析应用程序的框架,因此我忽略了许多实现细节。其实学习MFC是一个很漫长的过程,不同的阶段会产生不同的看法和理解。相信随着学习的深入,每个人都会有自己的理解。但是这里我给出自己的经验:学习MFC的时候,一定要在宏观上对其应用框架有一个感性的认识!因为之后我们在学习的时候就会有一个明显的方向,知道自己在学什么。当初我并没有意识到这一点,所以常常不知道自己下一步该做什么,也不知道自己读书是为了什么。希望能通过这篇文章和大家一起学习!