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 CALLBACK
AfxWndProc(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);
}
 
 
 

总结《二》MFC中WinMain和CALLBACK的更多相关文章

  1. 多线程编程之二 ---MFC中的多线程开发

    下载源代码 五.MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环. 工作者线程没有消 ...

  2. MFC中获取各个窗口之间的句柄或者指针对象的方法

    MFC在非常多的对话框操作中,我们常常要用到在一个对话框中调用还有一个对话框的函数或变量.能够用例如以下方法来解决.    HWND hWnd=::FindWindow(NULL,_T("S ...

  3. VC++ MFC中如何将应用程序的配置信息保存到注册表中(二)

    在上一篇中介绍了几个写入注册表数据和读取注册表数据的接口,并介绍了使用方法. 这一片教你如何使得你的应用程序在下次打开时保持上一次关闭前的状态. 在上一篇添加的代码的基础上,要添加WM_CLOSE消息 ...

  4. <MFC_1>深入剖析MFC的WinMain和消息机制

    一.开篇引论 熟悉Win32开发的朋友,应该非常了解它的基本组成和流程 1. WinMain:书写窗口类(WNDCLASS) -> 注册窗口类 -> 创建窗口 -> 显示窗口和更新窗 ...

  5. MFC中的乱起八糟----字符编码:LPTSTR,LPCTSTR, TCHAR等

    注意,编写有 Unicode 意识的代码总是一件好事,比如: CString graycat = CString(_T("Gray")) + _T("Cat") ...

  6. MFC中存在的不属于任何类的全局函数,它们统统在函数名称开头加上Afx

    MFC中存在的不属于任何类的全局函数,它们统统在函数名称开头加上Afx. 函数名称 说明 AfxWinInit 被WinMain(MFC提供)调用的一个函数,用做MFC GUI程序初始化的一部分,如果 ...

  7. C运行时库(C Run-time Library)详解(提供的另一个最重要的功能是为应用程序添加启动函数。Visual C++对控制台程序默认使用单线程的静态链接库,而MFC中的CFile类已暗藏了多线程)

    一.什么是C运行时库 1)C运行时库就是 C run-time library,是 C 而非 C++ 语言世界的概念:取这个名字就是因为你的 C 程序运行时需要这些库中的函数. 2)C 语言是所谓的“ ...

  8. MFC中使用FLASH

    一.准备工作 第一步:下载并安装Adobe Flash Player. 从官方网站(http://get.adobe.com/cn/flashplayer/)上下载最新的Flash Player(大约 ...

  9. MFC中对话框类(Dialog)的应用

    转载http://hi.baidu.com/jackywdx/item/feee8041d2c2e12310ee1e85 Windows应用程序通常是通过对话框接收用户输入.向用户输出信息,本节介绍应 ...

随机推荐

  1. Struts2之Action三种接收参数形式与简单的表单验证

    有了前几篇的基础,相信大家对于Struts2已经有了一个很不错的认识,本篇我将为大家介绍一些关于Action接收参数的三种形式,以及简单的表单验证实现,下面进入正题,首先我们一起先来了解一下最基本的A ...

  2. Ubuntu16.04双网卡主备配置

    前几日写了一篇Ubuntu14.04双网卡主备配置,没成想变化总是这么快,今日安装某软件,提示最匹配的ubuntu版本是16.04,作为一个码农能有什么办法,只能不断去适应变化.拥抱变化. 首先16. ...

  3. libxml2简单的生成、解析操作

    3. 简单xml操作例子 link:http://www.blogjava.net/wxb_nudt/archive/2007/11/18/161340.html 了解以上基本知识之后,就可以进行一些 ...

  4. MUI---IOS切换到后台继续播放音乐

    应用切换到后台继续音乐播放HBuilder默认生成的应用在iOS是不支持后台音乐播放的,当应用切换到后台时音乐将暂停播放,下次切换到前台继续播放.如果要支持应用切换到后台后继续播放音乐功能需要进行额外 ...

  5. android 常见内存泄漏原因及解决办法

    android常见内存泄漏主要有以下几类: 一.Handler 引起的内存泄漏. 在Android开发中,我们经常会使用Handler来控制主线程UI程序的界面变化,使用非常简单方便,但是稍不注意,很 ...

  6. iOS SwiftMonkey 随机暴力测试

    参考源文章 https://github.com/zalando/SwiftMonkey https://kemchenj.github.io/2017/03/16/2017-03-16/ 简介 这个 ...

  7. Cglib 与 JDK动态代理的运行性能比较

    都说 Cglib 创建的动态代理的运行性能比 JDK 动态代理能高出大概 10 倍,今日抱着怀疑精神验证了一下,发现情况有所不同,遂贴出实验结果,以供参考和讨论. 代码很简单,首先,定义一个 Test ...

  8. 计蒜客 30990 - An Olympian Math Problem - [简单数学题][2018ICPC南京网络预赛A题]

    题目链接:https://nanti.jisuanke.com/t/30990 Alice, a student of grade 6, is thinking about an Olympian M ...

  9. CodeForces - 583C GCD Table map的auto遍历 ,有点贪心的想法

    题意:给你n*n gcd表中的所有数(以任意顺序) ,求对角线上的n个数分别是什么.gcd表定义如下,先将n个数填在对角线的上,然后将各个格子填上对应对角线上的数的gcd值,也就是V[i][j]=gc ...

  10. models语言中filter和all取数据有什么区别

    转自:http://www.bubuko.com/infodetail-1882394.html rs=Person.objects.all() all返回的是QuerySet对象,程序并没有真的在数 ...