调皮的MFC(2)

上回书说到,WinMain和窗口处理函数分别被相应的类操作和宏操作所取代。这次我们就来看一下,取代之后的MFC程序是如何运行的吧。

先把类继承图贴出来,随时可以回顾一下。

主cpp文件中的第一个操作语句:

// The one and only CMyWinApp object
CMyWinApp theApp;

1.生成一个对象自然是要调用构造函数的,但是不仅要调用自身的构造函数,还得调用父类的构造函数。虽说一个自动生成的类,其构造函数是空的,但是父类的构造函数还是可以完成一些初始化操作的。

打开MFC框架中的APPCORE.CPP文件,可以看到CWinApp的构造函数定义。其关部分操作如下:

CWinApp::CWinApp(LPCTSTR lpszAppName)
{
	if (lpszAppName != NULL)
		m_pszAppName = _tcsdup(lpszAppName);
	else
		m_pszAppName = NULL;

	// initialize CWinThread state
	ASSERT(AfxGetThread() == NULL);
	pThreadState->m_pCurrentWinThread = this;
	ASSERT(AfxGetThread() == this);
	m_hThread = ::GetCurrentThread();
	m_nThreadID = ::GetCurrentThreadId();

	// initialize CWinApp state
	ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
	pModuleState->m_pCurrentWinApp = this;
	ASSERT(AfxGetApp() == this);

	// in non-running state until WinMain
	m_hInstance = NULL;
	m_pszHelpFilePath = NULL;
	m_pszProfileName = NULL;
	m_pszExeName = NULL;
	m_lpCmdLine = NULL;
	m_pCmdInfo = NULL;
……
}

由上可见,CWinApp中的成员变量因为theApp全局对象的出现而获得配置与初值。如果程序中没有theApp存在,编译链接还是可以顺利通过,但执行时会出现系统错误消息。

2.theApp配置完成后,接下来就该WinMain出场了。但是我们并未编写相应的代码,而是由MFC准备好并由链接器直接加到应用程序代码中的,其代码可在APPMODUL.CPP中查看:

extern "C" int WINAPI
//_t是为了支持Unicode准备的一个宏
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPTSTR lpCmdLine, int nCmdShow)
{
	// call shared/exported WinMain
	return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}

3.由上可见,函数体中调用了AfxWinMain函数(在WINMAIN.CPP中),该函数的主要部分如下:

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	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())//执行MFC内部管理
		goto InitFailure;

	// Perform specific initializations
	if (!pThread->InitInstance())//执行与窗口相关的操作
	{
		if (pThread->m_pMainWnd != NULL)
		{
			TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");
			pThread->m_pMainWnd->DestroyWindow();
		}
		nReturnCode = pThread->ExitInstance();
		goto InitFailure;
	}
	nReturnCode = pThread->Run();//消息循环的开启

InitFailure:
	AfxWinTerm();
	return nReturnCode;
}

4.在上述函数体中又调用了AfxWinInit,该函数定义于APPINIT.CPP中,主要部分如下:

BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	// fill in the initial state for the application
	CWinApp* pApp = AfxGetApp();
	if (pApp != NULL)
	{
		// Windows specific initialization (not done if no CWinApp)
//为应用对象的部分成员赋值
		pApp->m_hInstance = hInstance;
		pApp->m_hPrevInstance = hPrevInstance;
		pApp->m_lpCmdLine = lpCmdLine;
		pApp->m_nCmdShow = nCmdShow;
		pApp->SetCurrentHandles();
	}
	// initialize thread specific data (for main thread)
	if (!afxContextIsDLL)
		AfxInitThread();//调用==== >
	return TRUE;
}

5.最后又调用了AfxInitThread函数。该函数定义于THRDCORE.CPP中,内容如下:

void AFXAPI AfxInitThread()
{
	if (!afxContextIsDLL)
	{
		// set message filter proc
		_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
		ASSERT(pThreadState->m_hHookOldMsgFilter == NULL);
		pThreadState->m_hHookOldMsgFilter = ::SetWindowsHookEx(WH_MSGFILTER,
			_AfxMsgFilterHook, NULL, ::GetCurrentThreadId());

#ifndef _AFX_NO_CTL3D_SUPPORT
		// intialize CTL3D for this thread
		_AFX_CTL3D_STATE* pCtl3dState = _afxCtl3dState;
		if (pCtl3dState->m_pfnAutoSubclass != NULL)
			(*pCtl3dState->m_pfnAutoSubclass)(AfxGetInstanceHandle());

		// allocate thread local _AFX_CTL3D_THREAD just for automatic termination
		_AFX_CTL3D_THREAD* pTemp = _afxCtl3dThread;
		pTemp;  // avoid unused warning
#endif
	}
}

