无意中看到这篇文章,我觉得比MFC设计基础写的好,所以就整理了一下作为以后参考。
http://www.cnblogs.com/shipfi/archive/2005/10/13/121629.html
首先,我们必须在源文件中定义了一个CMyWinApp的实体 ① 它是第一步操作。(如: CMyWinApp MyWinApp;注:代码详见:G:/Program/MyMFC/Hello/Hello.dsw),CMyWinApp继承自CWinApp,CMyWinApp无构造函数,但需调用CWinApp构造函数。
配置好MyWinApp实体的一些信息,包括获得该线程的句柄,线程的ID,该实体的句柄等。配置完后,_tWinMain登场,注意,我们没有撰写WinMain的代码,WinMain是MFC早已准备好并由链接器直接加到应用程序代码中的。_tWinMain是为了支持Unicode而准备的一个宏,实质还是WinMain。
好,_tWinMain函数做了什么工作呢?它只是在里面调用AfxWinMain函数。如下:return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); AfxWinMain是一个全局的函数,并不属于任一个类。
下面让我们来看一看AfxWinMain函数做了些什么工作?
第一、它会获得Application Object的指针(亦CWinApp派生对象的指针),在此是获得MyWinApp的指针,以备后用。它是通过下面这句来实现的:
CWinApp* pApp = AfxGetApp(); //AfxGetApp为全局函数,不属于任何类,详见第8条... 注:凡是函数前有Afx字样的都是全局函数。
第二、调用AfxWinInit(...)函数,用作MFC GUI程序初始化的一部分,这个函数详见后解...
第三、用上面获得的pApp调用InitApplication()函数,如下:pApp->InitApplication(); InitApplication函数详见后解...
第四、用pApp调用InitInstance()函数,如下:pApp->InitInstance(); InitInstance函数详见后解...
第五、用pApp调用Run()函数,如下: nReturnCode = pApp->Run(); Run函数详见后解...
第六、最后调有AfxWinTerm函数,结束该应用程序。
所以,主要的AfxWinMain做的工作就应该如下代码所示:
int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
...{
int nReturnCode = -1;
CWinApp* pApp = AfxGetApp(); //详见第8条
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow); //详见第9条
pApp->InitApplication(); //详见第10条
pApp->InitInstance(); //详见第11条
nReturnCode = pApp->Run(); //详见第12条
AfxWinTerm();
return nReturnCode;
}
8. AfxGetApp是如何获得CWinApp继承类(在此即CMyWinApp)的实体的指针的?
AfxGetApp是一个全局函数,定义于AFXWIN1.INL中,如下:
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() { return afxCurrentWinApp; }
而afxCurrentWinApp则又是一个宏,定于AFXWIN.H中:
#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_pCurrentWinApp = this;
这样,应该看清楚他是如何获得CWinApp继承类(在此是指CMyWinApp类)的实体的指针了吧? 那么,pApp->InitApplication() / pApp->InitInstance() / pApp->Run()就可以理解成这样:
CMyWinApp::InitApplication(); / CMyWinApp::InitInstance(); / CMyWinApp::Run() 因为CMyWinApp只继承了InitInstance的实现,所以,就导致调用:
CMyApp::InitApplication(); / CMyWinApp::InitInstance(); / CWinApp::Run()
9. AfxWinInit ----AFX内部的初始化操作:②
AfxWinInit是CWinApp构造函数后的第一个操作,也就是第二步操作,以下是它的操作摘要:
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)
...{
ASSERT(hPrevInstance == NULL);
AFX_MODULE_STATE* pState = AfxGetModuleState();
pState->m_hCurrentInstanceHandle = hInstance;
pState->m_hCurrentResourceHandle = hInstance;
// fill in the initial state for the application
CWinApp* pApp = AfxGetApp(); //还是获得CMyWinApp实体的指针。
if (pApp != NULL)
...{
// Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance = hInstance; //设定WinMain四个参数的初始值。
pApp->m_hPrevInstance = hPrevInstance;
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
}
if (!afxContextIsDLL)
AfxInitThread();
return TRUE;
}
10. CWinApp::InitApplication()函数:③
AfxWinInit函数对内部初始化之后,进入第三步操作:InitApplication,由于CMyWinApp继承自CWinApp,而InitApplication又是CWinApp的一个虚拟函数,我们在CMyWinApp中没有改写它(大部分情况下也不需要改写它),所以我们调用的是CWinApp::InitApplication(),下面我们来看看InitApplication在MFC中做了什么动作?
BOOL CWinApp::InitApplication()
...{
if (CDocManager::pStaticDocManager != NULL)
...{
if (m_pDocManager == NULL)
CDocManager::pStaticDocManager = NULL;
}
if (m_pDocManager != NULL)
m_pDocManager->AddDocTemplate(NULL);
else
CDocManager::bStaticInit = FALSE;
return TRUE;
}
这些操作都是MFC为内部管理而做的。只要记住一点,我们的派生类无需改写它,它是关于CDocManager的类,关于该类详见后解...
11. CMyWinApp::InitInstance()函数:④
BOOL CMyWinApp::InitInstance()
...{
m_pMainWnd = new CMyFrameWnd;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
哦,它生成一个CMyFrameWnd的实体,把主应用程序与窗口函数联系起来(上面说过,CFrameWnd等于于SDK中的WndProc)。在SDK的代码中,要让窗口Show和Update之前必须还要做些工作,什么工作呢?看看SDK程序:
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
...{
static TCHAR szAppName[] = TEXT ("SysMets2") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ; //注册窗口类
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
...{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 2"), //构建窗口
WS_OVERLAPPEDWINDOW | WS_VSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
...{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
其中,在ShowWindow和UpdateWindow之前,要做两件事,一是注册窗口类(红色部份),二是构建窗口(蓝色部分),那么,我们可以肯定,就这一句代码:m_pMainWnd = new CMyFrameWnd; 就可以为我们做完了这两件事—注册窗口和构建窗口。是怎么做的呢?首先,它调用CMyFrameWnd的构造函数,该构造函数的实现是这样的:(代码详见G:/Program/MyMFC/Hello/Hello.dsw)
CMyFrameWnd::CMyFrameWnd()
...{
Create(NULL,"Hello MFC",WS_OVERLAPPEDWINDOW,rectDefault,NULL,"MainMenu");
}
调用CMyFrameWnd中的Create成员函数,CMyFrameWnd中实现该Create函数了吗?没有,那再往父类追朔,CFrameWnd中实现Create函数了吗?实现了!那调用CFrameWnd::Create()函数。
BOOL CFrameWnd::Create( LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle = WS_OVERLAPPEDWINDOW,
const RECT& rect = rectDefault,
CWnd* pParentWnd = NULL,
LPCTSTR lpszMenuName = NULL,
DWORD dwExStyle = 0,
CCreateContext* pContext = NULL );
lpszClassName指定WNDCLASS窗口类,我们至今为止还没有注册WNDCLASS窗口类,所以,只能先定义为NULL,往后再看,lpszWindowName指定窗口的标题。后面的一些参数都有默认值,关于这些默认值的一些说明,可以详见<《MFC深入浅出》第三篇第6章282-283页。
调用CFramwWnd::Create函数,可以引发注册窗口类动作,也就是上面的第一件事:看看它是怎么实现的?
BOOL CFrameWnd::Create(...) //此处省略了它的参数
...{
HMENU hMenu = NULL;
if(lpszMenuName!=NULL)
...{
HINSTANCE hInst == AfxFindResourceHandle(lpszMenuName,RT MENU);
hMenu = ::LoadMenu(hInst,lpszMenuName);
}
m_strTitle = lpszWindowName;
CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext);
return TRUE;
}
CFrameWnd::Create函数中调用CreateEx函数,而CreateEx函数会调致调用CWnd::CreateEx函数的操作:
BOOL CWnd::CreateEx(...) //此处省略了它的参数
...{
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;
PreCreateWindow(cs);
AfxHookWindowCreate(this); //此動作將在第9章探討。
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);
...
}
啊哈!CreateWindowEx我们可以容易看出来,它把我们的第二件事,也就是构建窗口完成了,那么,第一件事(注册窗口函数),就是在调用PreCreateWindows函数中完成的。看看PreCreateWindow函数长得什么样?
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
...{
if (cs.lpszClass == NULL)
...{
AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);
cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
}
...
}
有点头目了,因为出现了RegisterClass的字样,再看看AfxDeferRegisterClass是什么东东:
#define AfxDeferRegisterClass(fClass)
((afxRegisterClass & fClass)? TRUE : AfxEndDeferRegisterClass(fClass))
而afxRegisterClass又是一个宏:
#define afxRegisterClass AfxGetModuleState()->m_fRegisteredClasses
好,最主要最主要的注册窗口动作将发生在AfxEndDeferRegisterClass中:
关于此函数代码有点长,可以参见《深入浅出MFC》第285-286页。但它最主要做了几件事,一,成生一个WNDCLASS对象,二、指定wndclass的lpfnWndProc、hInstance、hCursor的值,三、调用全局函数AfxRegisterClass注册窗口。
看看,就一个CMyFrameWnd* MyFrameWnd = new CMyFrameWnd代码,并且在CMyFrameWnd构造函数中改写Create函数,就可以把注册窗口类、构建窗口的一些繁琐工作全部在台面下做完。台上完全不动丝毫声色。回顾一下上面这一大段的说明的简要架构:
调用CMyFrameWnd::CMyFrameWnd()--->调用CFrameWnd::Create()函数--->调用CWnd::CreateEx()函数--->调用CFrameWnd::PreCreateWindow()---> 调用AfxRegisterClass() -->调用::CreateWindowEx()
12. CWinApp::Run – 程序生命的活水源头⑤
我们的程序到这里,已到第五步了。窗口类已注册好了,窗口也Create出来了,ShowWindow和UpdateWindow也执行了,但是,对照 SDK程序,WinMain中还有一步没有做,就是翻译消息和分派消息(TranslateMessage和DispatchMessage)。别急,这些工作做在了CWinApp::Run中。
我们的CMyWinApp没有改写Run这个虚拟函数,所以,应该执行CWinApp::Run()函数,这是什么样的执行情况呢?
int CWinApp::Run()
...{
if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
...{
// Not launched /Embedding or /Automation, but has no main window!
TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application. ");
AfxPostQuitMessage(0);
}
return CWinThread::Run();
}
其中,m_pMainWnd是在CWinThread定义的一个指向CWnd的指针,它的作用就像:AfxGetApp()->m_pMainWnd; 指向本应用程序的窗口类的指针。这里的意思应该是判断应用程序中窗口类存不存在,如不存在,代码发出警告后退出。如存在,执行CWinThread::Run()
int CWinThrad::Run()
...{
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
for (;;)
...{
while (bIdle && !::PeekMessage(&m_msgCur,NULL,NULL,PM_NOREMOVE)
...{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE;
}
// phase2: pump messages while available
do
...{ // pump message, but quit on WM_QUIT
if (!PumpMessage())
return ExitInstance();
// reset "no idle" state after pumping "normal" message
if (IsIdleMessage(&m_msgCur))
...{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
ASSERT(FALSE);
}
CWinThread::Run()
BOOL CWinThread::PumpMessage()
...{
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
...{
return FALSE;
}
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
...{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
return TRUE;
}
到此,对于SDK中WinMain函数中所做的全部动作,在MFC中已一一揪出来了。可以看出,它们全隐含在各式各样的类中。彼此百转曲折,但最后还是完成了和SDK一模一样的操作。下面就只剩下窗口处理函数的问题了,我们在AfxEndDeferRegisterClass中看到过这样的代码: 中有一个PumpMessage()函数,是我们分派消息的地方:
而在CWinApp类的构造函数中,有一段代码是这样定义的:
wndcls.lpfnWndProc = DefWindowProc;
意思是说,把所有的消息都发送给DefWindowProc这个函数,但是,我们了解到,处理消息的是继承CFrameWnd的类(即CMyFrameWnd),DefWindowProc是怎么把收到的消息再传给CFrameWnd类的呢?其中牵扯到了MFC的乾坤大挪移,这部分讨论见后,亦可见《深入浅出MFC》第九章。