MFC窗口程序启动运行机制剖析

本文的测试环境:

IDE:VS2010 + VC助手(GO按钮对后面的跟踪调试很有用)

操作系统:Win8.1

一、创建工程、环境设置、测试代码和调试基础

这里首先我们创建一个Win32项目,这里我的工程名为MFCBase,应用程序类型选择"Windows 应用程序",附加选项选择"空项目"  就好。

然后设置下工程:项目-->MFCBase属性-->在配置属性下面的常规选项卡的右边,把项目默认值的"MFC的使用"更改为"在静态库中使用MFC","字符集"一栏更改为"未设置"。


我之所以这样设置是因为只有当我们使用MFC静态库才能查看到微软提供给我们的部分源代码,设置字符集是方便我们后面的字符串的处理。

给工程添加一个名为MFCBase.cpp的C++源文件,编写如下代码:

#include <afxwin.h>
class CMyFrameWnd : public CFrameWnd
{};
class CMyWinApp : public CWinApp
{
public:
	CMyWinApp();
	virtual BOOL InitInstance();
};
CMyWinApp::CMyWinApp()
{}
CMyWinApp theApp;
BOOL CMyWinApp::InitInstance()
{
	CMyFrameWnd *pFrame = new CMyFrameWnd;
	pFrame->Create(NULL, "MFCBase");
	m_pMainWnd = pFrame;
	pFrame->ShowWindow(SW_SHOW);
	pFrame->UpdateWindow();
	return TRUE;
}

开头之所以包含afxwin.h 是因为afxwin.h 中包含了afx.h、windows.h等头文件可以方便的提供对MFC类库和Win32 API函数调用的支持

编译运行起来,你会发现是一个窗口程序,就像我们初学Win32时写的WinHello一样,虽然我们没有设计窗口类,注册窗口类,创建并显示窗口,甚至是保证程序不退出的消息循环。但是你看到的确实是一个运行良好的窗口程序。

下面会用到一些VC的一些基础调试知识和快捷键,这里以快捷建的方式大概罗列下:

F9   : 给程序的某行代码下断(点),当程序运行到此处就会停下来

F10 : 单步步过,就是执行一行代码(遇到函数也不会进入其中)

F11:  步入,遇到函数按此键可以进入函数内

Shift+F11 : 跳出当前函数,返回到上一层

Ctrl+F10 : 让程序运行到当前光标停留的行

Ok,常用的基本上就是这些了,并且VC调试工具栏都有相应的按键

下面开始调试追踪整个程序的启动运行流程 :

二、全局对象theApp的构造过程分析

我们先在如下两行代码处设置两个断点,


按F5调试运行

程序将首先停在将全局对象theApp实例化(第12行)处,按F11来观察其构造函数的构造,程序停留在CMyWinApp的构造函数的入口处(第13行),再按F11,那么程序必然会停留到CMyWinApp的父类CWinApp的构造函数中。

因为:当子类在实例化对象的时候,父类构造函数先于子类被调用,析构函数的调用顺序则相反。

我们进入到CWinApp的构造函数(这里包括后面,我将只提取关键代码来分析,只关心和程序运行流程关系最密切的代码有助于我们理清思路):

①代码片断:

CWinApp::CWinApp(LPCTSTR lpszAppName)
{// 内部this指针为theApp
	............................
	// 获取当前程序模块状态信息地址
	AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
	............................
        // 获取当前程序模块状态信息的成员m_thread:当前程序模块线程状态信息地址
	AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
	............................
        // 把theApp对象的地址存入当前程序模块线程状态信息的m_pCurrentWinThread成员中
	pThreadState->m_pCurrentWinThread = this;
	// 再次获取保存在前程序模块线程状态信息m_pCurrentWinThread中theApp对象地址
	ASSERT(AfxGetThread() == this);
    ............................
	// 把theApp对象的地址存入当前程序模块状态信息的m_pCurrentWinApp成员中
	pModuleState->m_pCurrentWinApp = this;
	// 再次获取保存在前程序模块状态信息m_pCurrentWinApp中theApp对象地址
	ASSERT(AfxGetApp() == this);
    ............................
}

