接上文【MFC】05.MFC第一大机制:程序启动机制-笔记,这一篇文章来带领大家逆向分析MFC第二大机制:窗口创建机制的源码。
我们知道,在Win32编程中,如果我们要创建一个窗口,基本步骤为:
- 注册窗口
- 创建一个窗口,必须要给一个类名称
- 消息处理回调函数
那么MFC的窗口创建机制又是怎样的呢?我们来回溯一下MFC源码:
首先,我们继续用上文中创建的MFC程序:
#include <afxwin.h>
//实现我们自己的框架类
class CMyFrameWnd :public CFrameWnd {
public:
};
//实现我们自己的应用程序类
class CMyApp :public CWinApp {
public:
CMyApp() {};
//重写虚函数
virtual BOOL InitInstance() {
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, L"FirstMFC");
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
CMyApp theApp;
不难猜到,创建窗口的代码在这:pFrame->Create(NULL, L"FirstMFC");
我们就在这行代码上下断点,跟过去:
我们知道我们重写的的InitInstance函数在WinMian函数转发的AfxwWimMian函数中会被调用:
这里给的参数,第一个NULL是lpszClassName(窗口类名),第二个参数是lpszWindowName(应用程序类名),但是我们了解Win32编程,我们知道如果想要创建窗口或者是注册窗口,都必须要窗口类名,但是这里给的是NULL,为什么还是能够正常执行过去呢?
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame = new CMyFrameWnd;
//Create函数是框架类的方法:
//我们知道Create方法的参数肯定不止俩,我们跟过去之后,发现其他参数是有默认参数的
pFrame->Create(NULL, L"FirstMFC")
{
//进入函数第一件事:判断是否包含菜单,如果包含菜单,则加载菜单
HMENU hMenu = NULL;
if (lpszMenuName != NULL)
{
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();
return FALSE;
}
}
m_strTitle = lpszWindowName; // save title for later
//这里的代码很明显是创建窗口了,但是没有窗口类名,怎么能创建窗口呢?我们跟进去看看:
//这里由于其他参数我们不关注,这里只留下窗口类名
//这里源代码函数实现实在if语句的条件中执行的,这里直接拉出来
CreateEx( lpszClassName, lpszWindowName,...)
{
//可以看到在这里有断言:
ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) ||
AfxIsValidAtom(lpszClassName));
ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName));
//这里是创建窗口所需要的类:
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;
//这里隐含的this指针:框架类窗口
PreCreateWindow(cs){
//跟进去函数也能发现是框架类窗口的方法
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
//发现这里有一个貌似MFC注册窗口的函数:跟进去看看
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)){
//这些是所需要的类,相信大家都很熟悉了
WNDCLASS wndcls;
memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults
wndcls.lpfnWndProc = DefWindowProc;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hCursor = afxData.hcurArrow;
//接下来发现了好多类似的代码,应该是判断一些东西,我们的代码进入了这个if语句,我们就跟进去
if (fToRegister & AFX_WNDFRAMEORVIEW_REG)
{
//这里是对wndcls做一些赋值操作
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
//代码跟到这里,应该是注册窗口了,因为吧注册窗口所需要的类传进去了,为了以防万一,我们跟进去看看:
if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME)){
//代码前部分做了一些赋值操作,加载图标
//我们跟到这个函数中看看注册操作:
return AfxRegisterClass(pWndCls){
WNDCLASS wndcls;
//这里的GetClassInfo是判断窗口是否已经注册过
GetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName,&wndcls)
//在这里发现了Win32API:注册窗口
if (!RegisterClass(lpWndClass))
}
}
}
//在注册玩窗口后,发现了这段代码
//这里的_afxWndFrameOrView是一个宏或者数组,跟过去看看,发现是个数组
//跟到最后,发现是FrameOrView
cs.lpszClass = _afxWndFrameOrView;
}
}
...
return TRUE;
}
}
}
//在创建窗口之前,发现埋下了一个钩子
AfxHookWindowCreate(this){
void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
//MFC的第三个全局变量
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
if (pThreadState->m_pWndInit == pWnd)
return;
//这里是埋下了一个创建窗口的消息钩子
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
//如果失败的话,抛出异常
if (pThreadState->m_hHookOldCbtFilter == NULL)
AfxThrowMemoryException();
}
//这里保存了我们的框架类窗口对象
pThreadState->m_pWndInit = pWnd;
}
}
//发现这里是创建窗口
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);
}
}
触发的钩子函数
LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
//MFC第三个全局变量的地址
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
//取出结构体参数
LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;
//多态 父类指针 指向子类
CWnd* pWndInit = pThreadState->m_pWndInit
//创建的窗口句柄
HWND hWnd = (HWND)wParam;
//窗口句柄传入了进去
//内部this指针是我们的对象
pWndInit->Attach(hWnd);
{
//MAP对象
CHandleMap* pMap = afxMapHWND(TRUE);
{
//MFC 第二个全局变量 线程模块状态
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
//把映射类对象 保存到了 第二个全局变量里面
pState->m_pmapHWND = new CHandleMap
return pState->m_pmapHWND;
}
//内部this指针是谁 pMap映射类对象
pMap->SetPermanent(this->m_hWnd = hWndNew, 框架类对象);
{
inline void CHandleMap::SetPermanent(HANDLE h, CObject* permOb)
{
//pMap映射类对象
this->m_permanentMap[窗口句柄] = 框架类对象 pFrame;
//stl map map[key] = val
}
}
WNDPROC afxWndProc = AfxGetAfxWndProc();
oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,(DWORD_PTR)afxWndProc);
}
LRESULT lResult = CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,wParam, lParam);
}
//真正的窗口过程处理函数
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
{
CHandleMap* pMap = afxMapHWND();
{
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
pState->m_pmapHWND
}文章来源:https://www.toymoban.com/news/detail-640608.html
return (CObject*)m_permanentMap.GetValueAt((LPVOID)h);
}
AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam)
{
//父类指针 指向了子类
lResult = pWnd->WindowProc(nMsg, wParam, lParam);
}
}文章来源地址https://www.toymoban.com/news/detail-640608.html
到了这里,关于【MFC】06.MFC六大机制:窗口创建机制-笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!