• 鸡啄米vc++2010系列3(MFC应用程序框架分析)


    MFC应用程序框架分析

       一.SDK应用程序与MFC应用程序运行过程的对比

           程序运行都要有入口函数,在之前的C++教程中都是main函数,而Windows应用程序的入口函数是WinMain函数,MFC程序也是从WinMain函数开始的。下面鸡啄米就给出用Windows SDK写的“HelloWorld”程序,与应用程序框架进行对比,这样能更好的了解框架是怎样运行的。Windows SDK开发程序就是不使用MFC类库,直接用Windows API函数进行软件开发。鸡啄米不是要讲解SDK开发,只是为了对比而简单介绍,至于SDK开发可以在大家学完MFC以后选择是否要研究,一般来说有简单了解就可以了。

           SDK应用程序

           首先,给出Windows SDK应用程序“HelloWorld”的源码:  

    C++代码
    1. #include <windows.h>    
    2.   
    3. LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);   
    4.      
    5. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)      
    6. {      
    7.   const static TCHAR appName[] = TEXT("Hello world");      
    8.   WNDCLASSEX myWin;      
    9.   myWin.cbSize = sizeof(myWin);      
    10.   myWin.style = CS_HREDRAW | CS_VREDRAW;      
    11.   myWin.lpfnWndProc = myWndProc;      
    12.   myWin.cbClsExtra = 0;      
    13.   myWin.cbWndExtra = 0;      
    14.   myWin.hInstance = hInstance;      
    15.   myWin.hIcon = 0;      
    16.   myWin.hIconSm  = 0;      
    17.   myWin.hCursor = 0;      
    18.   myWin.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);      
    19.   myWin.lpszMenuName = 0;      
    20.   myWin.lpszClassName = appName;      
    21.   //Register      
    22.   if (!RegisterClassEx(&myWin)) return 0;      
    23.   const HWND hWindow = CreateWindow(      
    24.     appName,      
    25.     appName,      
    26.     WS_OVERLAPPEDWINDOW,      
    27.     CW_USEDEFAULT,      
    28.     CW_USEDEFAULT,      
    29.     CW_USEDEFAULT,      
    30.     CW_USEDEFAULT,      
    31.     0,      
    32.     0,      
    33.     hInstance,      
    34.     0);      
    35.   ShowWindow(hWindow,iCmdShow);      
    36.   UpdateWindow(hWindow);      
    37.   {      
    38.     MSG msg;      
    39.     while(GetMessage(&msg,0,0,0))      
    40.     {      
    41.       TranslateMessage(&msg);      
    42.       DispatchMessage(&msg);      
    43.     }      
    44.     return (int)msg.wParam;      
    45.   }      
    46. }      
    47.      
    48. LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)      
    49. {      
    50.   if (msg==WM_PAINT)      
    51.   {      
    52.     PAINTSTRUCT ps;      
    53.     const HDC hDC = BeginPaint(hWindow,&ps);      
    54.     RECT rect;      
    55.     GetClientRect(hWindow,&rect);      
    56.     DrawText(hDC,TEXT("HELLO WORLD"),-1,&rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);      
    57.     EndPaint(hWindow,&ps);      
    58.     return 0;      
    59.   }      
    60.   else if (msg==WM_DESTROY)      
    61.   {      
    62.     PostQuitMessage(0);      
    63.     return 0;      
    64.   }      
    65.   return DefWindowProc(hWindow,msg,wParam,lParam);      
    66. }  

           上面的程序运行的流程是:进入WinMain函数->初始化WNDCLASSEX,调用RegisterClassEx函数注册窗口类->调用ShowWindow和UpdateWindow函数显示并更新窗口->进入消息循环。关于消息循环再简单说下,Windows应用程序是消息驱动的,系统或用户让应用程序进行某项操作或完成某个任务时会发送消息,进入程序的消息队列,然后消息循环会将消息队列中的消息取出,交予相应的窗口过程处理,此程序的窗口过程函数就是myWndProc函数,窗口过程函数处理完消息就完成了某项操作或任务。本例是要显示“HELLO WORLD”字符串,UpdateWindow函数会发送WM_PAINT消息,但是此消息不经过消息队列而是直接送到窗口过程处理,在窗口过程函数中最终绘制了“HELLO WORLD”字符串。


           MFC应用程序

           下面是MFC应用程序的运行流程,通过MFC库中代码进行分析:

           首先在HelloWorld.cpp中定义全局对象theApp:CHelloWorldApp theApp;。调用CWinApp和CHelloWorldApp的构造函数后,进入WinMain函数(位于appmodul.cpp中)。

    C++代码
    1. extern "C" int WINAPI   
    2. _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,   
    3.     _In_ LPTSTR lpCmdLine, int nCmdShow)   
    4. #pragma warning(suppress: 4985)   
    5. {   
    6.     // call shared/exported WinMain   
    7.     return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);   
    8. }  

           在TCHAR.h中,有此定义:#define _tWinMain   WinMain,所以这里的_tWinMain就是WinMain函数。它调用了AfxWinMain函数(位于WinMain.cpp中)。

    C++代码
    1. int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)   
    2. {    
    3.        .............略   
    4.        // App global initializations (rare)   
    5.        if (pApp != NULL && !pApp->InitApplication())   
    6.               goto InitFailure;   
    7.   
    8.        if (!pThread->InitInstance())   
    9.        {   
    10.               .........略   
    11.        }   
    12.   
    13.        // Run函数位于THRDCORE.cpp中,由此函数进入消息循环   
    14.        nReturnCode = pThread->Run();   
    15.   
    16.        ..............略   
    17.   
    18.        return nReturnCode;   
    19. }   

           上面InitInstance函数的代码如下:

    C++代码
    1. BOOL CTestApp::InitInstance()        
    2. {       
    3.        .............略       
    4.        CSingleDocTemplate* pDocTemplate;       
    5.        pDocTemplate = new CSingleDocTemplate(       
    6.               IDR_MAINFRAME,       
    7.               RUNTIME_CLASS(CTestDoc),       
    8.               RUNTIME_CLASS(CMainFrame),      // main SDI frame window       
    9.               RUNTIME_CLASS(CTestView));     
    10.        if (!pDocTemplate)   
    11.              return FALSE;     
    12.        AddDocTemplate(pDocTemplate);       
    13.        // Parse command line for standard shell commands, DDE, file open       
    14.       
    15.        CCommandLineInfo cmdInfo;       
    16.        ParseCommandLine(cmdInfo);       
    17.       
    18.        //ProcessShellCommand位于AppUI2.cpp中,注册并创建窗口       
    19.        if (!ProcessShellCommand(cmdInfo))       
    20.              return FALSE;       
    21.       
    22.        m_pMainWnd->ShowWindow(SW_SHOW);       
    23.        m_pMainWnd->UpdateWindow();       
    24.       
    25.        return TRUE;       
    26. }      

           InitInstance中的ProcessShellCommand函数又调用了CMainFrame的LoadFrame函数注册并创建了窗口,执行完ProcessShellCommand函数以后,调用了m_pMainWnd的ShowWindow和UpdateWindow函数显示并更新框架窗口。这些是不是与上面的SDK程序十分类似?

           接下来该是消息循环了,上面的AfxWinMain函数中调用了pThread的Run函数(位于THRDCORE.cpp中),在Run中包含了消息循环。Run函数的代码如下:

    C++代码
    1. int CWinThread::Run()       
    2. {       
    3.         .............略       
    4.         // phase2: pump messages while available       
    5.         do      
    6.         {       
    7.               // pump message, but quit on WM_QUIT       
    8.               if (!PumpMessage())       
    9.                      return ExitInstance();       
    10.       
    11.               // reset "no idle" state after pumping "normal" message       
    12.               if (IsIdleMessage(&m_msgCur))       
    13.               {       
    14.                      bIdle = TRUE;       
    15.       
    16.                      lIdleCount = 0;       
    17.       
    18.               }       
    19.        } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));       
    20.        ..............略       
    21. }       
    22.         
    23. BOOL CWinThread::PumpMessage()       
    24. {     
    25.        return AfxInternalPumpMessage();    
    26. }    
    27.   
    28. BOOL AFXAPI AfxInternalPumpMessage()   
    29. {   
    30.        _AFX_THREAD_STATE *pState = AfxGetThreadState();   
    31.       
    32.        if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))          
    33.        {       
    34.              .............略       
    35.        }       
    36.        ...............略       
    37.        if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))   
    38.        {   
    39.              ::TranslateMessage(&(pState->m_msgCur));   
    40.              ::DispatchMessage(&(pState->m_msgCur));   
    41.        }     
    42.       
    43.        return TRUE;       
    44. }       

           我们看到PumpMessage中通过调用GetMessage、TranslateMessage、DispatchMessage等建立了消息循环并投递消息。

           窗口过程函数AfxWinProc形式如下:

    C++代码
    1. LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam)   
    2. {   
    3.       ……   
    4.       CWnd*pWnd=CWnd::FromHandlePermanent(hWnd);  
    5.       ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);   
    6. }  

           两者运行过程对比

           到此,通过对比可以发现,MFC应用程序的运行流程与SDK程序是类似的,都是先进行一些初始化过程,再注册并创建窗口,然后显示、更新窗口,最后进入消息循环,消息都由窗口过程函数处理。现在大家是不是觉得有些头绪了?在运行流程上有基本的掌握即可。

           二.MFC应用程序框架主要类之间的关系

           在第二讲中,给大家演示了如何利用应用程序向导生成单文档应用程序框架,可以看到程序的基本框架和必要的代码都自动生成了,上一讲又讲解了文件组成结构,实际上在前面自动生成的框架中比较重要的类包括以下几个:CHelloWorldApp、CMainFrame、CHelloWorldDoc和CHelloWorldView,至于其他的类比如CClassView、CFileView等都是在框架窗口(CMainFrame)上创建的面板等,不是必要的。

           鸡啄米就四个主要类的关系简单讲下,CHelloWorldApp类处理消息,将收到的消息分发给相应的对象。CMainFrame是视图CHelloWorldView的父窗口,视图CHelloWorldView就显示在CMainFrame的客户区中。视图类CHelloWorldView用来显示文档类CHelloWorldDoc中的数据,并根据对视图类的操作修改文档类的数据。一个视图类只能跟一个文档类相联系,而一个文档类可以跟多个视图类相联系。关于视图类和文档类的关系后面会详细讲解。

    转载:http://www.jizhuomi.com/software/145.html
  • 相关阅读:
    js中的内存空间
    MAC升级Nodejs和Npm到最新版
    Linux性能相关工具地图
    ios UILable自包裹
    vue项目使用WebViewJavascriptBridge
    K3Wise后台生成单据编号和单据内码
    解析使用HTTP方式调用金蝶查询单据返回的LIST数据
    jenkins指定构建状态下(构建失败),发送通知到企业微信群 Duke
    MySQLdb._exceptions.OperationalError: (2002, "Can't connect to MySQL server on 'db' (115)") Duke
    jenkins主题简单美化 Duke
  • 原文地址:https://www.cnblogs.com/zfluo/p/5131923.html
Copyright © 2020-2023  润新知