【MFC】初识MFC(8)

MFC简介

微软提供的一个类库(Class Libraries),封装了Windows的API,并且包含一个应用程序框架。

1、MFC是对前面窗口编程所用到的API进行封装,在Windows C基础上引入了C++面向对象的思想,简单而言就是把API函数和处理数据捆绑一起,做成了类——当然封装、继承、多态的机制也应用起来;

2、MFC不仅仅是类库(类的一个集合),而且还是一个应用程序框架——已经搭建好了程序的“骨架”(毛坯房),程序员只需要”修修改改“,实现实际功能即可。

理解:

1、Windows C 当然需要掌握,MFC把API进行了一层简单的包装,函数名称、功能、调用的方法大体相同——所以学MFC,必先了解SDK编程;

2、框架的意义在于,不是让程序员自主地调用MFC里的类,而是以框架为基础,被动地接受这种开发的模式——降低”编码“的难度?包括了两(三)种应用程序开发模式:

  • 单文档、多文档:应用程序(实现程序初始化)、框架(管理程序的各个窗口)、视图(用户区和数据展示窗口、一个程序可以有多个)、文档(程序的数据)的窗口程序模式——单文档、多文档应用程序。
  •   对话框:简化单文档、多文档中框架和文档,集中于窗口元素(按钮、文本框、编辑框、列表等等)的可视化设计模式。

 初衷是,MFC告诉你写代码的地方(类向导里的消息响应),你去实现自己的功能吧,就这么简单!

后果是,初学者看到这些”自动“生成代码,被”框架“ 搞得不知所措,战战兢兢,不敢下手。也就是照葫芦画瓢,照写几个范例而已。

因此,了解MFC的实现机制,MFC与SDK编程的关系,掌握这个只能被接受的框架,才能打开程序员设计的思路,放开手脚,自由地实现自己的功能。(推荐书:《深入浅出MFC》)

MFC机制

窗口编程 WinMain 函数是少不了,编译型的程序,入口总是避免不了。创建MFC程序(单文档、多文档、对话框)后在工程中并没发现WinMain函数,程序流程就搞不清楚了。

MFC在程序编译时自动补上WinMain函数,函数的实现在VS的安装目录,比如

C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\atlmfc\src\mfc    (VS2012)

 代码是这样的:

// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include "sal.h"


/
// Standard WinMain implementation
//  Can be replaced as long as 'AfxWinInit' is called first

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	_In_ LPTSTR lpCmdLine, int nCmdShow)
{
	ASSERT(hPrevInstance == NULL);

	int nReturnCode = -1;
	CWinThread* pThread = AfxGetThread();
	CWinApp* pApp = AfxGetApp();

	// AFX internal initialization
	if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
		goto InitFailure;

	// App global initializations (rare)
	if (pApp != NULL && !pApp->InitApplication())
		goto InitFailure;

	// Perform specific initializations
	if (!pThread->InitInstance())
	{
		if (pThread->m_pMainWnd != NULL)
		{
			TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
			pThread->m_pMainWnd->DestroyWindow();
		}
		nReturnCode = pThread->ExitInstance();
		goto InitFailure;
	}
	nReturnCode = pThread->Run();

InitFailure:
#ifdef _DEBUG
	// Check for missing AfxLockTempMap calls
	if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
	{
		TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
			AfxGetModuleThreadState()->m_nTempMapLock);
	}
	AfxLockTempMaps();
	AfxUnlockTempMaps(-1);
#endif

	AfxWinTerm();
	return nReturnCode;
}

/

AfxWinMain 就是WinMain函数(F12可以看定义,_tWinMain等等可以忽略),可能是为了避免MFC ”非纯面向对象“ 程序设计框架(纯面向对象语言是没用全局变量、全局函数),隐藏主函数也是一种办法吧!

开始入口:

   CWinThread* pThread = AfxGetThread();
   CWinApp* pApp = AfxGetApp();

CWinThread  是  CWinApp 的父类,可以暂时不关心。

CWinApp  其实是创建MFC框架时,应用程序类的父类:

并且,在应用程序的源文件中,可以找到一个全局对象的定义:

  AfxGetApp() 的函数功能可以猜到了吧,父类的指针 指向 派生类的 对象!

多态的思想,这时候父类指针调用的函数 就 ”自动地切换“ 到程序员写的代码里去了。

否则,MFC这么知道程序员定义了一个什么类?怎么去调用这个类定义的函数作为程序的切入点?

AfxWinInit 函数在同层目录下的appinit.cpp文件里,这个函数的功能可以忽略。

下面就是InitApplication,派生类没有改写,调用  CWinApp 类(同层目录appcore.cpp),这个可以忽略。

