• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            何苦做程序?!

            業(yè)精于勤,荒于嬉;行成于思,毀于隨! I believe , I can flying! 勿在浮砂筑高臺(tái)!

            C++博客 首頁 新隨筆 聯(lián)系 聚合 管理
              4 Posts :: 1 Stories :: 14 Comments :: 0 Trackbacks

             MFC程序設(shè)計(jì)
                         之來龍去脈
             題記:前些日子一直想寫這個(gè)東西,做了一個(gè)開頭放在我的BLOG上(但是名為<MFC框架程序WINMAIN函數(shù)分析>),到后來就沒有再管了,其實(shí)那只是冰山一角.具體MFC是怎么運(yùn)行的,還是沒有交待清楚,雖然自己的BLOG很少有人光顧,但是本著做事兒就要做到底的心態(tài),繼續(xù)完成該文。
             說明:1、本文作者在VS2003中跟蹤代碼,此代碼為VS2003中拷貝,使用MFC7。
                   2、不同框架的MFC程序由所不同,本文以單文檔為例。
                      3、本文讀者需要有一定的SDK的基礎(chǔ),不需要太多,至少知道它的基本框架和來龍去脈即可!
                      4、文章只想起到說明作用,所以代碼會(huì)有一些刪除。
             學(xué)MFC,竟然還不知道MFC的MAIN函數(shù)在什么地方?怎么運(yùn)行的?實(shí)在不高明。
             看過候捷(JJHOU)老師的《深入淺出MFC》的,對它一定很熟悉。呵呵,本文是獻(xiàn)給沒有看過那本書,但是又很希望學(xué)習(xí)MFC程序設(shè)計(jì)的朋友的。(沒有看過那本書的朋友還不趕快去買?)其實(shí)本文,主要是對《深入淺出MFC》第六章的一個(gè)總結(jié)和補(bǔ)充罷了!(本文有該書不同的地方,也有一些筆者自己的見解!)
             言歸正傳。
             假如你用AppWizard一步一步NEXT下來,然后在CLASSVIEW中去找尋WINMAIN函數(shù),那么你只有失望。MFC最大的特點(diǎn)是什么?封裝!MFC的確封裝的太好了,以至于很多想學(xué)習(xí)MFC的人都望而卻步。閑話少說,還是繼續(xù)我們今天的話題,MAIN函數(shù)!實(shí)話告訴你吧,即使你搜索所有的MFC生成的文件,都無法發(fā)現(xiàn)WINMAIN的字眼,那么它就近在什么地方呢?
             我相信你已經(jīng)想到,MAIN函數(shù)應(yīng)該在主要的應(yīng)用程序文件中。難道是“您定義的程序名.cpp”這個(gè)文件?不錯(cuò)就是它。再Crtl+F一下,看有沒有我們要找的WINMAIN函數(shù)?看來你又要失望了,但是你注意有這樣一句:
             
            /////////////////////////////////////////////////////////////////////////////
            // The one and only CMyApp object

             CMyApp theApp;   //本人建立的工程名為My。
             
             
             是不是很特別,再注意一下那句注釋“The one and only CMyApp object”,每個(gè)應(yīng)用程序有且只用一個(gè)CMyApp對象。我想你應(yīng)該想到了,WinMain函數(shù)每個(gè)程序也只能有一個(gè),那么這個(gè)全局對象跟WinMain函數(shù)肯定有莫大的關(guān)系?沒錯(cuò),相信你的直覺。
             特別注意:深曉C++細(xì)節(jié)的人一定知道,全局對象優(yōu)先于MAIN函數(shù)執(zhí)行的道理。如果你不知道也沒關(guān)系,那么我在這里告訴你:“全局對象優(yōu)先于MIAN函數(shù)執(zhí)行,且構(gòu)建于棧中,切記,切記!”
             現(xiàn)在,我們該深入WinMain運(yùn)行機(jī)制了,確切的說,應(yīng)該是MFC的機(jī)制!
             首先,看看MFC的庫文件把,它能給我們帶來許多驚喜。(vc6的相應(yīng)的目錄是\Microsoft Visual Studio\VC98\MFC\SRC;VC7相應(yīng)的目錄是\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc)
             現(xiàn)在我們就從這個(gè)全局下手,開始今天的旅途。
             CMyApp theApp;
             此時(shí),系統(tǒng)會(huì)執(zhí)行CMyApp的父類(CWinApp)構(gòu)造函數(shù),再執(zhí)行CMyApp的構(gòu)造函數(shù)。(先有老爹,再有兒子!),此時(shí)就會(huì)調(diào)用CWinApp的構(gòu)造函數(shù)。
             
             CWinApp的構(gòu)造函數(shù)(在VC提供的MFC代碼中以“文中的一個(gè)字或詞組”的方式查詢關(guān)鍵字,此時(shí)打開APPCORE.CPP,以下使用相同搜索方式,不再復(fù)述。)找到以下內(nèi)容:
             CWinApp::CWinApp(LPCTSTR lpszAppName)
             {
              if (lpszAppName != NULL)
               m_pszAppName = _tcsdup(lpszAppName);
              else
               m_pszAppName = NULL;
             
              // initialize CWinThread state
              AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
              AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
              ASSERT(AfxGetThread() == NULL);
              pThreadState->m_pCurrentWinThread = this;
              ASSERT(AfxGetThread() == this);
              m_hThread = ::GetCurrentThread();
              m_nThreadID = ::GetCurrentThreadId();
             
              // initialize CWinApp state
              ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
              pModuleState->m_pCurrentWinApp = this;
              ASSERT(AfxGetApp() == this);
             ... ...
             }
             OK,就到這里就可以了,仔細(xì)看上面代碼,它已經(jīng)完成了應(yīng)用程序線程額的啟動(dòng),它給予了我們程序的生命。現(xiàn)在請注意:
                    pThreadState->m_pCurrentWinThread = this;
             pModuleState->m_pCurrentWinApp = this;
                 這兩行代碼其實(shí)都是做的一件事兒。
                 這段代碼的意思是,獲得了CMyApp的全局對象的this指針。(此時(shí)你肯定要疑問,為什么是CMyApp的指針?this目前是在CWinApp中啊?   對此我的答案是,可是你是由CMyApp的對象引發(fā)的CWinApp的構(gòu)造啊!!)這個(gè)指針可非一般的人物,稍后我們的很多工作都要靠它完成。
                 CWinApp之中的成員變量將因?yàn)閠heApp這個(gè)全局對象的誕生而獲得配置和初始值。
              構(gòu)造完父類,現(xiàn)在構(gòu)造子類。可是我們看到,AppWizard給我們的子類里它什么也沒做?是的,這一切都聽從你的安排!
                CMyApp::CMyApp()
                {
             // TODO: add construction code here,
             // Place all significant initialization in InitInstance
             }
             
             
             
              接下來就是今天的主角兒了,搜索關(guān)鍵字“WinMain”,出現(xiàn)很多文件。別急,因?yàn)楝F(xiàn)在我們應(yīng)該先看看WinMain的聲明。打開appmodul.cpp:

                 _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
             LPTSTR lpCmdLine, int nCmdShow)
             {
             // call shared/exported WinMain
             return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
             }
             
            這里_tWinMain是為了支持UNICODE而命名的一個(gè)宏,真正起作用的是AfxWinMain,注意看看它的參數(shù),是不是和SDK的WinMain函數(shù)一樣?
             現(xiàn)在再搜索下AfxWinMain,其實(shí)在winmain.cpp中:
             
            int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
             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();
            ... ...
            }
             此段代碼注意五個(gè)細(xì)節(jié):
             CWinApp* pApp = AfxGetApp();
             意為獲得對象指針,其實(shí)就是剛才那個(gè)THIS。不記得了?指向CMyApp的那個(gè)!還值得注意的是,Afx意是全局的,隨時(shí)你都可以調(diào)用它。(AFX就是MFC開發(fā)小組的開發(fā)代號,意為Application Framework 傳說X只是為了好看,沒實(shí)在意思?!)
             if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
             AfxWinInit完成了線程的初始化和窗框類的注冊。具體參看appinit.cpp中的定義。
             if (pApp != NULL && !pApp->InitApplication())
             其實(shí)pApp和pThread是同一個(gè)指針,都是指向CMyApp的指針,這里因?yàn)镃MyApp中沒有定義InitApplication,實(shí)際上就調(diào)用的CWinApp::InitApplication(),完成了MFC的內(nèi)容管理。
             if (!pThread->InitInstance())
             因?yàn)镃MyApp中改寫了它,所以調(diào)用CMyApp中的,其實(shí)它也是初始化工作。此時(shí)也完成了默認(rèn)窗口類的定義。假如你熟悉SDK編程的話,一定不會(huì)忘記窗口類的設(shè)計(jì)、注冊、創(chuàng)建、現(xiàn)實(shí)及更新的步驟,此時(shí)MFC以為你設(shè)計(jì)好了默認(rèn)的窗口類。
             現(xiàn)在你不禁要疑問,InitApplication()和InitInstance()有何不同?
             答案是,假如你執(zhí)行一個(gè)程序,于是兩個(gè)函數(shù)都會(huì)被調(diào)用;當(dāng)你在不關(guān)閉前一個(gè)程序的前提下,再執(zhí)行一個(gè)程序,那么就只執(zhí)行后一個(gè)函數(shù)。
             nReturnCode = pThread->Run();
             這個(gè)一步驟在《深入淺出MFC》中被成為程序的活水源頭,在我看來它就是你開車踩油門的步驟。待會(huì)我們會(huì)具體闡述!
             
             在設(shè)計(jì)窗口類以后,就應(yīng)該是注冊,MFC自動(dòng)調(diào)用(跳轉(zhuǎn)到)AfxEndDeferRegisterClass(WINCORE.CPP中),為你注冊了五個(gè)窗口類,分別是:AfxWnd,AfxCreateBar,AfxMDIFrame,AfxFrameOrView,AfxOleControl以上窗口類MFC將自動(dòng)轉(zhuǎn)化成獨(dú)立無二的類名,供其調(diào)用。
             在窗口的注冊以后,就應(yīng)該是窗口的創(chuàng)建工作,此時(shí)會(huì)調(diào)用CFrameWnd::Create(),該代碼位于WINFRM.Cpp中
            BOOL CFrameWnd::Create(LPCTSTR lpszClassName,
             LPCTSTR lpszWindowName,
             DWORD dwStyle,
             const RECT& rect,
             CWnd* pParentWnd,
             LPCTSTR lpszMenuName,
             DWORD dwExStyle,
             CCreateContext* pContext)
            {
             HMENU hMenu = NULL;
             if (lpszMenuName != NULL)
             {
              // load in a menu that will get destroyed when window gets destroyed
              HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU);
              if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
              {
               TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.\n");
               PostNcDestroy();            // perhaps delete the C++ object
               return FALSE;
              }
             }

             m_strTitle = lpszWindowName;    // save title for later

             if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
              rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
              pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
             {
              TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.\n");
              if (hMenu != NULL)
               DestroyMenu(hMenu);
              return FALSE;
             }

             return TRUE;
            }
             
             其中完成了窗口的創(chuàng)建工作,里面還涉及擴(kuò)展風(fēng)格的調(diào)用CreateEx,具體細(xì)節(jié)請參看MSDN。
             
             此時(shí)你不禁要問,我們的事兒都讓MFC做完了?工業(yè)化生產(chǎn)出來的窗口都是千篇一律啊,我要有我自己的風(fēng)格!
             別急,MFC給用戶提供了一個(gè)修改窗口設(shè)計(jì)的機(jī)會(huì)那就是:PreCreateWindow(CREATESTRUCT& cs) 你在MSDN中查詢一下CREATESTRUCT這個(gè)結(jié)構(gòu)體,你會(huì)發(fā)現(xiàn)它和我們的CreateWindow幾乎是一模一樣,這個(gè)就是MFC留給你修改窗口的一個(gè)機(jī)會(huì)。在PreCreateWindow時(shí),會(huì)跳到CWnd::PreCreateWindow,里面有一個(gè)宏:AfxDeferRegisterClass,它的作用是:如果該窗口類沒有被注冊,那么就注冊它;如果注冊了,就什么也不管!
             窗口類的設(shè)計(jì)、注冊、創(chuàng)建都已經(jīng)完成,現(xiàn)在只剩下更新和顯示了。這些工作都交由 CMyApp::InitInstance()完成:
              m_pMainWnd->ShowWindow(SW_SHOW);
              m_pMainWnd->UpdateWindow();
             現(xiàn)在if (!pThread->InitInstance())的工作已經(jīng)完成,按照MAIN函數(shù)的內(nèi)容,接下來該:nReturnCode = pThread->Run()了
             此時(shí)應(yīng)該調(diào)用CMyApp的Run()函數(shù),但是在CMyApp類中,根本沒有聲明或定義這樣一個(gè)函數(shù),根據(jù)多態(tài)性的原來,指針遷升,指向CWinApp::Run(),其代碼位于APPCORE.CPP中:
             
             int CWinApp::Run()
             {
              if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
              {
               // Not launched /Embedding or /Automation, but has no main window!
               TRACE(traceAppMsg, 0, "Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");
               AfxPostQuitMessage(0);
              }
              return CWinThread::Run();
             }
             
             最后你會(huì)發(fā)現(xiàn),它由調(diào)用了一個(gè)CWinThread::Run(),此時(shí)你就看不到CWinThread::Run()的代碼了(至少筆者沒有找到,因?yàn)槲④浿惶峁┝瞬糠諱FC代碼。)但是你可以在MSDN中找到CWinThread::Run()的描述:
             Run 控制線程的函數(shù)。包含消息泵。一般不重寫。
             再具體點(diǎn)就是:
             Run acquires and dispatches Windows messages until the application receives a WM_QUIT message. If the thread's message queue currently contains no messages, Run calls OnIdle to perform idle-time processing. Incoming messages go to the PreTranslateMessage member function for special processing and then to the Windows function TranslateMessage for standard keyboard translation. Finally, the DispatchMessage Windows function is called.
             Run is rarely overridden, but you can override it to implement special behavior.
             This member function is used only in user-interface threads.
             原來它把消息循環(huán)包裝了一下,在MFC中稱為消息映射(message map)的東西!至于消息映射的具體細(xì)節(jié)本人會(huì)另寫文章說明!
             OK,MFC不再神秘,掌握了它的來龍去脈,再看其他的MFC書籍的時(shí)候,就知道我該怎么做?為什么我要這樣做?起到了知其然又知其所以然的效果,這就是我所追求的技術(shù)境界。
             
             

            posted on 2006-02-18 12:04 lewislau 阿木 閱讀(4037) 評論(3)  編輯 收藏 引用

            評論

            # re: 【原創(chuàng)】MFC程序設(shè)計(jì)之來龍去脈 2006-02-23 22:18 一兩清風(fēng)
            在VC6中,可以使用step by step方式的DEBUG來逐步調(diào)試剛建立的MFC程序,觀察IDE窗口標(biāo)題就會(huì)發(fā)現(xiàn)被隱藏源碼所在文件的位置,觀察DEBUG窗口的箭頭就會(huì)發(fā)現(xiàn)MFC程序的執(zhí)行順序......。  回復(fù)  更多評論
              

            # re: 【原創(chuàng)】MFC程序設(shè)計(jì)之來龍去脈 2006-03-10 11:46 Wolfgang Jia
            編程人員都知道,線程是由系統(tǒng)內(nèi)核啟動(dòng)的。當(dāng)CWinApp構(gòu)造函數(shù)調(diào)用時(shí),其實(shí)已經(jīng)有一個(gè)包含一個(gè)線程的進(jìn)程了(您可以F10單步執(zhí)行后在任務(wù)管理器里看到線程數(shù)和進(jìn)程數(shù)分別增加 1)。CWinThread只是對thread的包裝。theApp對象是從_tWinMain中獲得線程的。_tWinMain只能是從MFC的固有模塊中WinMain獲得執(zhí)行權(quán)。很重要的一點(diǎn),只有執(zhí)行的線程才可以創(chuàng)建對象。  回復(fù)  更多評論
              

            # re: 【原創(chuàng)】MFC程序設(shè)計(jì)之來龍去脈 2008-11-02 18:41 snowzjy
            這段代碼的意思是,獲得了CMyApp的全局對象的this指針。(此時(shí)你肯定要疑問,為什么是CMyApp的指針?this目前是在CWinApp中啊? 對此我的答案是,可是你是由CMyApp的對象引發(fā)的CWinApp的構(gòu)造啊!!)

            這句話有問題,能不能解釋清楚一點(diǎn)  回復(fù)  更多評論
              

            99久久人人爽亚洲精品美女| 久久久久一级精品亚洲国产成人综合AV区 | www久久久天天com| 中文字幕久久欲求不满| 亚州日韩精品专区久久久| 久久精品国产亚洲77777| 99久久精品免费看国产免费| 狠狠色丁香久久婷婷综合蜜芽五月| 久久青青草原亚洲av无码app | 国产精品久久永久免费| 久久久中文字幕日本| 国产综合久久久久久鬼色| 久久国产精品免费一区| 久久久免费精品re6| 久久笫一福利免费导航 | 人人狠狠综合久久亚洲| 久久久久亚洲av无码专区| 亚洲国产精品无码久久青草| 精品亚洲综合久久中文字幕| 囯产极品美女高潮无套久久久 | 久久久久亚洲AV无码观看| 热久久这里只有精品| 亚洲αv久久久噜噜噜噜噜| 久久本道久久综合伊人| 久久线看观看精品香蕉国产| 人妻无码αv中文字幕久久琪琪布 人妻无码久久一区二区三区免费 人妻无码中文久久久久专区 | 久久激情亚洲精品无码?V| 国内精品伊人久久久久| 久久久久亚洲精品天堂| 久久综合色老色| 伊色综合久久之综合久久| 久久无码国产| 久久人人爽人人精品视频| 久久精品亚洲福利| 精品久久久久国产免费| 国产精品VIDEOSSEX久久发布| 久久国产精品国产自线拍免费| 久久香蕉超碰97国产精品| 久久99国产精品尤物| 久久久久亚洲AV片无码下载蜜桃| 新狼窝色AV性久久久久久|