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. Unity3D动作资源(AnimatinClip)优化

    能做到去掉Scale曲线,降低浮点精度 using System; using UnityEngine; using System.Collections; using System.Collecti ...

  2. cJson 创建 读取

    关于c语言操作json,cjson还挺好用,许多操作已经帮开发员封装好了,使用起来很方便.资源下载地址为:http://sourceforge.net/projects/cjson/在test.c文件 ...

  3. stdarg.h头文件源代码分析

    谈到C语言中可变参数函数的实现(参见C语言中可变参数函数实现原理),有一个头文件不得不谈,那就是stdarg.h 本文从minix源码中的stdarg.h头文件入手进行分析: #ifndef _STD ...

  4. 【JSP】JSP中的Java脚本

    前言 现代Web开发中,在JSP中嵌入Java脚本不是推荐的做法,因为这样 不利于代码的维护.有很多好的,替代的方法避免在JSP中写Java脚本.本文仅做为JSP体系技术的一个了解.     类成员定 ...

  5. CMOS构成的常见电路

    CMOS门电路 以MOS(Metal-Oxide Semiconductor)管作为开关元件的门电路称为MOS门电路.由于MOS型集成门电路具有制造工艺简单.集成度高.功耗小以及抗干扰能力强等优点,因 ...

  6. iOS uitextfield长度限制

    [textUsername addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEd ...

  7. jenkins中windows节点设置开机自启动slave-agent

    做web UI自动化时,为了提高效率,用了多台windows节点来跑自动化,但slave-agent每次在关机后都得手工启动,麻烦,网上看到了一系列说启动任务中,感觉还是不考虑,这里使用windows ...

  8. iOS - viewDidLoad, viewWillDisappear, viewWillAppear区别及加载顺序

    viewWillAppear: Called when the view is about to made visible. Default does nothing视图即将可见时调用.默认情况下不执 ...

  9. 微信小程序---示例DEMO

    转:CSDN的文章: https://blog.csdn.net/rolan1993/article/details/73467867 不错的DEMO: https://github.com/skyv ...

  10. [分布式系统学习]阅读笔记 Distributed systems for fun and profit 抽象 之二

    本文是阅读 http://book.mixu.net/distsys/abstractions.html 的笔记. 第二章的题目是"Up and down the level of abst ...