MFC中阴晦的WinMain

阅读: 评论:0

MFC中阴晦的WinMain

MFC中阴晦的WinMain

无意中看到这篇文章,我觉得比MFC设计基础写的好,所以就整理了一下作为以后参考。

.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, p, rect.right - rect.left, rect.bottom - p, 
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 (ssage != 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》第九章。

 

 

本文发布于:2024-01-29 05:28:34,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170647731713026.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:UX基础
下一篇:付款方式 OA DA
标签:阴晦   MFC   WinMain
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23