总结《二》MFC中WinMain和CALLBACK
发布日期:2021-06-30 12:12:22
浏览次数:3
分类:技术文章
本文共 8252 字,大约阅读时间需要 27 分钟。
MFC中WinMain和回调函数CALLBACK
一,路线
1.一般普通窗口或控件建立调用的CWnd :: CreateEx函数
2.经过资源对话框创建的即不调用的CWnd :: CreateEx函数
二,在WIN32SDK下编程我们总是从入口函数WINMAIN和给予窗口类指定窗口回调函数(CALLBACK),如下:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd ){ WNDCLASSEX wc = { 0 }; wc.cbSize = sizeof(WNDCLASSEX); wc.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH); wc.hCursor = LoadCursor(nullptr, IDC_ARROW); wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION); wc.hInstance = hInstance; wc.lpfnWndProc = WndProc;//CALLBACK
那么MFC隐藏了这些细节,我们具体来解剖下。
三,WINMAIN
在MFC中由appmodul.cpp中声明
extern int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow);extern "C" int WINAPI_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow)#pragma warning(suppress: 4985){ // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);}
winmain.cpp定义
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: AfxWinTerm(); return nReturnCode;}
三,CALLBACK
分析上述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){ ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) || AfxIsValidAtom(lpszClassName)); ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName)); // allow modification of several common create parameters 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; if (!PreCreateWindow(cs)) { PostNcDestroy(); return FALSE; } AfxHookWindowCreate(this); 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); if (!AfxUnhookWindowCreate()) PostNcDestroy(); // cleanup if CreateWindowEx fails too soon if (hWnd == NULL) return FALSE; ASSERT(hWnd == m_hWnd); // should have been set in send msg hook return TRUE;}
在正式创建窗口前加入一个的PreCreateWindow的虚函数,这样派生类就有机会重新定义它,当我们想改变窗口类里的属性时可以从派生类覆写此虚函数,也可以我们自己注册窗口类,自己注册后直接返回TRUE即可。
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs){ if (cs.lpszClass == NULL) { // make sure the default window class is registered VERIFY(AfxDeferRegisterClass(AFX_WND_REG)); // no WNDCLASS provided - use child window default ASSERT(cs.style & WS_CHILD); cs.lpszClass = _afxWnd; } if ((cs.hMenu == NULL) && (cs.style & WS_CHILD)) { cs.hMenu = (HMENU)(UINT_PTR)this; } return TRUE;}在CreateWindowEx创建窗口之前调用了AfxHookWindowCreate函数,函数源码:
void AFXAPI AfxHookWindowCreate(CWnd* pWnd){ _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData(); if (pThreadState->m_pWndInit == pWnd) return; if (pThreadState->m_hHookOldCbtFilter == NULL) { pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT, _AfxCbtFilterHook, NULL, ::GetCurrentThreadId()); if (pThreadState->m_hHookOldCbtFilter == NULL) AfxThrowMemoryException(); } ASSERT(pThreadState->m_hHookOldCbtFilter != NULL); ASSERT(pWnd != NULL); ASSERT(pWnd->m_hWnd == NULL); // only do once ASSERT(pThreadState->m_pWndInit == NULL); // hook not already in progress pThreadState->m_pWndInit = pWnd;}内部调用了SetWindowHookEx函数下了钩子,钩子类型为:WH_CBT,即在激活、创建、破坏、最小化、最大化、移动或调整窗口大小之前,系统会先调用我们下的钩子函数,即_AfxCbtFilterHook,该函数源码:
LRESULT CALLBACK_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam){ _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData(); if (code != HCBT_CREATEWND) { // wait for HCBT_CREATEWND just pass others on... return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code, wParam, lParam); } LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs; CWnd* pWndInit = pThreadState->m_pWndInit; BOOL bContextIsDLL = afxContextIsDLL; if (pWndInit != NULL || (!(lpcs->style & WS_CHILD) && !bContextIsDLL)) { // Note: special check to avoid subclassing the IME window if (_afxDBCS) { // check for cheap CS_IME style first... if (GetClassLong((HWND)wParam, GCL_STYLE) & CS_IME) goto lCallNextHook; // get class name of the window that is being created LPCTSTR pszClassName; TCHAR szClassName[_countof("ime")+1]; if (DWORD_PTR(lpcs->lpszClass) > 0xffff) { pszClassName = lpcs->lpszClass; } else { szClassName[0] = '\0';#pragma warning(push)#pragma warning(disable: 4302) // 'type cast' : truncation from 'LPCSTR' to 'ATOM' GlobalGetAtomName((ATOM)lpcs->lpszClass, szClassName, _countof(szClassName));#pragma warning(pop) pszClassName = szClassName; } // a little more expensive to test this way, but necessary... if (::AfxInvariantStrICmp(pszClassName, _T("ime")) == 0) goto lCallNextHook; } ASSERT(wParam != NULL); // should be non-NULL HWND HWND hWnd = (HWND)wParam; WNDPROC oldWndProc; if (pWndInit != NULL) { AFX_MANAGE_STATE(pWndInit->m_pModuleState); // connect the HWND to pWndInit... pWndInit->Attach(hWnd); // allow other subclassing to occur first pWndInit->PreSubclassWindow(); WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr(); ASSERT(pOldWndProc != NULL); // subclass the window with standard AfxWndProc WNDPROC afxWndProc = AfxGetAfxWndProc(); oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (DWORD_PTR)afxWndProc); ASSERT(oldWndProc != NULL); if (oldWndProc != afxWndProc) *pOldWndProc = oldWndProc; pThreadState->m_pWndInit = NULL; } else { static ATOM s_atomMenu = 0; bool bSubclass = true; if (s_atomMenu == 0) { WNDCLASSEX wc; memset(&wc, 0, sizeof(WNDCLASSEX)); wc.cbSize = sizeof(WNDCLASSEX); s_atomMenu = (ATOM)GetClassInfoEx(NULL, _T("#32768"), &wc); } // Do not subclass menus. if (s_atomMenu != 0) { ATOM atomWnd = (ATOM)::GetClassLongPtr(hWnd, GCW_ATOM); if (atomWnd == s_atomMenu) bSubclass = false; } else { TCHAR szClassName[256]; if (::GetClassName(hWnd, szClassName, 256)) { szClassName[255] = NULL; if (_tcscmp(szClassName, _T("#32768")) == 0) bSubclass = false; } } if (bSubclass) { // subclass the window with the proc which does gray backgrounds oldWndProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); if (oldWndProc != NULL && GetProp(hWnd, _afxOldWndProc) == NULL) { SetProp(hWnd, _afxOldWndProc, oldWndProc); if ((WNDPROC)GetProp(hWnd, _afxOldWndProc) == oldWndProc) { GlobalAddAtom(_afxOldWndProc); SetWindowLongPtr(hWnd, GWLP_WNDPROC, (DWORD_PTR)_AfxActivationWndProc); ASSERT(oldWndProc != NULL); } } } } }lCallNextHook: LRESULT lResult = CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code, wParam, lParam); return lResult;}该函数把code不是HCBT_CREATEWND过滤,保留了HCBT_CREATEWND。
该code为一个窗口即将被创建时在向窗口发送WM_CREATE或者WM_NCCREATE消息,所以在这个时候进行了子类化操作,就是把所有的窗口回调函数设为AfxWndProc
LRESULT CALLBACKAfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam){ // special message which identifies the window as using AfxWndProc if (nMsg == WM_QUERYAFXWNDPROC) return 1; // all other messages route through message map CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); ASSERT(pWnd != NULL); ASSERT(pWnd==NULL || pWnd->m_hWnd == hWnd); if (pWnd == NULL || pWnd->m_hWnd != hWnd) return ::DefWindowProc(hWnd, nMsg, wParam, lParam); return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);}
转载地址:https://jadeshu.blog.csdn.net/article/details/71512547 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
不错!
[***.144.177.141]2024年04月22日 15时57分20秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Mysql 之主从复制
2019-04-30
LVS负载均衡------NAT模式
2019-04-30
squid代理-----透明代理模式
2019-04-30
squid代理介绍----ACL控制应用+sarg日志分析+反向代理
2019-04-30
redis集群之主从模式+哨兵模式
2019-04-30
JavaScript原生开关灯效果
2019-04-30
企业邮箱如何申请注册,邮箱申请如何免费注册?
2019-04-30
微信企业邮箱,手机邮箱格式地址怎么写?
2019-04-30
公司如何申请企业邮箱,公司邮箱怎么申请,公司企业邮箱哪个好?
2019-04-30
电子邮箱账号怎么申请,怎样申请邮箱账号呢
2019-04-30
邮箱怎么发邮件,邮件发信量多少,职场新人怎么发汇报邮件呢?
2019-04-30
maven 多层次pom 新引入包,编译成功,还是没有将包引入到本地
2019-04-30
leetCode2 两数相加
2019-04-30
【工具使用】使用pip与conda安装、更新与卸载Pytorch和torchvision
2019-04-30
【工具使用】Google免费云环境Colaboratory使用
2019-04-30
【深度学习笔记】卷积层,全连接层,池化层的相关输出参数计算
2019-04-30