在上一篇追踪了mfc程序流程之后回到了自己所写的初始化函数
BOOL CMyWinApp::InitInstance()
{
CMyFrameWnd * pFrame = new CMyFrameWnd();
pFrame->Create(NULL,"FirstMFC");
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
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;
}
}
进入函数内部发现定义了一个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中,继续执行得到如下代码
::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(真正的窗口处理函数)