①->1 (片段①拆解分析)

// 获取当前程序模块状态信息地址
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();

其中_AFX_CMDTARGET_GETSTATE被宏替换为AfxGetModuleState()

AFX_MODULE_STATE* AFXAPI AfxGetModuleState()
{// MFC全局函数
	// 获取当前程序线程状态信息对象地址
	_AFX_THREAD_STATE* pState = _afxThreadState;
	............................
	// 获取保存在前程序线程状态信息对象中的m_pModuleState
	pResult = pState->m_pModuleState;
    ............................
	return pResult;
}

微软在MFC中定义了一个全局对象:  当前程序线程状态信息

当前程序线程状态信息有一个成员:当前程序模块状态信息

当前程序模块状态信息有一个成员:当前程序模块线程状态信息

我用类的包含关系表示大概是这样的:

当前程序线程状态信息
{
 public:
     m_pModuleState//当前程序模块状态信息
   {
            public:
                  m_thread//当前程序模块线程状态信息
     };
};

①->2(片段①拆解分析)

  // 再次获取保存在前程序模块线程状态信息m_pCurrentWinThread中theApp对象地址
  ASSERT(AfxGetThread() == this);

其实这一行就是确认是否真的成功的把theApp对象的指针保存到了当前程序模块线程状态信息中,AfxGetThread()函数:

CWinThread* AFXAPI AfxGetThread()
{// MFC全局函数
	// 获取当前程序模块线程状态对象地址
	AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
	// 获取保存在当前程序模块线程状态对象中的m_pCurrentWinThread(theApp对象地址)
	CWinThread* pThread = pState->m_pCurrentWinThread;
	return pThread;
}

①->3(片段①拆解分析)

    // 再次获取保存在前程序模块状态信息m_pCurrentWinApp中theApp对象地址
    ASSERT(AfxGetApp() == this);

这里同AfxGetThread()函数差不多,也是为了确保上面的this指针成功的赋值。

AfxGetThread()内部直接return afxCurrentWinApp(),而afxCurrentWinApp()函数又被宏替换:

#define afxCurrentWinApp    AfxGetModuleState()->m_pCurrentWinApp

这样AfxGetThread()就拿到了保存在当前程序模块状态信息对象中的m_pCurrentWinApp成员中的theApp对象地址。


接下来我们可以一路按F10往下走退出CWinApp的构造函数回到我们的构造函数CMyWinApp::CMyWinApp(), 到此为止构造函数的分析就结束了。


总结上面的流程我们发现父类的构造函数中主要完成了一下几件事情:

1 把全局对象theApp对象的地址保存到了MFC全局对象:当前程序线程信息对象的成员p_mModuleState(当前程序模块状态信息)中的
m_pCurrentWinApp中了以及其成员m_thread(当前程序模块线程状态信息)的m_pCurrentWinThread中。

说其来太绕了,还是用类的方式来呈现一下吧:

_afxThreadState// 当前程序线程状态信息
{
 public:
     m_pModuleState// 当前程序模块状态信息
   {
    public:
		m_pCurrentWinApp = &theApp;
        m_thread// 当前程序模块线程状态信息
		{
		 public:
			 m_pCurrentWinThread = &theApp;
		};
     };
};


2 我们总结出一些函数的功能以及theApp对象的获取方式:

1 AfxGetModuleState()可以获取当前程序的模块状态信息对象地址:m_pModuleState

2  m_pModuleState->m_thead 获取当前程序模块线程状态信息对象的地址

3  AfxGetApp()可以获取保存在m_pModuleState->m_pCurrentWinApp 中的theApp对象地址

3  AfxGetThread()可以获取保存在m_pModuleState->m_thread->m_pCurrentWinThread中的theApp对象地址


好了,全局对象的构造过程就分析到这里。

下面我们来看看主函数的执行,我们会发现在我们的代码中根本就没有所谓的主函数,为了能找到主函数,我在CMyWinApp::InitInstance ()函数的入口处下个断点,先让程序运行到这里,那么当程序运行到这里的时候,主函数肯定是已经被调用过的了,那么我们就可以通过VS的查看调用堆栈(Call Stack)的功能进而定位到主函数的位置