函数中主要初始化了线程的相关状态,顺便还设置了个“钩子”。钩子函数的声明如下:

HHOOK WINAPI SetWindowsHookEx(
__in int idHook, //钩子的类型,即它处理的消息类型
__in HOOKPROC lpfn, //回调函数地址
__in HINSTANCE hMod, //应用程序实例的句柄。如果dwThreadId 标识当前进程创建的一个线程,而且子程代码位于当前进程,hMod必须为NULL。
__in DWORD dwThreadId); //与安装的钩子子程相关联的线程ID

函数体中设置的WH_MSGFILTER Hook使我们能够监视传递到菜单,滚动条,消息框的消息,以及传递到通过安装了Hook子程的应用程序建立的对话框的消息。

6.好了,到此为止AfxWinInit函数的调用就彻底结束了,接下来调用的是InitApplication函数。

pApp->InitApplication();

其中InitApplication是虚函数,而本类中并未改写,所以它将调用父类的对应InitApplication函数,即相当于:CWinApp:: InitApplication();

该函数定义于APPCORE.CPP中,内容如下:

BOOL CWinApp::InitApplication()
{
	if (CDocManager::pStaticDocManager != NULL)
	{
		if (m_pDocManager == NULL)
			m_pDocManager = CDocManager::pStaticDocManager;
		CDocManager::pStaticDocManager = NULL;
	}
	if (m_pDocManager != NULL)
		m_pDocManager->AddDocTemplate(NULL);
	else
		CDocManager::bStaticInit = FALSE;
	return TRUE;
}

其中的操作都是MFC内部的相关管理操作。

7.下一步操作是:pThread->InitInstance();

AfxGetThread()返回的是当前界面线程对象的指针,AfxGetApp()返回的是应用程序对象的指针,如果该应用程序(或进程)只有一个界面线程在运行,那么这两者返回的都是一个全局的应用程序对象指针,这个全局的应用程序对象就是MFC应用框架所默认的theApp对象。

当我们在调用AfxGetThread时,自然还只有一个界面线程在运行。也就是说,上述的语句调用也相当于:pApp->InitInstance();好了,这个时候终于第一次可以实实在在的在我们自己的项目中看到要分析的代码了。在继承CWndApp的那个类的cpp文件中会自动生成InitInstance函数的部分代码,主要内容如下:

BOOL CMyWinApp::InitInstance()
{
CMyFrameWnd* pMyFrameWnd = new CMyFrameWnd;//注册相关的窗口类
pMyFrameWnd ->ShowWindow(m_nCmdShow);
pMyFrameWnd ->UpdateWindow();
return TRUE;
}

8.很明显,接下来就该调用CMyFrameWnd的默认构造函数了。在本类中,自动生成的代码什么都没做,但是其父类CFrameWnd的相关操作也会被调用。在CFrameWnd构造函数中的关键步骤就是调用了CFrameWnd::Create函数。该函数的主要操作如下:

BOOL CFrameWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext* pContext)
/*
第一个参数:指定WNDCLASS窗口类,放置NULL是要以MFC內建的窗口类产生一个标准的外框窗口。
第二个参数:指定窗口标题
第三给参数:指定窗口风格,默认是WS_OVERLAPPEDWINDOW
第四个参数:指定窗口的位置与大小
第五个参数:指定父窗口。对于一个top-level窗口而言,此值应为NULL,表示没有(其实父窗口是desktop)。
第六个参数:指定菜单
第七个参数:指定窗口的扩充风格
第八个参数:指向一个CCreateContext结构的指针,Framework利用它,在具备Document/View结构的程序中初始化外框窗口。
*/
{
HMENU hMenu = NULL;
if (lpszMenuName != NULL)
{
//搜索包含该菜单资源的实例(当前进程或者按进行装入的DLL)
HINSTANCE hInst = AfxFindResourceHandl(lpszMenuName, RT_MENU);
//装入菜单资源
if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
{
TRACE0("Warning: failed to load menu for CFrameWnd.\n");
PostNcDestroy(); //perhaps delete to C++ object
return FALSE;
}
}
m_strTitle = lpszWindowName; //存储窗口标题,以备后用(如刷新显示)
// 调用CWnd::CreateEx()
if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right – rect.left, rect,bottom – rect.top, pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
{
if (hMenu != NULL)
DestroyMenu(hMenu); //如果创建失败,释放菜单资源
return FALSE;
}
return TRUE;
}

