深入浅出MFC笔记2-MFC程序如何包装Win32程序

从上一节的Win32程序与MFC程序的对比中可以看到,MFC程序中可运行代码只有MyApp::InitInstance()和MyFrame::MyFrame(),可以想到MFC程序一定会调用此两个函数,如果你手中有一个好的集成开发环境(如果没有,那你最好去安装一个^_^),可以在这两个函数中设置断点,然后运行程序,程序首先会在InitInstance函数中中断,此时查看程序调用堆栈,可以看到如下图所示的函数调用顺序:

嗯!我们看到堆栈中有Win32程序入口函数WinMain,说明MFC已经为我们定义了WinMain函数,MFC程序也是从WinMain函数开始运行。

然后进入函数WinMain发现其只是调用了AfxWinMain函数,其代码如下:

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

主要的函数调用过程如下:

AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
pApp->InitApplication();
pThread->InitInstance();
pThread->Run();
AfxWinTerm();

这里先不对每个调用进行说明,只用记住这里有这些过程调用。

这部分里面就有pThread->InitInstance()调用,可以知道pThread一定是指向MyApp对象的(CWinThread是CWinApp的基类,CWinApp是MyApp的基类)。

这里顺便说明下MFC的类层次结构:

CObject

|-CCmdTarget

  |-CWinThread

  | |-CWinApp

  |

  |-CWnd

  | |-CFrameWnd

  | |-Cview

  | |-CDialog

  |

  |-CDocument

  |

  |-CDocTemplate

    |-CSingleDocTemplate

    |-CMultiDocTemplate

上面只是列出了和MFC底层机制相关的部分类,更加详细的内容可以参看MFC帮助文档。

在InitInstance中我们创建了一个MyFrame对象赋值给m_pMainWindow指针,之后显示更新窗口,我们有理由猜测注册窗口类别和创建Windows窗口对象的过程是在创建MyFrame对象时完成的,下面深入MyFrame的创建过程。

还是在集成环境中执行进入MyFrame的创建过程,最终可以看到如下图所示的调用堆栈:

可以看到依次调用CFrameWnd::Create,CWnd::CreateEx,CFrameWnd::PreCreateWindow,AfxEndDefRegisterClass,_AfxRegisterWithIcon函数,_AfxRegisterWithIcon代码如下:

AFX_STATIC BOOL AFXAPI _AfxRegisterWithIcon(WNDCLASS* pWndCls,
	LPCTSTR lpszClassName, UINT nIDIcon)
{
	pWndCls->lpszClassName = lpszClassName;
	HINSTANCE hInst = AfxFindResourceHandle(
		ATL_MAKEINTRESOURCE(nIDIcon), ATL_RT_GROUP_ICON);
	if ((pWndCls->hIcon = ::LoadIcon(hInst, ATL_MAKEINTRESOURCE(nIDIcon))) == NULL)
	{
		// use default icon
		pWndCls->hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
	}
	return AfxRegisterClass(pWndCls);
}
上面的代码中就有AfxRegisterClass函数调用,此函数又调用了::AfxCtxRegisterClass,这个函数是用宏定义的,调用了RegisterClass函数注册窗口。

定义的宏如下:AFX_ISOLATIONAWARE_STATICLINK_FUNC(ATOM ,RegisterClassW,(const WNDCLASSW*lpWndClass),(lpWndClass),0)

书中写的是在AfxRegisterClass函数中直接调用了::RegisterClass,和现在分析的并不一样,可能是版本更新后的调整。

现在注册窗口类的代码已经找到,下面是创建Windows对象的过程。

通过上面的调用堆栈进入CWnd::CreateEx函数,在调用CFrameWnd::PreCreateWindow函数后有::AfxCtxCreateWindowEx调用,此函数和上面::AfxCtxRegisterClass一样是通过宏定义的,实际上就是调用了::CreateWindowEx,这样创建窗口的代码也找到了,接着看下窗口函数的指定。

在注册窗口前有调用AfxEndDeferRegisterClass,代码如下:

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;
...
}
嗯?上面用的窗口函数竟然是DefWindowProc!这其中的玄机等学习MFC的消息处理时再来细说。

现在我们再MFC程序中也找的WinMain入口函数,在窗口类中设置了窗口处理函数,注册窗口类RegisterClass函数,创建窗口CreateWindowEx函数,显示窗口函数ShowWindow以及更新窗口函数UpdateWindow,在第一节的问题中只剩下消息循环的启动了。

我们回到AfxWinMain函数中,在pThread->InitInstance后可以看到调用了pThread->Run函数,深入此函数,看见调用堆栈如下:

栈顶函数AfxInternalPumpMessage代码如下:

BOOL AFXAPI AfxInternalPumpMessage()
{
	_AFX_THREAD_STATE *pState = AfxGetThreadState();

	if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
	{
		// Note: prevents calling message loop things in 'ExitInstance'
		// will never be decremented
		return FALSE;
	}

  // process this message

	if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))
	{
		::TranslateMessage(&(pState->m_msgCur));
		::DispatchMessage(&(pState->m_msgCur));
	}
  return TRUE;
}
可以看到上面的代码中就有Win32程序进行消息提取的调用过程,循环在CWinThread::Run中进行。

对我们MFC的程序执行过程作一个总结:

从WinMain开始,调用AfxWinMain,调用重载的MyApp::InitInstance,创建MyFrame对象,调用CFrameWnd::Create,调用CWnd::CreateEx,调用CFrameWnd::PreCreateWindow,调用::RegisterClass注册窗口,调用::CreateWindowEx创建窗口,调用CWinApp::Run,调用CWinThread::Run(循环),调用::GetMessage获取消息进行处理。以上除了全局函数外,其他函数都可以在MyFrame和MyApp进行重写从而修改程序的执行。


至此,我们已经在MFC的代码中找到了Win32程序的各个必要的组成部分,但是对消息处理部分还不甚明了,接着来详细分析下MFC的消息处理过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值