查看调用栈:


发现主函数的调用流程大概如下:



我们来到AfxWinMain函数并在其入口点下断,我们先停止当前的调试(因为此时AfxWinMain已经执行过了),重新调试启动程序运行到这里

下面我这种分析AfxWinMain都做了那些事情:

②代码片段:

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	_In_ LPTSTR lpCmdLine, int nCmdShow)
{
        ..................................
	CWinThread* pThread = AfxGetThread(); // 获取保存在当前程序模块线程状态对象中的this(theApp)的地址
	CWinApp* pApp = AfxGetApp();          // 获取保存在当前程序模块状态对象中的this(theApp)的地址
        ....................................
        注意:上面两句都是在用父类对象指针接受子类对象的指针,那么可以为多态准备条件
        // App global initializations (rare)
	if (pApp != NULL && !pApp->InitApplication()) // 用theApp对象调用应用程序类的虚函数进行初始化,这里我们暂且不步入分析了
		goto InitFailure;

	if (!pThread->InitInstance()) // 这里同过theApp的地址(也就是当前我们的类对象的地址)调用虚函数(创建、显示窗口),这里将触发多态,
                                      // 如果我们自己定义了InitInstance()那么这里将回到我们的代码调用我们的代码,否则调用父类的(CWinThread::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(); // 这里跟进去发现就是消息循环

         .....................................................
	return nReturnCode;
}

MSDN中CWinThread和CWinApp的继承关系



其中由于我们重写了InitIntance()函数,当我们F11步入pThread->InitInstance()那行代码时将回到我们的代码进行窗口的创建和显示

在上面的代码中还有个函数值得我们深入研究:

②->1(片段②拆解分析)

nReturnCode = pThread->Run(); // 这里跟进去发现就是消息循环
这里由于pThread是指向父类CWinThread的指针且Run被声明为虚函数故可发生多态进而调用我们重写的代码

步入CWinApp::Run()发现其直接返回CWinThread::Run()调用

继续跟进CWinThread::Run ()

int CWinThread::Run() // 内部this指针为theApp
{
	.................................
	// 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(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
		{
			// call OnIdle while in bIdle state
			if (!OnIdle(lIdleCount++)) // 调用成员虚函数OnIdle()做空闲处理
				bIdle = FALSE; // assume "no idle" state
		}
		// phase2: pump messages while available
		do
		{
			// pump message, but quit on WM_QUIT
			if (!PumpMessage())  // 跟进去这里就是我们平时缩写的GetMessage消息循环,
				                 // 虽然为成员虚函数当不建议重写,应为我们没什么可多做的
				return ExitInstance();// 虚函数,做善后处理
			// reset "no idle" state after pumping "normal" message
			//if (IsIdleMessage(&m_msgCur))
			if (IsIdleMessage(&(pState->m_msgCur)))
			{
				bIdle = TRUE;
				lIdleCount = 0;
			}

		} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
	}
}
跟进发现PumpMessage()直接返回对AfxInternalPumpMessage()全局函数的调用

BOOL AFXAPI AfxInternalPumpMessage()
{
	..................................
	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;
}


到这里程序的脉络基本上也算是比较清晰的了,我们总结下AfxWinMain主要的框架流程:

     WinMain (........)
    {
         AfxWinMain()
        {
               CWinThread* pThread = AfxGetThread();
	       CWinApp* pApp = AfxGetApp(); 
               /* 以上两句都是获取theApp的地址 */
               pApp->InitApplication(); //利用theApp调用应用程序类的成员虚函数 (初始化)
              pThread->InitInstance();// 利用theApp调用应用程序类的成员虚函数 (创建/显示窗口)
              pThread->Run();// 利用theApp调用应用程序类的成员虚函数 (消息循环),函数内部this为&theApp
              {
                 CWinThread::Run();//函数内部this为&theApp
                {
                  for (;;)
	           {       
                       while (当没有消息时)
		      {
			 this->OnIdle();
                         // 利用theApp调用应用程序类的成员虚函数 (空闲处理)
		       }
                       do
                       {
                           if (GetMessage 抓到WM_QUIT消息)
                               return ExitInstance (...);
                         // 利用theApp调用应用程序类的成员虚函数 (善后处理)
                           
                        }while (...)
                   }
                 } 
              }
         }
     }