9.在上函数中,最重要的操作就是又调用了CreateEx函数。但是从CFrameWnd开始就没有重新定义该函数,所以调用的还是CWnd中定义的CreateEx函数。该函数定义于WINCORE.CPP中,主要的操作如下:

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)
{
	// 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);
……
}

10.在以上函数中又先后调用了PreCreateWindow和::CreateWindowEx两个函数。其中PreCreateWindow的主要操作定义于WINFRM.CPP,主要内容如下:

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
	if (cs.lpszClass == NULL)
	{
		VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
		cs.lpszClass = _afxWndFrameOrView;  // COLOR_WINDOW background
	}
……
}

11.其中又调用了AfxDeferRegisterClass函数(其实这是一个宏)。它定义于AFXIMPL.H中,如下:

#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)

12.其中又有AfxEndDeferRegisterClass函数的调用,该函数定义于WINCORE.CPP中,主要操作如下:

BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{
	// mask off all classes that are already registered
	AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
	fToRegister &= ~pModuleState->m_fRegisteredClasses;
	if (fToRegister == 0)
		return TRUE;

	LONG fRegisteredClasses = 0;

	// common initialization
	WNDCLASS wndcls;
	memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults
	wndcls.lpfnWndProc = DefWindowProc;
	wndcls.hInstance = AfxGetInstanceHandle();
	wndcls.hCursor = afxData.hcurArrow;

	INITCOMMONCONTROLSEX init;
	init.dwSize = sizeof(init);

	// work to register classes as specified by fToRegister, populate fRegisteredClasses as we go
	if (fToRegister & AFX_WND_REG)
	{
		// Child windows - no brush, no icon, safest default class styles
		wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
		wndcls.lpszClassName = _afxWnd;
		if (AfxRegisterClass(&wndcls))
			fRegisteredClasses |= AFX_WND_REG;
	}
	if (fToRegister & AFX_WNDOLECONTROL_REG)
	{
		// OLE Control windows - use parent DC for speed
		wndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
		wndcls.lpszClassName = _afxWndOleControl;
		if (AfxRegisterClass(&wndcls))
			fRegisteredClasses |= AFX_WNDOLECONTROL_REG;
	}
	if (fToRegister & AFX_WNDCONTROLBAR_REG)
	{
		// Control bar windows
		wndcls.style = 0;   // control bars don't handle double click
		wndcls.lpszClassName = _afxWndControlBar;
		wndcls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
		if (AfxRegisterClass(&wndcls))
			fRegisteredClasses |= AFX_WNDCONTROLBAR_REG;
	}
	if (fToRegister & AFX_WNDMDIFRAME_REG)
	{
		// MDI Frame window (also used for splitter window)
		wndcls.style = CS_DBLCLKS;
		wndcls.hbrBackground = NULL;
		if (_AfxRegisterWithIcon(&wndcls, _afxWndMDIFrame, AFX_IDI_STD_MDIFRAME))
			fRegisteredClasses |= AFX_WNDMDIFRAME_REG;
	}
	if (fToRegister & AFX_WNDFRAMEORVIEW_REG)
	{
		// SDI Frame or MDI Child windows or views - normal colors
		wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
		wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
		if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME))
			fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;
	}
	if (fToRegister & AFX_WNDCOMMCTLS_REG)
	{
		// this flag is compatible with the old InitCommonControls() API
		init.dwICC = ICC_WIN95_CLASSES;
		fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WIN95CTLS_MASK);
		fToRegister &= ~AFX_WIN95CTLS_MASK;
	}
……
}

以上函数在创建窗口之前,根据不同功能的窗口会注册一些窗口类。其中主要用到了两个函数_AfxRegisterWithIcon和AfxRegisterClass。

13.在函数_AfxRegisterWithIcon内部其实还是调用了AfxRegisterClass函数。该函数的定义如下:

AFX_STATIC BOOL AFXAPI _AfxRegisterWithIcon(WNDCLASS* pWndCls,
	LPCTSTR lpszClassName, UINT nIDIcon)
{
	pWndCls->lpszClassName = lpszClassName;
	HINSTANCE hInst = AfxFindResourceHandle(
		MAKEINTRESOURCE(nIDIcon), RT_GROUP_ICON);
	if ((pWndCls->hIcon = ::LoadIcon(hInst, MAKEINTRESOURCE(nIDIcon))) == NULL)
	{
		// use default icon指定icon
		pWndCls->hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
	}
	return AfxRegisterClass(pWndCls);
}

14.AfxRegisterClass中对本文有意义的代码如下:

BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass)
{
	WNDCLASS wndcls;
	if (GetClassInfo(lpWndClass->hInstance, lpWndClass-> lpszClassName, &wndcls))
	{
		// class already registered
		return TRUE;
	}

	if (!::RegisterClass(lpWndClass))
	{
		TRACE1("Can't register window class named %s\n",
			lpWndClass->lpszClassName);
		return FALSE;
	}
……
	return TRUE;
}

函数中通过调用RegisterClass注册窗口类,以便在后面供CreateWindowEx函数使用,来创建相应的窗口对象。至此Create函数执行完了,CMyFrameWnd对象也构造完成。

15.后面的执行语句是:

pMyFrameWnd ->ShowWindow(m_nCmdShow);
pMyFrameWnd ->UpdateWindow();

调用ShowWindow函数来显示窗口,并调用UpdateWindow函数给程序送去了WM_PAINT消息,驱动窗口的绘制。到此InitInstance函数终于执行完毕,剩下最后一步操作:pThread->Run();

16.和上面的pThread->InitInstance();调用一样,这里调用的也是pApp-> Run();由于本类中没有重写Run虚函数,所以还是调用父类CWinApp中的Run函数。该函数定义于APPCORE.CPP中,内容如下:

int CWinApp::Run()
{
	if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
	{
		// Not launched /Embedding or /Automation, but has no main window!
		TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");
		AfxPostQuitMessage(0);
	}
	return CWinThread::Run();
}

17.正常情况下,上面的函数会调用CWinThread::Run函数。该函数定义于THRDCORE.CPP,主要内容如下:

int CWinThread::Run()
{
	ASSERT_VALID(this);

	// for tracking the idle time state
	BOOL bIdle = TRUE;
	LONG lIdleCount = 0;

	// acquire and dispatch messages until a WM_QUIT message is received.
	for (;;)
	{
		// phase1: check to see if we can do idle work
		while (bIdle &&
			!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
		{
			// call OnIdle while in bIdle state
			if (!OnIdle(lIdleCount++))
				bIdle = FALSE; // assume "no idle" state
		}

		// 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));
	}

	ASSERT(FALSE);  // not reachable
}

终于在其中看见了我们朝思暮想的PeekMessage的熟悉身影。小乔出现了,那么大乔呢?别着急,看看PumpMessage就行了,人家只是有点害羞而已。

18.PumpMessage的定义在同一个文件中,主要内容如下:

BOOL CWinThread::PumpMessage()
{
	ASSERT_VALID(this);

	if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
	{
return FALSE;
	}
……
	if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
	{
		::TranslateMessage(&m_msgCur);
		::DispatchMessage(&m_msgCur);
	}
	return TRUE;
}

是的除了GetMessage,里面还有TranslateMessage和DispatchMessage姐妹花,该出现的都出现了。现在消息循环也已经开启了,程序成功跑起来了,收工。

不得不说,写到这里感觉身体被掏空了,我要去喝杯水,思考一下人生了。

时间: 2024-10-17 09:55:47

调皮的MFC(2)的相关文章

《深入浅出MFC》第七章 简单而完整:MFC骨干程序

不二法门:熟记MFC类层次结构.经常使用的主要MFC类:CWinApp, CWnd<-CView, CWnd<-CFrameWnd, CFrameWnd<-CMDIFrameWnd, CFrameWnd<-CMDIChildWnd, CWnd<-CDialog, CWnd<-CControlBar, CControlBar<-CStatusBar, CControlBar<-CToolBar, CCmdTarget<-CDocument, CCmdTa

深入浅出MFC——消息映射与命令传递(六)

1. 消息分类: 2. 万流归宗——Command Target(CCmdTarget): 3. "消息映射"是MFC内建的一个信息分派机制.通过三个宏(DECLARE_MESSAGE_MAP/BEGIN.../ON.../END...)完成消息网的建构. 4. 注意:CWinThread派生自CCmdTarget,但没有DECLARE_/BEGIN_/END_宏组. 5. 消息映射与虚函数: 6.

MFC控件使用技巧:List Control

1)每列内容过长,显示不完整 只有加载数据的情况下,才会出现水平滚动条 解决方案: 可以添加如下一个空的内容项: m_List.InsertItem(0,NULL);//为了显示进度条 2)不允许点击修改第一列(当然最多能够让我们改动的也只有他了) MFC默认情况下可以修改第一列,其他的不允许修改(需要定制DrawItem) 解决方案: Edit Labels 属性设置为 False 3)报表的形式表示 View 属性设置为 Report 4)注意:不要和列表框控件混淆(英文名: List Bo

