青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

何苦做程序?!

業精于勤,荒于嬉;行成于思,毀于隨! I believe , I can flying! 勿在浮砂筑高臺!

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

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

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

     _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPTSTR lpCmdLine, int nCmdShow)
 {
 // call shared/exported WinMain
 return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
 }
 
這里_tWinMain是為了支持UNICODE而命名的一個宏,真正起作用的是AfxWinMain,注意看看它的參數,是不是和SDK的WinMain函數一樣?
 現在再搜索下AfxWinMain,其實在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();
... ...
}
 此段代碼注意五個細節:
 CWinApp* pApp = AfxGetApp();
 意為獲得對象指針,其實就是剛才那個THIS。不記得了?指向CMyApp的那個!還值得注意的是,Afx意是全局的,隨時你都可以調用它。(AFX就是MFC開發小組的開發代號,意為Application Framework 傳說X只是為了好看,沒實在意思?!)
 if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
 AfxWinInit完成了線程的初始化和窗框類的注冊。具體參看appinit.cpp中的定義。
 if (pApp != NULL && !pApp->InitApplication())
 其實pApp和pThread是同一個指針,都是指向CMyApp的指針,這里因為CMyApp中沒有定義InitApplication,實際上就調用的CWinApp::InitApplication(),完成了MFC的內容管理。
 if (!pThread->InitInstance())
 因為CMyApp中改寫了它,所以調用CMyApp中的,其實它也是初始化工作。此時也完成了默認窗口類的定義。假如你熟悉SDK編程的話,一定不會忘記窗口類的設計、注冊、創建、現實及更新的步驟,此時MFC以為你設計好了默認的窗口類。
 現在你不禁要疑問,InitApplication()和InitInstance()有何不同?
 答案是,假如你執行一個程序,于是兩個函數都會被調用;當你在不關閉前一個程序的前提下,再執行一個程序,那么就只執行后一個函數。
 nReturnCode = pThread->Run();
 這個一步驟在《深入淺出MFC》中被成為程序的活水源頭,在我看來它就是你開車踩油門的步驟。待會我們會具體闡述!
 
 在設計窗口類以后,就應該是注冊,MFC自動調用(跳轉到)AfxEndDeferRegisterClass(WINCORE.CPP中),為你注冊了五個窗口類,分別是:AfxWnd,AfxCreateBar,AfxMDIFrame,AfxFrameOrView,AfxOleControl以上窗口類MFC將自動轉化成獨立無二的類名,供其調用。
 在窗口的注冊以后,就應該是窗口的創建工作,此時會調用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;
}
 
 其中完成了窗口的創建工作,里面還涉及擴展風格的調用CreateEx,具體細節請參看MSDN。
 
 此時你不禁要問,我們的事兒都讓MFC做完了?工業化生產出來的窗口都是千篇一律啊,我要有我自己的風格!
 別急,MFC給用戶提供了一個修改窗口設計的機會那就是:PreCreateWindow(CREATESTRUCT& cs) 你在MSDN中查詢一下CREATESTRUCT這個結構體,你會發現它和我們的CreateWindow幾乎是一模一樣,這個就是MFC留給你修改窗口的一個機會。在PreCreateWindow時,會跳到CWnd::PreCreateWindow,里面有一個宏:AfxDeferRegisterClass,它的作用是:如果該窗口類沒有被注冊,那么就注冊它;如果注冊了,就什么也不管!
 窗口類的設計、注冊、創建都已經完成,現在只剩下更新和顯示了。這些工作都交由 CMyApp::InitInstance()完成:
  m_pMainWnd->ShowWindow(SW_SHOW);
  m_pMainWnd->UpdateWindow();
 現在if (!pThread->InitInstance())的工作已經完成,按照MAIN函數的內容,接下來該:nReturnCode = pThread->Run()了
 此時應該調用CMyApp的Run()函數,但是在CMyApp類中,根本沒有聲明或定義這樣一個函數,根據多態性的原來,指針遷升,指向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();
 }
 
 最后你會發現,它由調用了一個CWinThread::Run(),此時你就看不到CWinThread::Run()的代碼了(至少筆者沒有找到,因為微軟只提供了部分MFC代碼。)但是你可以在MSDN中找到CWinThread::Run()的描述:
 Run 控制線程的函數。包含消息泵。一般不重寫。
 再具體點就是:
 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.
 原來它把消息循環包裝了一下,在MFC中稱為消息映射(message map)的東西!至于消息映射的具體細節本人會另寫文章說明!
 OK,MFC不再神秘,掌握了它的來龍去脈,再看其他的MFC書籍的時候,就知道我該怎么做?為什么我要這樣做?起到了知其然又知其所以然的效果,這就是我所追求的技術境界。
 
 

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