同过上面的分析发现MFC正是利用C++的多态特性,通过theApp全局对象指针一步一步的操纵着程序的执行流程(提供我们介入的虚函数)

并且从中总结下CWinApp的成员虚函数(将来可以被我们的类重写的)

      InitApplication
      InitInstance
      Run
      OnIdle
      ExitInstance
CWinApp的成员变量
    m_pMainWnd 保存了框架类对象地址


为了验证上面我们总结的成员虚函数,我们可以编写如下测试代码:

#include "stdafx.h"
class CMyFrameWnd : public CFrameWnd
{
};
class CMyWinApp : public CWinApp
{
public:
	CMyWinApp ();
	virtual BOOL InitInstance ();
	virtual BOOL InitApplication ();
	virtual int Run ( );
	virtual BOOL OnIdle (LONG lCount);
	virtual int ExitInstance ( );
};
CMyWinApp::CMyWinApp ()
{
}
BOOL CMyWinApp::InitApplication ()
{//初始化应用程序
	AfxMessageBox ("CMyWinApp::InitApplication ()");
	return CWinApp::InitApplication ();
}
int CMyWinApp::Run ()
{// 消息循环
	AfxMessageBox ("CMyWinApp::Run ()");
	return CWinApp::Run ();
}
BOOL CMyWinApp::OnIdle (LONG lCount)
{// 消息循环中的空闲处理
	//	AfxMessageBox ("CMyWinApp::OnIdle (lCount)");
	return CWinApp::OnIdle (lCount);
}
int CMyWinApp::ExitInstance ()
{// 推出应用程序之前触发调用
	AfxMessageBox ("CWinApp::ExitInstance ()");
	return CWinApp::ExitInstance ();
}

CMyWinApp theApp; // 全局对象(爆破点)

BOOL CMyWinApp::InitInstance () // 创建显示窗口
{//次函数的this为&theApp
	CMyFrameWnd *pFrame = new CMyFrameWnd;
	pFrame->Create (NULL, "MFCBase");
	this->m_pMainWnd = pFrame; // CWinApp 和 CFrameWnd 建立联系
	pFrame->ShowWindow (SW_SHOW);
	pFrame->UpdateWindow ();
	return TRUE;
}

编译运行该程序,你将对窗口程序的创建流程有一个更加清晰的认识


下面着重分析窗口的创建和显示过程 (IinitInstance)

③代码片段:

BOOL CMyWinApp::InitInstance () // 创建显示窗口
{//次函数的this为&theApp
	CMyFrameWnd *pFrame = new CMyFrameWnd;
	pFrame->Create (NULL, "MFCBase");
	this->m_pMainWnd = pFrame; // CWinApp 和 CFrameWnd 建立联系
	pFrame->ShowWindow (SW_SHOW);
	pFrame->UpdateWindow ();
	return TRUE;
}

③->1(片段③拆解分析)

pFrame->Create (NULL, "MFCBase")

带着疑问来看下面的分析:

     传给Create第一个窗口类名为空能成功创建窗口么?还是里面会修改这个值?

BOOL CFrameWnd::Create(LPCTSTR lpszClassName,
	LPCTSTR lpszWindowName,
	DWORD dwStyle,
	const RECT& rect,
	CWnd* pParentWnd,
	LPCTSTR lpszMenuName,
	DWORD dwExStyle,
	CCreateContext* pContext)
{// 内部的this指针为pFrame
	HMENU hMenu = NULL;
	if (lpszMenuName != NULL) // 首先加载菜单资源
	{
		// load in a menu that will get destroyed when window gets destroyed
		HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, ATL_RT_MENU);
		if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
		{
			TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.\n");
			PostNcDestroy();            // perhaps delete the C++ object
			return FALSE;
		}
	}

	m_strTitle = lpszWindowName;    // save title for later
    
	// 下面调用了CreateEx()来创建
	if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
		rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
		pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
	{
		TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.\n");
		if (hMenu != NULL)
			DestroyMenu(hMenu);
		return FALSE;
	}

	return TRUE;
}