【mfc】用对话框分页实现用户登录

所谓的对话框分页就是点击完一个对话框的按钮,切换到另一个对话框, 这样的对话框多用于一些需要用户登录才能够进行操作的软件, 下面就用对话框分页来实现用户登录系统 一.基本目标 有如下的程序,输入用户名与密码,如果用户名为admin,密码为123456,那么则成功登录,切换到一个有"欢迎登录"与"关闭"按钮的对话框 如果用户名或者密码输入错误则弹出提示, 点击关闭能够关闭这个程序,不弹出用户登录的对话框. 二.制作过程 1.首先如同之前的<[mfc]对于对话框程

MFC中给控件添加变量,DoDataExchange中

DoDataExchange函数其实是一项数据动态绑定技术.比如你在写动态按钮过程中须对按钮添加变量时,怎么添加?控件类已经写好了,其变量是已经固定的.你要添加新的变量就要用到DoDataExchange函数. 你要在对话框的构造函数里面初始化一个变量,再用DoDataExchange函数将它绑定到你的动态按扭中,比如:DDX_Check(pDX, IDC_CHECK1, m_Lesson1);这就是将m_Lesson1(这是一个外部变量,其定义在对话框的构造函数里)绑定到IDC_CHECK1中

MFC消息映射的原理:笔记

多态的实现机制有两种,一是通过查找绝对位置表,二是查找名称表:两者各有优缺点,那么为什么mfc的消息映射采用了第二种方法,而不是c++使用的第一种呢?因为在mfc的gui类库是一个庞大的继承体系,而里面的每个类有很多成员函数(只说消息反映相关的成员函数啊),而且在派生类中,需要改写的也比较少(我用来做练习的程序就是那么一两个,呵呵).那么用c++的虚函数的实现机制会导致什么问题呢?就是大量虚表的建立使得空间浪费掉很多. 嗯-怎么办呢?于是各大c++名库(比如QT,MFC,VCL-)在消息映射的实

MFC-消息分派

前言 由于工作需要,这几天学了一点MFC,在AFX里看到很多熟悉的东西,如类型信息,序列化,窗口封装和消息分派.几乎每个界面库都必须提供这些基础服务,但提供的手法却千差万别.MFC大量地借用了宏,映射表来实现,而VCL则更多的在语言级别上给与支持.这其实是很容易理解的,因为C++是一个标准,不会因某个应用而随便扩展语言:相反Delphi完全由一个公司掌握,因此每支持一项新技术,变化最大的往往是语言本身. 学习MFC的代码,再对照VCL的实现,这真是一个很有意思的过程,其中可以看到两个框架在一些设

MFC中的DC CDC HDC由来由去理解

MFC中的DC CDC HDC由来由去理解 在此非常感谢博客主的究竟钻研,非常详细的参考资料:http://blog.csdn.net/yam_killer/article/details/7661449

(七):处理MFC

(一):简单介绍 为了可以在一个Winelib应用中使用MFC,你须要首先使用Winelib又一次编译MFC. 在理论上,你应该为Windows的MFC编写一个封装(怎样编写在后面介绍).可是,在实践中,对MFC来说.可能不是一个真正的解决方案: 数量巨大的API使得编写封装的工作量非常大 进一步说.MFC包括大量的API.这些API在制作封装的时候处理起来是非常复杂的. 即使你编写了封装,你也须要去改动MFC的头文件以使编译器不会堵塞他们. 在你应用中的非常大一部分MFC代码是以宏的形式展现的