評論

# re: 【原創】MFC程序設計之來龍去脈 2006-02-23 22:18 一兩清風
在VC6中,可以使用step by step方式的DEBUG來逐步調試剛建立的MFC程序,觀察IDE窗口標題就會發現被隱藏源碼所在文件的位置,觀察DEBUG窗口的箭頭就會發現MFC程序的執行順序......。  回復  更多評論
  

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

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

這句話有問題,能不能解釋清楚一點  回復  更多評論
  

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美丝袜第一区| 免费日韩视频| 亚洲高清在线观看| 亚洲欧美日韩综合一区| 亚洲一区二区不卡免费| 亚洲男女毛片无遮挡| 午夜精品久久久久久久| 欧美在线一区二区| 久久手机精品视频| 亚洲国产国产亚洲一二三| 日韩视频免费观看高清在线视频| 一区二区三区成人| 羞羞色国产精品| 女生裸体视频一区二区三区| 欧美日韩国产影片| 国产日韩亚洲欧美综合| 在线日韩中文字幕| 在线午夜精品| 久久午夜精品一区二区| 亚洲精品国产精品国自产观看浪潮 | 91久久精品一区二区三区| 亚洲人体偷拍| 午夜亚洲性色视频| 欧美成人国产| 国产日韩欧美一区二区| 亚洲九九爱视频| 久久爱91午夜羞羞| 99re6热只有精品免费观看| 欧美在线观看视频一区二区三区| 欧美激情综合五月色丁香| 国产亚洲欧美一区| 亚洲午夜伦理| 亚洲国产另类久久久精品极度| 亚洲欧美精品一区| 欧美日韩在线一区二区| 亚洲欧洲一区二区三区在线观看 | 久久久久久久精| 一本一本久久| 欧美成人综合网站| 亚洲成在线观看| 久久一区二区精品| 亚洲影视中文字幕| 国产精品二区三区四区| 亚洲精品在线一区二区| 欧美 日韩 国产一区二区在线视频| 亚洲综合色在线| 欧美午夜在线一二页| 99热精品在线观看| 亚洲国产欧美日韩| 欧美成人精品一区| 亚洲精品国产精品乱码不99按摩| 久久综合亚洲社区| 久久久精品免费视频| 国产一区亚洲一区| 久久精品在线播放| 久久久夜色精品亚洲| 伊大人香蕉综合8在线视| 久久先锋影音av| 久久久国产视频91| 永久免费视频成人| 欧美jizzhd精品欧美巨大免费| 久久成人羞羞网站| 在线成人激情黄色| 亚洲人成亚洲人成在线观看图片 | 91久久精品国产| 欧美一区永久视频免费观看| 欧美韩日亚洲| 一本色道久久99精品综合| 亚洲电影第三页| 欧美精品在线观看播放| 欧美激情a∨在线视频播放| 欧美一区二区三区免费观看 | 中文在线一区| 欧美日韩一区二区在线观看 | 午夜精品亚洲| 午夜视频一区| 在线欧美一区| 亚洲欧洲一区二区天堂久久| 欧美极品一区二区三区| 亚洲一区二区三区精品在线| 亚洲一区免费| 在线观看中文字幕不卡| 亚洲日韩成人| 国产精品亚洲激情| 欧美.www| 欧美性一二三区| 狼人社综合社区| 欧美全黄视频| 久久在线播放| 欧美视频1区| 久久综合色一综合色88| 欧美激情视频免费观看| 久久成人18免费观看| 免费成人av资源网| 小黄鸭精品密入口导航| 男人的天堂亚洲在线| 亚洲欧美日韩综合| 美女视频黄a大片欧美| 亚洲欧美激情视频在线观看一区二区三区| 午夜视频在线观看一区二区| 亚洲精品一区久久久久久| 亚洲欧美在线网| 一区二区高清在线| 久久久久久电影| 亚洲欧美清纯在线制服| 欧美成人中文| 免费视频一区| 国产精品尤物福利片在线观看| 欧美成人高清| 国产一区自拍视频| 亚洲一级黄色片| 日韩亚洲精品视频| 久久精品官网| 欧美一区视频在线| 国产精品r级在线| 亚洲国产日韩在线| 亚洲国产精品福利| 久久精品久久综合| 久久国产精品亚洲va麻豆| 欧美三级欧美一级| 最新国产の精品合集bt伙计| 亚洲第一黄色| 久久精品99国产精品酒店日本| 亚洲综合激情| 欧美日韩色一区| 亚洲精品美女免费| 亚洲伦伦在线| 美女网站在线免费欧美精品| 老**午夜毛片一区二区三区| 国产日韩欧美中文| 欧美亚洲在线| 久久久久久久成人| 国产精品激情偷乱一区二区∴| 久久精选视频| 久久久精品国产免大香伊| 国产精品久久毛片a| av成人动漫| 亚洲欧美在线x视频| 欧美三日本三级少妇三99 | 欧美一区二区三区免费视频| 校园春色国产精品| 国产日韩欧美在线播放| 性色av一区二区三区| 久久久噜噜噜久久中文字幕色伊伊 | 亚洲免费观看高清完整版在线观看| 亚洲人精品午夜| 欧美高清在线| 夜夜爽99久久国产综合精品女不卡 | 亚洲视频精选在线| 国产精品你懂的在线| 亚洲欧美视频在线观看| 老司机成人在线视频| 亚洲三级色网| 国产精品v日韩精品| 亚洲欧美偷拍卡通变态| 久久一区二区三区超碰国产精品| 在线免费观看日韩欧美| 欧美激情精品久久久久| 亚洲一区二区欧美日韩| 久久人91精品久久久久久不卡| 亚洲国产三级网| 欧美色欧美亚洲高清在线视频| 午夜精彩国产免费不卡不顿大片| 开心色5月久久精品| 一区二区三区不卡视频在线观看| 国产精品视频xxxx| 麻豆亚洲精品| 亚洲一区欧美| 亚洲福利视频一区二区| 亚洲欧美久久久久一区二区三区| 黄色日韩网站视频| 欧美日韩视频在线一区二区观看视频| 亚洲一区免费看| 亚洲国产99| 久久精品电影| aa级大片欧美| 在线日韩中文字幕| 国产精品毛片大码女人| 蜜臀av性久久久久蜜臀aⅴ| 亚洲一区二区在线免费观看视频 | 羞羞答答国产精品www一本| 亚洲第一黄色网| 久久九九精品99国产精品| 亚洲手机在线| 亚洲欧洲综合另类| 国产综合自拍| 国产精品你懂的在线| 欧美激情亚洲自拍| 久久人人97超碰精品888| 亚洲午夜在线观看| 亚洲精品一区二区三区蜜桃久| 美女黄色成人网| 欧美中文字幕在线| 亚洲一区二区在线| 校园激情久久| 欧美专区在线| 亚洲免费在线观看视频| 亚洲日韩欧美视频| 伊人久久大香线| 国产在线乱码一区二区三区|