我们跟进其中的CreateEx()函数(注:参数中也有函数调用,那么F11将先进入参数调用的函数中),注意这里的lpszClassName类名仍然为空

③->1->1(片段③->1拆解分析)

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)
{// 内部的this指针为pFrame
     ....................................
	// allow modification of several common create parameters
	// 定义了一个局部的窗口类创建信息结构体,提供修改的机会
	CREATESTRUCT cs;
	cs.dwExStyle = dwExStyle;
	cs.lpszClass = lpszClassName; // 注意这里的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 = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,
			cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
			cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); // 最后调用全局函数创建窗口

    ...............................
	return TRUE;
}


其中的PreCreateWindow(cs),AfxHookWindowCreate()我们下面将分别步入跟踪分析

③->1->1->1(片段③->1->1拆解分析)

PreCreateWindow(cs)
跟进分析

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
	if (cs.lpszClass == NULL)
	{
		VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
		// 注意这里调用AfxDeferRegisterClass注册窗口类AFX_WNDFRAMEORVIEW_REG
		// 我们同过跟踪发现AFX_WNDFRAMEORVIEW_REG是一个系统定义好的字符串"AfxFrameOrView100sd"
		// 个人觉得所有没有指定创建窗口的类名是都将被赋予系统提供的默认值,这个值可能有所不同
		cs.lpszClass = _afxWndFrameOrView;  
		// COLOR_WINDOW background ,    这里给类名重新赋值,_afxWndFrameOrView 等同于 字符串"AfxFrameOrView100sd"
	}

	if (cs.style & FWS_ADDTOTITLE)
		cs.style |= FWS_PREFIXTITLE;

	cs.dwExStyle |= WS_EX_CLIENTEDGE;

	return TRUE;
}

继续跟进其中的AfxDeferRegisterClass()

③->1->1->1->1(片段③->1->1->1拆解分析)

VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));

跟进分析

BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{
	// 定义局部窗口类对象
	WNDCLASS wndcls;
	memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults
	wndcls.lpfnWndProc = DefWindowProc; // 注意:这里直接赋值为默认的窗口处理函数,后面我们不久没有机会处理自己的窗口消息了么???
	wndcls.hInstance = AfxGetInstanceHandle();
	wndcls.hCursor = afxData.hcurArrow;

     ...................................

	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)) // 注意:这里注册的窗口类为_afxWndFrameOrView
			fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;
	}

	// must have registered at least as mamy classes as requested
	return (fToRegister & fRegisteredClasses) == fToRegister;
}


两次Shift+F11回到我们的CreateEx()函数转而分析我们AfxHookWindowCreate()函数

带着一下问题:

1 上面注册窗口类使用的窗口过程函数为默认DefWindowProc我们后面怎么处理自己的消息呢?

③->1->1->2(片段③->1->1拆解分析)

AfxHookWindowCreate(this); 

步入AfxHookWindowCreate()函数

void AFXAPI AfxHookWindowCreate(CWnd* pWnd) // pWnd === pFrame
{// 内部this指针为pFrame
	_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
		// 获取MFC中全局对象当前程序线程状态信息对象地址(注意不是:当前程序模块线程状态信息对象)
      .........................................

	if (pThreadState->m_hHookOldCbtFilter == NULL)
	{
		pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
			_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
		// 调用Win32 API 设置窗口创建类型WH_CBT钩子,重点分析钩子处理函数_AfxCbtFilterHook
		if (pThreadState->m_hHookOldCbtFilter == NULL)
			AfxThrowMemoryException();
	}
    ........................................
	pThreadState->m_pWndInit = pWnd;// 把pFrame放入当前程序线程状态信息对象中
}

③->1->1->2->1(片段③->1->1->2拆解分析)

pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());

跟进_AfxCbtFilterHook

