总结《二》MFC中WinMain和CALLBACK

来源:互联网 发布:ubuntu镜像文件 编辑:程序博客网 时间:2024/05/19 00:11
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 WinMainreturn 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 initializationif (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))goto InitFailure;// App global initializations (rare)if (pApp != NULL && !pApp->InitApplication())goto InitFailure;// Perform specific initializationsif (!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 parametersCREATESTRUCT 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 soonif (hWnd == NULL)return FALSE;ASSERT(hWnd == m_hWnd); // should have been set in send msg hookreturn TRUE;}

在正式创建窗口前加入一个的PreCreateWindow的虚函数,这样派生类就有机会重新定义它,当我们想改变窗口类里的属性时可以从派生类覆写此虚函数,也可以我们自己注册窗口类,自己注册后直接返回TRUE即可。
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs){if (cs.lpszClass == NULL){// make sure the default window class is registeredVERIFY(AfxDeferRegisterClass(AFX_WND_REG));// no WNDCLASS provided - use child window defaultASSERT(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 onceASSERT(pThreadState->m_pWndInit == NULL);   // hook not already in progresspThreadState->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 windowif (_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 createdLPCTSTR 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 HWNDHWND 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 firstpWndInit->PreSubclassWindow();WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();ASSERT(pOldWndProc != NULL);// subclass the window with standard AfxWndProcWNDPROC 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 backgroundsoldWndProc = (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 AfxWndProcif (nMsg == WM_QUERYAFXWNDPROC)return 1;// all other messages route through message mapCWnd* 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);}







0 0
原创粉丝点击