mfc学习之主框架窗口创建过程

在上一篇追踪了mfc程序流程之后回到了自己所写的初始化函数

BOOL CMyWinApp::InitInstance()
{
	CMyFrameWnd * pFrame = new CMyFrameWnd();
	pFrame->Create(NULL,"FirstMFC");
	m_pMainWnd = pFrame;
	pFrame->ShowWindow(SW_SHOW);
	pFrame->UpdateWindow();
	return TRUE;
}

在上述代码中首先创建了一个pFrame对象,然后执行CMyFrameWnd的成员函数Create,在win32中创建一个窗口首先需要注册窗口类,创建窗口,显示窗口,刷新窗口,消息循环等几个步骤,因此Create函数中应该会执行这几步,在pFrame->Create()代码前加断点,单步调试进入Create函数内部

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

进入Create函数后首先发现有一个创建菜单的选项,即我们给的lpszMenuName值部位空时会加载菜单然后执行进入CreateEx

进入函数内部发现定义了一个CREATESTRUCT cs;结构体,该结构体类成员如下所示

typedef struct tag CREATESTRUCT
{
    LPVOID lpCreateParams;
    HINSTANCE hInstance;   //应用程序的实例句柄
   HMENU hMenu;           //窗口菜单句柄
   HWND hwndParent;       //父窗口句柄
   int cy;                //指定新窗口的高度,以像素为单位
   int cx;                //指定新窗口的宽度,以像素为单位
   int y;                 //指定新窗口的左上角x坐标
   int x;                 //指定新窗口的左上角y坐标
   LONG style;            //指定新窗口的类型(风格)
   LPCTSTR lpszName;      //指定新窗口的名称
   LPCTSTR lpszClass;     //指定新窗口类的名称
   DWORD dwExStyle;       //指定新窗口扩展风格。
}CREATESTRUCT;

继续单步运行得到

由于我们在create创建时没有给lpszClassName参数赋值,但是注册窗口类时lpszClassName为NULL肯定是不行,因此,mfc应该会在后面给cs.lpszClass重新赋值

继续执行代码

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;

AfxGetInstanceHandle()函数获取WinMain的第一个参数,继续执行到PreCreateWindow,将cs结构体传进去重新赋值,单步进入


进入后发现如下代码
WNDCLASS wndcls;
memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults    
wndcls.lpfnWndProc = DefWindowProc;
其中lpfnWndProc为一个回调函数,即消息处理函数,但是DefWindowProc作消息处理函数会使程序陷入死循环,因此mfc在下面还会继续处理继续单步执行代码


单步进入得到代码
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 = ::LoadIconW(hInst, ATL_MAKEINTRESOURCEW(nIDIcon))) == NULL)
	{
		// use default icon
		pWndCls->hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
	}
	return AfxRegisterClass(pWndCls);
}
pWndCls->lpszClassName被重新赋值为 "AfxFrameOrView110sd",即为注册类名,此时可以调用Win32API来注册窗口类了,返回CreateEx中,继续执行得到如下代码




AfxHookWindowCreate显而易见是给程序埋一个钩子,用于修改消息处理函数,单步进入这个函数发现

::SetWindowsHookEx (WH_CBT,_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());

 //调用win32API函数在程序中埋下一个钩子,类型为WH_CBT,一旦WM_CREAT出现马上勾到AfxCbtFilterHook

继续跟踪进入钩子处理函数 _AfxCbtFilterHook ( ),发现一个pWndInit->Attach(hWnd)函数,该函数将创建的窗口类对象与实际代表窗口的句柄作

一一绑定,至此,类对象才真正代表了实际窗口

继续执行代码

WNDPROC afxWndProc = AfxGetAfxWndProc();
oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,
		(DWORD_PTR)afxWndProc);

//利用win32Api函数SetWindowLongPtr将窗口处理函数,从DefWindowProc更改为AfxWndPro(真正的处理函数),至此窗口创建完成,虽然还是有很多地方不是特别了解,但也算是对MFC窗口的创建过程有了一个大概的认识

MFC窗口创建总结有以下几点

1.      加载菜单

2.      调用PreCreateWindow函数  设计并注册窗口类

内部调用AfxDeferRegisterClass设计窗口类

 

WNDCLASSwndcls;

wndcls.lpfnWndProc = DefWindowProc;//???????????下面会更改

……

调用_AfxRegisterClassWithIcon内部调用AfxRegisterClass

内部调用windows API函数::RegisterClass注册窗口类

3. 调用AfxHookWindowCreate

1)  将自己new的框架类对象地址(pFrame)保存到当前程序信息中

2)  调用windows API函数SetWindowsHookEx在程序埋下一个类型为WH_CBT的钩子,一旦CreateWindowEx函数执行成功,调用钩子处理函数

4.      执行windows API函数CreateWindowEx创建窗口,钩子将WM_CREATE消息钩到_AfxCbtFilterHook(钩子处理函数)

5.      调用钩子处理函数

1)  将窗口句柄(hWnd)和 框架类对象地址(pFrame)进行一对一的绑定

2)  调用windowsAPI函数SetWindowLongPtr将窗口处理函数更改为AfxWndProc(真正的窗口处理函数)






你可能感兴趣的:(MFC学习)