_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{// 钩子处理函数
	_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
	// 获取MFC中全局对象当前程序线程状态信息对象地址

        ..............................................
   
	CWnd* pWndInit = pThreadState->m_pWndInit; // 重新获取pFrame, pThreadState->m_pWndInit === pFrame
        ..................................................
  	HWND hWnd = (HWND)wParam;  // 从消息的wParam中获取刚刚创建的窗口句柄
	WNDPROC oldWndProc;
        ...........................................
        ASSERT(CWnd::FromHandlePermanent(hWnd) == NULL);// 映射类对象地址
	...........................................

	// connect the HWND to pWndInit...
	pWndInit->Attach(hWnd);// 这里把新创建的窗口句柄和pFrame框架关联起来,重点分析
	....................................

	// subclass the window with standard AfxWndProc
	WNDPROC afxWndProc = AfxGetAfxWndProc();// 获取AfxGetWndProc 函数的地址
	oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,(DWORD_PTR)afxWndProc);
	// // 利用Win32 API函数将窗口处理函数更改为AfxWndProc(真正的窗口处理函数 )
        ..............................................
	LRESULT lResult = CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
		wParam, lParam); // 处理完消息后放回消息队列
         .....................................
	return lResult;
}


③->1->1->2->1->1(片段③->1->1->2->1拆解分析)

pWndInit->Attach(hWnd)

跟进Attach

BOOL CWnd::Attach(HWND hWndNew)
{// 内部this指针为pFrame, hWndNew为新刚刚创建的窗口句柄
    ...........................
	CHandleMap* pMap = afxMapHWND(TRUE); // 创建映射
	ASSERT(pMap != NULL);

	pMap->SetPermanent(m_hWnd = hWndNew, this);// 函数内部this为pMap (映射类对象地址),把刚刚创建的窗口句柄赋值给pFrame的m_hWnd成员
    .........................................
}

③->1->1->2->1->1->1(片段③->1->1->2->1->拆解分析)

CHandleMap* pMap = afxMapHWND(TRUE); // 创建映射

跟进 afxMapHWND()

CHandleMap* PASCAL afxMapHWND(BOOL bCreate)
{// 内部this指针为 pMap
  AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();// 获取全局对象当前程序模块线程状态信息对象地址
  .........................................................
  pState->m_pmapHWND = new CHandleMap(RUNTIME_CLASS(CWnd),ConstructDestruct<CWnd>::Construct, ConstructDestruct<CWnd>::Destruct, offsetof(CWnd, m_hWnd));
  //  new 了一个映射类对象,并将对象地址保存到当前程序模块线程状态信息对象中
  .................................
  return pState->m_pmapHWND;
}

③->1->1->2->1->1->2(片段③->1->1->2->1->拆解分析)

pMap->SetPermanent(m_hWnd = hWndNew, this);// 函数内部this为pMap (映射类对象地址),把刚刚创建的窗口句柄赋值给pFrame的m_hWnd成员

跟进SetPermanent()

void CHandleMap::SetPermanent(HANDLE h, CObject* permOb)
{// h 为刚刚创建的窗口句柄, perOb为pFramem , 内部this指针为pMap
	.......
	m_permanentMap[(LPVOID)h] = permOb; // 利用的窗口句柄是个唯一整数值的特性,巧妙唯一对应
	                                    // hWnd ------ pFrame ,窗口和框架关联
	.......
}


按两次Shift+F11回到钩子处理函数的入口设置断点并运行到该断点


③->1->1->2->1->1(片段③->1->1->2->1拆解分析)

WNDPROC afxWndProc = AfxGetAfxWndProc();// 获取AfxGetWndProc 函数的地址
转到AfxWndProc()定义

AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{// 钩子重新设置的窗口过程函数,   
     .....................
	// all other messages route through message map
	CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); 
	// MFC一般都是通过框架窗口CFrameWnd对象标识一个窗口而非窗口句柄HWND
	// 这里就是通过hWnd窗口句柄找到框架对象CMyFrameWnd(pFrame)
					
	..............................
	return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); // 里面给我们提供机会调用我们自己的窗口过程函数
}

③->1->1->2->1->1->1(片段③->1->1->2->1->1拆解分析)

CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
跟进FromHandlePermanent

   CWnd* pWnd = CWnd::FromHandlePermanent(hWnd)
  {
       CHandleMap* pMap = afxMapHWND()
      {
         AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); // 获取全局变量当前程序模块线程状态信息对象的地址
         return pState->m_pmapHWND; // 返回上面new的映射类对象地址
       }
       	pWnd = (CWnd*)pMap->LookupPermanent(hWnd) //函数内部this为pMap(映射类对象指针)
       {
           return m_permanentMap[hWnd];
        }
   }
③->1->1->2->1->1->2(片段③->1->1->2->1->1拆解分析)

return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); // 里面给我们提供机会调用我们自己的窗口过程函数
跟进AfxCallWndProc

 AfxCallWndProc(pWnd,...) // pWnd === pFrame
   {
       pWnd->WindowProc(..)
       {
             //回到自己的代码
        }
   }


可以在钩子处理函数_AfxCbtFilterHook的入口处按F5让程序运行到这里,当code=1表示接收到了WM_CREATE消息

到这里我们的MFC窗口程序启动运行机制基本分析完毕了。


当然我们可以把以上代码分析合并总结一下

theApp的构造

 CMyWinApp theApp
 {
          AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE(); //获取当前程序模块状态信息对象地址
          /*
             _AFX_CMDTARGET_GETSTATE()   AfxGetModuleStatue (); 宏
          
          */
          AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread; // 获取当前程序模块线程状态信息对象地址
          pThreadState->m_pCurrentWinThread = this; // 将&theApp保存到全局变量<span style="font-family: Arial, Helvetica, sans-serif;">前程序模块线程状态信息对象</span>的一个成员中
          AfxGetThread()
         {
              AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();// 获取全局变量<span style="font-family: Arial, Helvetica, sans-serif;">前程序模块线程状态信息对象</span>的地址
              CWinThread* pThread = pState->m_pCurrentWinThread; // 获取保存在<span style="font-family: Arial, Helvetica, sans-serif;">前程序模块线程状态信息对象</span>中的this指针
	      return pThread;  // 返回 this指针
          }
         pModuleState->m_pCurrentWinApp = this; // 将theApp的地址保存到全局变量<span style="font-family: Arial, Helvetica, sans-serif;">前程序模块状态信息对象</span>的一个成员中
         AfxGetApp ()
        {
              return AfxGetModuleState ()->m_pCurrentWinThread; //返回保存在<span style="font-family: Arial, Helvetica, sans-serif;">前程序模块状态信息对象</span><span style="font-family: Arial, Helvetica, sans-serif;">中的theApp地址</span>
         }
   }

WinMain的调用

     WinMain (........)  // 注意体会 是不是 theApp 在知道流程
    {
         AfxWinMain()
        {
               CWinThread* pThread = AfxGetThread();
	       CWinApp* pApp = AfxGetApp(); 
               /* 以上两句都是获取theApp的地址 */
               pApp->InitApplication(); //利用theApp调用应用程序类的成员虚函数 (初始化)
              pThread->InitInstance();// 利用theApp调用应用程序类的成员虚函数 (创建/显示窗口)
              pThread->Run();// 利用theApp调用应用程序类的成员虚函数 (消息循环),函数内部this为&theApp
              {
                 CWinThread::Run();//函数内部this为&theApp
                {
                  for (;;)
	           {       
                       while (当没有消息时)
		      {
			this->OnIdle();
                        // 利用theApp调用应用程序类的成员虚函数 (空闲处理)
		       }
                       do
                       {
                           if (GetMessage 抓到WM_QUIT消息)
                               return ExitInstance (...);
                         // 利用theApp调用应用程序类的成员虚函数 (善后处理)
                           
                        }while (...)
                   }
                 } 
              }
         }
     }


创建窗口

