MFC中阴晦的WinMain

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

http://www.cnblogs.com/shipfi/archive/2005/10/13/121629.html

 

 首先,我们必须在源文件中定义了一个CMyWinApp的实体 它是第一步操作。(如: CMyWinApp MyWinApp;注:代码详见:G:/Program/MyMFC/Hello/Hello.dsw),CMyWinApp继承自CWinAppCMyWinApp无构造函数,但需调用CWinApp构造函数。

 

配置好MyWinApp实体的一些信息,包括获得该线程的句柄,线程的ID,该实体的句柄等。配置完后,_tWinMain登场,注意,我们没有撰写WinMain的代码,WinMainMFC早已准备好并由链接器直接加到应用程序代码中的。_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(),下面我们来看看InitApplicationMFC中做了什么动作? 
     

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的代码中,要让窗口ShowUpdate之前必须还要做些工作,什么工作呢?看看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 ; 

 

其中,在ShowWindowUpdateWindow之前,要做两件事,一是注册窗口类(红色部份),二是构建窗口(蓝色部分),那么,我们可以肯定,就这一句代码: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深入浅出》第三篇第6282-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对象,二、指定wndclasslpfnWndProchInstancehCursor的值,三、调用全局函数AfxRegisterClass注册窗口。 

看看,就一个CMyFrameWnd* MyFrameWnd = new CMyFrameWnd代码,并且在CMyFrameWnd构造函数中改写Create函数,就可以把注册窗口类、构建窗口的一些繁琐工作全部在台面下做完。台上完全不动丝毫声色。回顾一下上面这一大段的说明的简要架构:

调用CMyFrameWnd::CMyFrameWnd()--->调用CFrameWnd::Create()函数--->调用CWnd::CreateEx()函数--->调用CFrameWnd::PreCreateWindow()---> 调用AfxRegisterClass() -->调用::CreateWindowEx()

12. CWinApp::Run – 程序生命的活水源头⑤

我们的程序到这里,已到第五步了。窗口类已注册好了,窗口也Create出来了,ShowWindowUpdateWindow也执行了,但是,对照 SDK程序,WinMain中还有一步没有做,就是翻译消息和分派消息(TranslateMessageDispatchMessage)。别急,这些工作做在了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; 

 

 到此,对于SDKWinMain函数中所做的全部动作,在MFC中已一一揪出来了。可以看出,它们全隐含在各式各样的类中。彼此百转曲折,但最后还是完成了和SDK一模一样的操作。下面就只剩下窗口处理函数的问题了,我们在AfxEndDeferRegisterClass中看到过这样的代码: 中有一个PumpMessage()函数,是我们分派消息的地方:

而在CWinApp类的构造函数中,有一段代码是这样定义的:  


wndcls.lpfnWndProc = DefWindowProc;


意思是说,把所有的消息都发送给DefWindowProc这个函数,但是,我们了解到,处理消息的是继承CFrameWnd的类(即CMyFrameWnd),DefWindowProc是怎么把收到的消息再传给CFrameWnd类的呢?其中牵扯到了MFC的乾坤大挪移,这部分讨论见后,亦可见《深入浅出MFC》第九章。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值