重点当然是 InitInstance,这个函数在派生类中进行了改写,那么转入派生类应用程序类中的函数调用:(以单文档为例,其他都类似)

BOOL CMFC01App::InitInstance()
{
	// 如果一个运行在 Windows XP 上的应用程序清单指定要
	// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
	//则需要 InitCommonControlsEx()。否则,将无法创建窗口。
	INITCOMMONCONTROLSEX InitCtrls;
	InitCtrls.dwSize = sizeof(InitCtrls);
	// 将它设置为包括所有要在应用程序中使用的
	// 公共控件类。
	InitCtrls.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&InitCtrls);

	CWinApp::InitInstance();


	// 初始化 OLE 库
	if (!AfxOleInit())
	{
		AfxMessageBox(IDP_OLE_INIT_FAILED);
		return FALSE;
	}

	AfxEnableControlContainer();

	EnableTaskbarInteraction(FALSE);

	// 使用 RichEdit 控件需要  AfxInitRichEdit2()	
	// AfxInitRichEdit2();

	// 标准初始化
	// 如果未使用这些功能并希望减小
	// 最终可执行文件的大小,则应移除下列
	// 不需要的特定初始化例程
	// 更改用于存储设置的注册表项
	// TODO: 应适当修改该字符串,
	// 例如修改为公司或组织名
	SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
	LoadStdProfileSettings(4);  // 加载标准 INI 文件选项(包括 MRU)


	// 注册应用程序的文档模板。文档模板
	// 将用作文档、框架窗口和视图之间的连接
	CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CMFC01Doc),
		RUNTIME_CLASS(CMainFrame),       // 主 SDI 框架窗口
		RUNTIME_CLASS(CMFC01View));
	if (!pDocTemplate)
		return FALSE;
	AddDocTemplate(pDocTemplate);


	// 分析标准 shell 命令、DDE、打开文件操作的命令行
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);



	// 调度在命令行中指定的命令。如果
	// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
	if (!ProcessShellCommand(cmdInfo))
		return FALSE;

	// 唯一的一个窗口已初始化,因此显示它并对其进行更新
	m_pMainWnd->ShowWindow(SW_SHOW);
	m_pMainWnd->UpdateWindow();
	return TRUE;
}

比较重要的是所谓的”三口组“,CSingleDocTemplate(单文档模板)捆绑框架、视图、数据,一起生成对象。这里面当然最核心的问题就是:窗户函数,涉及到消息的处理,这也是程序员非常需要关注的。

要找窗口函数,肯定要找窗口类的注册,查找的过程可以参考《深入浅出MFC》,结论是:从WINFRM.CPP 文件的Create 到  WINCORE.CPP 里的 CreateEx函数:

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
	LPCTSTR lpszWindowName, DWORD dwStyle,
	int x, int y, int nWidth, int nHeight,
	HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
	ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) || 
		AfxIsValidAtom(lpszClassName));
	ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName));
	
	// allow modification of several common create parameters
	CREATESTRUCT cs;
	cs.dwExStyle = dwExStyle;
	cs.lpszClass = lpszClassName;
	cs.lpszName = lpszWindowName;
	cs.style = dwStyle;
	cs.x = x;
	cs.y = y;
	cs.cx = nWidth;
	cs.cy = nHeight;
	cs.hwndParent = hWndParent;
	cs.hMenu = nIDorHMenu;
	cs.hInstance = AfxGetInstanceHandle();
	cs.lpCreateParams = lpParam;

	if (!PreCreateWindow(cs))
	{
		PostNcDestroy();
		return FALSE;
	}

	AfxHookWindowCreate(this);
	HWND hWnd = CreateWindowEx(cs.dwExStyle, cs.lpszClass,
			cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
			cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);

#ifdef _DEBUG
	if (hWnd == NULL)
	{
		TRACE(traceAppMsg, 0, "Warning: Window creation failed: GetLastError returns 0x%8.8X\n",
			GetLastError());
	}
#endif

	if (!AfxUnhookWindowCreate())
		PostNcDestroy();        // cleanup if CreateWindowEx fails too soon

	if (hWnd == NULL)
		return FALSE;
	ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
	return TRUE;
}

然后又转回WINFRM.CPP 等等,这个过程对初学者而言可以忽略,核心问题是了解MFC的程序流程。

wndcls.lpfnWndProc = DefWindowProc;  这就是答案。

后续《深入浅出MFC》中很大篇幅来讲解MFC的消息处理机制(三种类型消息),这个也暂时可以忽略。类向导-》消息响应、虚函数的使用就比较清晰了:

 可以不问来由,只管使用了,希望简单点,直接响应!否则改写DefWindowProc函数抓取所有窗口消息,也非常的灵活淡定!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值