pFrame->Create (NULL, "MFCCreate") // 函数内部的this指针为pFrame
{
    加载菜单
    CreateEx (...NULL...) // 函数内部的this指针为pFrame
    {
       CREATESTRUCT cs;
       ............................
       cs.lpszClass = lpszClassName;//NULL
       ............................
       cs.hInstance = AfxGetInstanceHandle();
       PreCreateWindow(cs)
       {
             AfxEndDeferRegisterClass ()
             {
        
                WNDCLASS wndcls;
	        memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults
	        wndcls.lpfnWndProc = DefWindowProc; // ??????????? 不受我们控制了怎么可以?
                ...................................
                _AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME) //  _afxWndFrameOrView === "AfxFrameOrView42sd"
                {
                     wndcls->lpszClassName = _afxWndFrameOrView; //  _afxWndFrameOrView === "AfxFrameOrView42sd"
                     AfxRegisterClass(wndcls)
                     {
                         ::RegisterClass(wndcls); // 注册窗口类
                      }
                 }
             } 
             cs.lpszClass = _afxWndFrameOrView;  // = "AfxFrameOrView42sd"      
        }
        AfxHookWindowCreate(pFrame)
       {
            _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData(); // 获取全局变量<span style="font-family: Arial, Helvetica, sans-serif;">前程序线程状态信息对象</span><span style="font-family: Arial, Helvetica, sans-serif;">的地址 </span>
           ::SetWindowsHookEx(WH_CBT,...); // 利用Win32 API 函数在程序中埋下一个类型为WH_CBT的钩子
           pThreadState->m_pWndInit = pFrame; // 将自己new的框架类对象指针pFrame保存到当前程序线程状态信息对象中
        }
        ::CreateWindowEx(...);// 次函数一旦执行,WM_CREATE 立即被钩子勾到钩子处理函数中
    }
}

钩子处理函数

_AfxCbtFilterHook (...,wParam,....)
{
    _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData(); // 获取全局变量<span style="font-family: Arial, Helvetica, sans-serif;">前程序线程状态信息对象</span><span style="font-family: Arial, Helvetica, sans-serif;">的地址</span>
    CWnd* pWndInit = pThreadState->m_pWndInit;// 重新获取pFrame pThreadState->m_pWndInit === pFrame
    HWND hWnd = (HWND)wParam; // 获取刚刚创建的窗口句柄
    pWndInit->Attach(hWnd) // 函数内部this为pFrame (pWndInit === pFrame )
   {
       CHandleMap* pMap = afxMapHWND(TRUE)
      {
         AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState (); // 获取当前程序模块线程状态信息对象的地址
         pState->m_pmapHWND = new CHandleMap (...); // new 了一个映射类对象,并将对象地址保存到<span style="font-family: Arial, Helvetica, sans-serif;">前程序线程状态信息对象</span><span style="font-family: Arial, Helvetica, sans-serif;">中</span>
         return pState->m_pmapHWND;
       }  
    }
    pMap->SetPermanent(p_Frame->m_hWnd = hWnd, pFrame) // 函数内部this为pMap (映射类对象地址)
    {
       m_permanentMap[hWnd] = pFrame;       
    }
    WNDPROC afxWndProc = AfxGetAfxWndProc(); // 获取AfxGetWndProc 函数的地址
    oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)afxWndProc);  (注:MFC中没有与SetWindowLong重名的函数) 
     // 利用Win32 API函数将窗口处理函数更改为AfxWndProc(真正的窗口处理函数 )
     
}
消息处理函数调用

AfxWndProc (...)
{
   CWnd* pWnd = CWnd::FromHandlePermanent(hWnd)
  {
       CHandleMap* pMap = afxMapHWND()
      {
         AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); // 获取全局变量<span style="font-family: Arial, Helvetica, sans-serif;">前程序线程状态信息对象</span><span style="font-family: Arial, Helvetica, sans-serif;">的地址</span>
         return pState->m_pmapHWND; // 返回上面new的映射类对象地址
       }
       	pWnd = (CWnd*)pMap->LookupPermanent(hWnd) //函数内部this为pMap(映射类对象指针)
       {
           return m_permanentMap[hWnd];
        }
   } 
   AfxCallWndProc(pWnd,...) // pWnd === pFrame
   {
       pWnd->WindowProc(..)
       {
             //回到自己的代码
        }
   }
}













  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值