• <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++博客 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
              4 Posts :: 1 Stories :: 14 Comments :: 0 Trackbacks

            2006年3月11日 #

             孫鑫VC講座筆記--WINDOWS程序內(nèi)部運(yùn)行原理

            聲明:

                    本人最近也在看孫老師的視頻,為了加強(qiáng)理解,所以想一些讀書(shū)筆記。但是在CSDN上一搜索,發(fā)現(xiàn)已經(jīng)有朋友做了相關(guān)筆記。根據(jù)面向?qū)ο蟮摹袄^承”觀點(diǎn),為了解決勞動(dòng)力,所以我打算在他們的基礎(chǔ)上添加、修改。應(yīng)該不涉及著作權(quán)什么的東東吧?!

                    我在BLOG.CSDN.NET/LEWISLAU上搜索了下 ,有兩位朋寫了相關(guān)筆記(而且都是一樣的)。不知道誰(shuí)才是原作者,所以列出兩位BLOG地址:

            http://blog.csdn.net/hhitjsj021                                http://blog.csdn.net/d007879

            以后我會(huì)在前輩的基礎(chǔ)上修改、發(fā)文!呵呵!繼承嘛!

             

             

             

             

            windows程序設(shè)計(jì)是種事件驅(qū)動(dòng)方式的程序設(shè)計(jì),主要基于消息的。當(dāng)用戶需要完成某種功能時(shí),需要調(diào)用OS某種支持,然后OS將用戶的需要包裝成消息,并投入到消息隊(duì)列中,最后應(yīng)用程序從消息隊(duì)列中取走消息并進(jìn)行響應(yīng)。

             

            MSG Structure

            --------------------------------------------------------------------------------

            The MSG structure contains message information from a thread's message queue.

            Syntax

            typedef struct {
                HWND hwnd;   //指示一個(gè)窗口的句柄,改消息和那個(gè)窗口相關(guān)聯(lián)。
                UINT message;  //具體的消息,用無(wú)符號(hào)整形表示
                WPARAM wParam; //關(guān)于消息的附加參數(shù)
                LPARAM lParam; //同上
                DWORD time; //32位整數(shù),表示消息被投遞出去的時(shí)間
                POINT pt; //表示光標(biāo)位置
            } MSG, *PMSG;

             句柄,資源的標(biāo)識(shí),操作系統(tǒng)通過(guò)句柄指到資源。常見(jiàn)的句柄有圖標(biāo)句柄(HICON),光標(biāo)句柄(HCURSOR),窗口句柄(HWND),應(yīng)用程序句柄(HINSTANCE)
             
            例如:當(dāng)按下按鍵會(huì)發(fā)送出WM_CHAR消息   通過(guò)消息的附加參數(shù),保存對(duì)應(yīng)的ASCII碼,即可知道按下的是那個(gè)鍵。

             

             


            消息隊(duì)列:
            每個(gè)應(yīng)用程序OS都為它建立一個(gè)消息隊(duì)列,消息隊(duì)列是個(gè)先進(jìn)先出的緩沖區(qū),其中每個(gè)元素都是一個(gè)消息,OS將生成的每個(gè)消息按先后順序放進(jìn)消息隊(duì)列中,應(yīng)用程序總是取走當(dāng)前消息隊(duì)列中的第一條消息,應(yīng)用程序取走消息后便知道用戶的操作和程序的狀態(tài),然后對(duì)其處理即消息響應(yīng),消息響應(yīng)通過(guò)編碼實(shí)現(xiàn)。

            使用VC編程除了良好的C基礎(chǔ)外還需要掌握兩方面:
            一,消息本身。不同消息所代表的用戶操作和應(yīng)用程序的狀態(tài)。
            二,對(duì)于某個(gè)特定的消息來(lái)說(shuō),要讓OS執(zhí)行某個(gè)特定的功能去響應(yīng)消息。


            Window程序入口:
            int WINAPI WinMain(
              HINSTANCE hInstance,  // 當(dāng)前事例句柄。
              HINSTANCE hPrevInstance,  // 先前事例句柄。
              LPSTR lpCmdLine,      // 命令行指針
              int nCmdShow          // (窗口)顯示的狀態(tài)
            );
            說(shuō)明:WinMain函數(shù)是Windows程序入口點(diǎn)函數(shù),由OS調(diào)用,當(dāng)OS啟動(dòng)應(yīng)用程序的時(shí)候,winmain函數(shù)的參數(shù)由OS傳遞的。

             

            創(chuàng)建一個(gè)完整的窗口需要經(jīng)過(guò)下面四個(gè)操作步驟:
            一,設(shè)計(jì)一個(gè)窗口類;如:WNDCLASS wndcls;
            二,注冊(cè)窗口類;    如:RegisterClass(&wndcls);
            三,創(chuàng)建窗口;      如:CreateWindow(),CreateWindowEX();
            四,顯示及更新窗口。如:ShowWindow(),UpdateWindow();
            說(shuō)明:創(chuàng)建窗口的時(shí)候一定要基于已經(jīng)注冊(cè)的窗口類.

             

             

            Windows提供的窗口類:
            typedef struct  WNDCLASS {
                UINT    style;        //窗口的類型
                WNDPROC lpfnWndProc;  //窗口過(guò)程函數(shù)指針(回調(diào)函數(shù))
                int     cbClsExtra; //窗口類附加字節(jié),為該類窗口所共享。通常0。
                int     cbWndExtra; //窗口附加字節(jié)。通常設(shè)為0。
                HANDLE  hInstance;  //當(dāng)前應(yīng)用程序事例句柄。
                HICON   hIcon;      //圖標(biāo)句柄 LoadIcon();
                HCURSOR hCursor;    //光標(biāo)句柄 LoadCursor();
                HBRUSH  hbrBackground; //畫(huà)刷句柄 (HBRUSH)GetStockObject();
                LPCTSTR lpszMenuName;  //菜單名字
                LPCTSTR lpszClassName; //類的名字
            } WNDCLASS,*PWNDCLASS;


            窗口類型style為一個(gè)變量,該變量每一位對(duì)應(yīng)著一種特性。對(duì)應(yīng)為1時(shí),有該種特性;對(duì)應(yīng)為0時(shí),無(wú)該種特性。為了方便記憶,用一些宏對(duì)應(yīng)一些特征,通過(guò)取反(~)和相與(&)可以取消一些特性。  通常設(shè)置為"CS_HREDRAW | CS_VREDRAW"表示垂直重繪和水平重繪。

            HICON可以由LoadIcon 賦值(它有兩個(gè)參數(shù)HINSTANCE和LPCTSTR,通常第一個(gè)參數(shù)為空,只對(duì)第二個(gè)參數(shù)賦值,即圖標(biāo)的ID)
            HCURSOR同HICON
            HBRUSH 使用GetStockObject函數(shù),它可以用來(lái)獲取筆、畫(huà)刷、字符、調(diào)試板的畫(huà)刷。使用時(shí)要用HBRUSH做一直強(qiáng)制轉(zhuǎn)化。因?yàn)镚etStockObject返回值和HBRUSH不同。

            窗口類注冊(cè):
            ATOM RegisterClass(
              CONST WNDCLASS *lpWndClass   // address of structure with class
                                          // data
            );
            //注意,是使用地址符

             


            創(chuàng)建窗口:
            HWND CreateWindow(
              LPCTSTR lpClassName,  //注冊(cè)窗口類名,用引號(hào)
              LPCTSTR lpWindowName, //窗口標(biāo)題,用引號(hào)
              DWORD dwStyle,        //窗口類型(風(fēng)格)通常為(WS_OVERLAPPEDWINDOW)
              int x,                // 窗口X坐標(biāo)
              int y,                // 窗口X坐標(biāo)
              int nWidth,           // 寬度
              int nHeight,          // 高度
              HWND hWndParent,      // 指向父窗口的句柄
              HMENU hMenu,          // 菜單句柄
              HANDLE hInstance,     // 當(dāng)前實(shí)例的句柄,由WINMAIN傳遞
              LPVOID lpParam        // WM_CREATE附加參數(shù)傳入指針
            );
            創(chuàng)建窗口的時(shí)候會(huì)發(fā)送WM_CREATE消息


            顯示和更新窗口窗口:
            BOOL ShowWindow(
              HWND hWnd,     // handle to window
              int nCmdShow   // show state of window
            );
            BOOL UpdateWindow(
              HWND hWnd   // handle of window  送出WM_PAINT消息
            );


            消息循環(huán)
            MSG msg;
            while(GetMessage(&msg,...))    //從消息隊(duì)列中取出一條消息
            {
             TranslateMessage(&msg); //進(jìn)行消息(如鍵盤消息)轉(zhuǎn)化。轉(zhuǎn)化過(guò)程不會(huì)影響原消息,只會(huì)創(chuàng)建新的消息。
             DispatchMessage(&msg); //分派消息到窗口的回調(diào)函數(shù)處理,(OS調(diào)用窗口回調(diào)函數(shù)進(jìn)行處理)。
            }

            BOOL GetMessage(
              LPMSG lpMsg,         // 消息結(jié)構(gòu)體變量
              HWND hWnd,           // 句柄,那個(gè)一個(gè)窗口?為NULL則為所有窗口句柄
              UINT wMsgFilterMin,  // 最小消息值,為0時(shí)返回所有消息
              UINT wMsgFilterMax   // 最大消息值
            );

             

            回調(diào)原理:當(dāng)應(yīng)用程序受到給某個(gè)窗口的消息時(shí),就應(yīng)調(diào)用某一函數(shù)來(lái)處理這條消息。這一消息有操作系統(tǒng)自動(dòng)完成。

            注:函數(shù)名可以用以表示函數(shù)代碼的首地址(函數(shù)指針),額外數(shù)據(jù)通常為0。


            窗口過(guò)程函數(shù)(回調(diào)函數(shù))原型:
            LRESULT CALLBACK WindowProc(  //這里WindowProc是個(gè)代號(hào)名字。
              HWND hwnd,      // handle to window
              UINT uMsg,      // message identifier
              WPARAM wParam,  // first message parameter
              LPARAM lParam   // second message parameter
            );
            說(shuō)明:兩種函數(shù)調(diào)用約定(__stdcall 和 __cdecl):
            #define CALLBACK    __stdcall
            //__stdcall 標(biāo)準(zhǔn)調(diào)用預(yù)定,是PASCAL 調(diào)用約定,象DELPHI使用的就是標(biāo)準(zhǔn)調(diào)用約定
            #define WINAPIV     __cdecl 
            // __cdecl 是C 語(yǔ)言形式的調(diào)用約定。
            主要區(qū)別:函數(shù)參數(shù)傳遞順序 和 對(duì)堆棧的清除上。
            問(wèn)題:除了那些可變參數(shù)的函數(shù)調(diào)用外,其余的一般都是__stdcall約定。但 C/C++編譯默然的是__cdecl約定。所以如果在VC等環(huán)境中調(diào)用__stdcall約定的函數(shù),必須要在函數(shù)聲明的時(shí)加上 __stdcall 修飾符,以便對(duì)這個(gè)函數(shù)的調(diào)用是使用__stdcall約定(如使用DELPHI編寫的DLL時(shí)候)。
            (VC中可通過(guò)這途徑修改:project|settings..|c/c++|...)
            在窗口過(guò)程函數(shù)中通過(guò)一組switch語(yǔ)句來(lái)對(duì)消息進(jìn)行處理:
            如:
            LRESULT CALLBACK WindowProc( 
              HWND hwnd,
              UINT uMsg,
              WPARAM wParam,
              LPARAM lParam  
            )
            {
                switch(uMsg)
                {
             case WM_PAINT:
              ...
              break;
             case ...
              break;
             case WM_CLOSE:
              //DestroyWindow(hwnd);
               //銷毀窗口,并發(fā)送WM_DESTROY消息。
              break;
             case WM_DESTROY:
              //PostQuitMessage(0);
              //發(fā)送WM_QUIT消息到消息隊(duì)列中,請(qǐng)求終止。
                     //GetMessage()取到WM_QUIT消息后,返回0,退出消息循                //   環(huán),從而終止應(yīng)用程序。
              break;
             default:
              return DefWindowProc(hwnd,uMsg,wParam,lParam);
             //用缺省的窗口過(guò)程處理我們不感興趣的消息(其它消息)。
             //這是必須的。
                }//switch
             return 0;
            }//WindowProc


             響應(yīng)WM_DESTROY,調(diào)用PostQuitMessage(int)結(jié)束進(jìn)程。它會(huì)投遞一個(gè)WM_QUIT消息對(duì)消息隊(duì)列中。當(dāng)消息循環(huán)的GetMessage取到WM_QUIT消息,則返回0,程序結(jié)束。
             另外對(duì)于不感興趣的消息要景象缺省的處理,使用DefWindowProc()內(nèi)為窗口的參數(shù)。

             


            關(guān)于DC句柄獲取:
            a)使用BeginPaint(),EndPaint()對(duì)。注意只能在響應(yīng)WM_PAINT消息時(shí)使用。
            b)使用GetDc(),ReleaseDC()對(duì)。注意他們不能在響應(yīng)WM_PAINT中使用

            posted @ 2006-03-11 11:19 lewislau 阿木 閱讀(2683) | 評(píng)論 (1)編輯 收藏

             【原創(chuàng)】我在成都當(dāng)程序員

            題記:前幾天出去感受一下,感觸頗深,發(fā)覺(jué)自己真的很遜。現(xiàn)在回到清靜的校園,自己覺(jué)得應(yīng)該更加努力。社會(huì)真的不是我們想的那樣,工作也不像學(xué)習(xí)一樣輕松。只希望各位朋友在走出校門,邁向社會(huì)的過(guò)程中,一切順利!

             在成都待了一個(gè)星期,確切的說(shuō)只有六天。在這六天里面,我完完全全是一個(gè)打工的,一個(gè)社會(huì)上的人,一個(gè)程序員。聽(tīng)上去多牛B,程序員啊,這個(gè)可是我夢(mèng)寐以求的職業(yè)啊,可是事實(shí)上一切都不是我想的那樣輝煌。我這幾天腦海里反復(fù)浮現(xiàn)一句話,程序員的的確確是十分辛苦的職業(yè)。
             我所在的公司是一家游戲外掛公司,專門做游戲外掛。從社會(huì)的角度來(lái)說(shuō),這種“勾搭”只能暗箱操作,如今卻還明目張膽的“開(kāi)張營(yíng)業(yè)”,確實(shí)搞笑;從技術(shù)的角度來(lái)說(shuō),游戲外掛,利用HOOK攔截?cái)?shù)據(jù)包、解密、修改、封包,樣樣都是安全方面的比較牛B的技術(shù),在沒(méi)有進(jìn)這個(gè)公司之前,我沒(méi)有想過(guò)一年以內(nèi)會(huì)接觸到這些底層的技術(shù)。所有說(shuō)公司對(duì)我來(lái)說(shuō),有些失望、但是也有一些期望。
             以前在綿陽(yáng)(家鄉(xiāng))的一些小IT公司也玩兒過(guò),但是這次是獨(dú)自在外,什么問(wèn)題都要自己解決,所有從各個(gè)方面來(lái)說(shuō),都是自己的一些新的嘗試。不過(guò)還好的是,這次我是和朋友一切去成都“實(shí)習(xí)”。呵呵,不過(guò)我比他好的就是,他在電信設(shè)計(jì)規(guī)劃院實(shí)習(xí)三個(gè)月,沒(méi)有實(shí)習(xí)工資,三個(gè)月轉(zhuǎn)正;而我實(shí)習(xí)一個(gè)月實(shí)習(xí)工資一千,一個(gè)月后轉(zhuǎn)正,根據(jù)情況而定工資,表現(xiàn)的好的話在3K左右(聽(tīng)上去挺吸引人的,但是真的很累)。
             我們到成都只用了2個(gè)小時(shí)找了一處住房,合租,350一月,只有一張床和一個(gè)書(shū)桌(天啦,成都的房?jī)r(jià)真TMD的嚇?biāo)廊耍Nㄒ槐容^欣慰的是我們住在川大外面,步行到川大只要5分鐘,原先設(shè)想的是每天下班去川大教室去上自習(xí),可是現(xiàn)實(shí)往往與設(shè)想有很大的差距,每天回到家累的簡(jiǎn)直書(shū)都不想看。(白天在公司至少看6個(gè)小時(shí)以上的文檔,對(duì)這那個(gè)15寸的CRT,我簡(jiǎn)直萬(wàn)分的不爽,公司里面50多人就我一個(gè)用CRT顯示器,我靠,欺負(fù)我??!)每晚和朋友在川大轉(zhuǎn)悠兩圈就回去睡覺(jué),這種生活,讓我回憶起兩歲的時(shí)候家里還不景氣的狀態(tài)。汗!
             言歸正轉(zhuǎn),還是說(shuō)工作的事兒,每天我7:40起床,解決完所有問(wèn)題大約8:20到達(dá)車站,搭上19路,順利的話8:55作用能到達(dá)高升橋(公司所在地),唉,不禁汗顏,每天要白白浪費(fèi)大約90分鐘時(shí)間搭車,在成都人看來(lái),這還算近的(大城市的是比我們這些農(nóng)民洋盤)。9點(diǎn)左右到達(dá)公司,不出意外的話只有我旁邊的仁兄比我早到。(公司老大考慮技術(shù)部經(jīng)常加班,所以允許技術(shù)部的兄弟們9:30上班,這個(gè)還夠人性化。)這位仁兄待會(huì)要重點(diǎn)介紹,公司里面就跟他混的最熟,特牛B。說(shuō)到加班,我突然想起才去公司和技術(shù)部老大談的時(shí)候,老大特別交代,“如果你加班的話,晚上可以打車回家。拿上小票,第二天回公司報(bào)銷。”我汗,原來(lái)這個(gè)公司加班就跟喝水一樣隨便。
             我才到公司,我的工作就是不停的看文檔,中文的、英文的都看。(我都懷疑我居然能看懂英文)老大給我的任務(wù)很簡(jiǎn)單,就一句話:“利用HOOK抓取程序的數(shù)據(jù)包。”他還特別交代,“不需要你解密數(shù)據(jù)包,抓取就可以”看上去降低了很大難度哦,但是有兩個(gè)人能證明這個(gè)我這個(gè)公司不是我現(xiàn)在的水平能夠接受的。第一個(gè)馮SIR,他說(shuō)這個(gè)東西對(duì)于我來(lái)說(shuō),非常非常具有挑戰(zhàn),希望我能創(chuàng)造奇跡。(汗,奇跡!)第二個(gè)是李SIR,回學(xué)校后我給他講述我的“作業(yè)”,他說(shuō),哦,這個(gè)東西和我?guī)熜盅芯可漠厴I(yè)設(shè)計(jì)很像。(我再一次汗顏)我承認(rèn)自己的喜歡有挑戰(zhàn)的工作,但是我還是很有自知之明,確切的說(shuō)我很自量力。
             我每天的工作就是看文檔,看的我要瘋,HOOK屬于WINDOWS的核心技術(shù),趨于操作系統(tǒng)底層,用于攔截操作系統(tǒng)發(fā)出的消息。對(duì)于我來(lái)說(shuō)決不是簡(jiǎn)單的事兒,所以不敢懈怠,我每天就GOOGLE,MSDN上搜尋關(guān)于它的一切,中文的英文的都看,要瘋!看看我旁邊的家伙,現(xiàn)在就來(lái)說(shuō)說(shuō)他,他是測(cè)試部的,他的工作就是不停的耍各種各樣的游戲(居然耍游戲真的能找到工作,我簡(jiǎn)直為我們學(xué)校的兄弟們感到欣慰),有的時(shí)候網(wǎng)速比較卡,他沒(méi)法耍游戲,就只能委屈一下看小說(shuō),這樣的工作讓我羨慕的不行。偶爾看到他再不斷的抄錄房產(chǎn)信息,就問(wèn)他是否要買房。他說(shuō),恩。我就納悶了,看上去年紀(jì)輕輕的就想房子,公司耍游戲的難不成拿8K一個(gè)月?繼續(xù)問(wèn)到,“你工作幾年了哦?都有錢買房了?”“我工作了都五年了,我一個(gè)月1500,一年存不到5000。靠自己的話都不知道什么時(shí)候才能買哦、。其實(shí)是我父母給錢。”終于發(fā)現(xiàn)一個(gè)比我還強(qiáng)的人了。事后,我給我爸媽打電話匯報(bào)我的近況,閑聊到此事兒,只聽(tīng)電話那邊一片寂靜,我爸耍句“你給我說(shuō)這些,難不成你還要我們給你準(zhǔn)備錢買房子?”(上帝作證,我絕對(duì)沒(méi)那么想)。
             每天吃午飯大約半小時(shí)不面對(duì)那可惡的CRT,其余時(shí)間就逃不過(guò)浩劫,現(xiàn)在用到筆記本是爽哦,至少不是CRT的。公司里面基本上都要玩兒游戲,還有人被盜過(guò)帳號(hào)。(我們都是干地下生意的,還被人家黑吃黑?!)我就想,寫個(gè)木馬對(duì)于我們公司的幾個(gè)高手來(lái)說(shuō)絕對(duì)是小菜一碟,但是自己的帳號(hào)被盜了還是沒(méi)辦法,說(shuō)不定就是自己的木馬盜的自己的帳號(hào)。
             公司還有一個(gè)女程序員,佩服啊。今天也是三八婦女節(jié),向牛B的女程序員致敬!
             每天下午六點(diǎn),其實(shí)那個(gè)時(shí)候我都可以離開(kāi)了,但是技術(shù)部所有同僚加班(除了我,因?yàn)槲沂切聛?lái)的,還做不了事兒。)所以我也不好走的太快,慢慢騰騰的磨到六點(diǎn)一刻才離開(kāi)公司,回家咯。
             上面所有行文,也許你還沒(méi)感覺(jué)到辛苦,因?yàn)橛行┘?xì)節(jié)的事兒再說(shuō)說(shuō):
             我每天上班搭乘公車,大約一共要站90分鐘,絕對(duì)有少無(wú)多。上班還好,人并不多;我大概描述下我下班的情況,我第一天下班等了兩輛19路后換成77路,結(jié)果多步行了四十分鐘的樣子,到家的時(shí)候都快20:30了,還沒(méi)吃飯。后來(lái)我搭19路,大概情況是,本來(lái)是前門透幣上車的,我只能從后門上,上車后貼在車門,途中路過(guò)要下車的我還要從后門下來(lái)讓別人下車。我靠,這下感覺(jué)到毛大爺真不對(duì),啥子人多力量大,瞎扯!
             回到住的地方,吃過(guò)飯的時(shí)間是最難打發(fā)的。以前在家熬夜,也不可能睡的太早,大概每天就十一點(diǎn)的樣子睡,期間的時(shí)間就不知道如何處理?!一切在家時(shí)候就玩兒電腦,現(xiàn)在連個(gè)電視都看不到,感覺(jué)自己都與外界隔絕了一樣。每天就到川大的散散步步,然后回住的地方躺在床上,發(fā)幾條短信,打幾個(gè)電話,便早早的睡去。這個(gè)是讓我精神上最難以忍受的地方。我靠,我是IT人哦?信息時(shí)代哦?!這到底是什么見(jiàn)鬼的生活,我最受不了就是這一點(diǎn)。不過(guò)還好,我是和朋友一起出來(lái)的,晚上還可以閑聊幾句,不過(guò)想想以后要是一個(gè)人出門在外,我估計(jì)自己早晚要瘋。
             晚上,書(shū)也不想看,要是以后還是這個(gè)樣子,我估計(jì)這輩子就只能當(dāng)一輩子的垃圾程序員了。
             有的時(shí)候我想,程序員的確非常辛苦,自己選擇這條道路對(duì)不對(duì)。后來(lái)想了想,努力做好每一件事兒,總有一天我相信自己的能力得到鍛煉以后,我相信態(tài)度決定一切。
             唉,現(xiàn)在回到學(xué)校了,真的很多感悟,大家還是一起努力吧!
             現(xiàn)在感覺(jué)做程序真的很累,而且付出和收入顯然不是成正比的,現(xiàn)在有點(diǎn)上了賊船又無(wú)法回頭,仔細(xì)想想又有點(diǎn)相追求庫(kù)克船長(zhǎng)那樣傳奇的人生,矛盾!

            posted @ 2006-03-11 11:18 lewislau 阿木 閱讀(976) | 評(píng)論 (8)編輯 收藏

            2006年2月18日 #

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

             CMyApp theApp;   //本人建立的工程名為My。
             
             
             是不是很特別,再注意一下那句注釋“The one and only CMyApp object”,每個(gè)應(yīng)用程序有且只用一個(gè)CMyApp對(duì)象。我想你應(yīng)該想到了,WinMain函數(shù)每個(gè)程序也只能有一個(gè),那么這個(gè)全局對(duì)象跟WinMain函數(shù)肯定有莫大的關(guān)系?沒(méi)錯(cuò),相信你的直覺(jué)。
             特別注意:深曉C++細(xì)節(jié)的人一定知道,全局對(duì)象優(yōu)先于MAIN函數(shù)執(zhí)行的道理。如果你不知道也沒(méi)關(guān)系,那么我在這里告訴你:“全局對(duì)象優(yōu)先于MIAN函數(shù)執(zhí)行,且構(gòu)建于棧中,切記,切記!”
             現(xiàn)在,我們?cè)撋钊隬inMain運(yùn)行機(jī)制了,確切的說(shuō),應(yīng)該是MFC的機(jī)制!
             首先,看看MFC的庫(kù)文件把,它能給我們帶來(lái)許多驚喜。(vc6的相應(yīng)的目錄是\Microsoft Visual Studio\VC98\MFC\SRC;VC7相應(yīng)的目錄是\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc)
             現(xiàn)在我們就從這個(gè)全局下手,開(kāi)始今天的旅途。
             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í)打開(kāi)APPCORE.CPP,以下使用相同搜索方式,不再?gòu)?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)在請(qǐng)注意:
                    pThreadState->m_pCurrentWinThread = this;
             pModuleState->m_pCurrentWinApp = this;
                 這兩行代碼其實(shí)都是做的一件事兒。
                 這段代碼的意思是,獲得了CMyApp的全局對(duì)象的this指針。(此時(shí)你肯定要疑問(wèn),為什么是CMyApp的指針?this目前是在CWinApp中啊?   對(duì)此我的答案是,可是你是由CMyApp的對(duì)象引發(fā)的CWinApp的構(gòu)造啊!!)這個(gè)指針可非一般的人物,稍后我們的很多工作都要靠它完成。
                 CWinApp之中的成員變量將因?yàn)閠heApp這個(gè)全局對(duì)象的誕生而獲得配置和初始值。
              構(gòu)造完父類,現(xiàn)在構(gòu)造子類。可是我們看到,AppWizard給我們的子類里它什么也沒(méi)做?是的,這一切都聽(tīng)從你的安排!
                CMyApp::CMyApp()
                {
             // TODO: add construction code here,
             // Place all significant initialization in InitInstance
             }
             
             
             
              接下來(lái)就是今天的主角兒了,搜索關(guān)鍵字“WinMain”,出現(xiàn)很多文件。別急,因?yàn)楝F(xiàn)在我們應(yīng)該先看看WinMain的聲明。打開(kāi)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();
             意為獲得對(duì)象指針,其實(shí)就是剛才那個(gè)THIS。不記得了?指向CMyApp的那個(gè)!還值得注意的是,Afx意是全局的,隨時(shí)你都可以調(diào)用它。(AFX就是MFC開(kāi)發(fā)小組的開(kāi)發(fā)代號(hào),意為Application Framework 傳說(shuō)X只是為了好看,沒(méi)實(shí)在意思?!)
             if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
             AfxWinInit完成了線程的初始化和窗框類的注冊(cè)。具體參看appinit.cpp中的定義。
             if (pApp != NULL && !pApp->InitApplication())
             其實(shí)pApp和pThread是同一個(gè)指針,都是指向CMyApp的指針,這里因?yàn)镃MyApp中沒(méi)有定義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ì)、注冊(cè)、創(chuàng)建、現(xiàn)實(shí)及更新的步驟,此時(shí)MFC以為你設(shè)計(jì)好了默認(rèn)的窗口類。
             現(xiàn)在你不禁要疑問(wè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》中被成為程序的活水源頭,在我看來(lái)它就是你開(kāi)車踩油門的步驟。待會(huì)我們會(huì)具體闡述!
             
             在設(shè)計(jì)窗口類以后,就應(yīng)該是注冊(cè),MFC自動(dòng)調(diào)用(跳轉(zhuǎn)到)AfxEndDeferRegisterClass(WINCORE.CPP中),為你注冊(cè)了五個(gè)窗口類,分別是:AfxWnd,AfxCreateBar,AfxMDIFrame,AfxFrameOrView,AfxOleControl以上窗口類MFC將自動(dòng)轉(zhuǎn)化成獨(dú)立無(wú)二的類名,供其調(diào)用。
             在窗口的注冊(cè)以后,就應(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é)請(qǐng)參看MSDN。
             
             此時(shí)你不禁要問(wèn),我們的事兒都讓MFC做完了?工業(yè)化生產(chǎn)出來(lái)的窗口都是千篇一律啊,我要有我自己的風(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,它的作用是:如果該窗口類沒(méi)有被注冊(cè),那么就注冊(cè)它;如果注冊(cè)了,就什么也不管!
             窗口類的設(shè)計(jì)、注冊(cè)、創(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)容,接下來(lái)該:nReturnCode = pThread->Run()了
             此時(shí)應(yīng)該調(diào)用CMyApp的Run()函數(shù),但是在CMyApp類中,根本沒(méi)有聲明或定義這樣一個(gè)函數(shù),根據(jù)多態(tài)性的原來(lá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()的代碼了(至少筆者沒(méi)有找到,因?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.
             原來(lái)它把消息循環(huán)包裝了一下,在MFC中稱為消息映射(message map)的東西!至于消息映射的具體細(xì)節(jié)本人會(huì)另寫文章說(shuō)明!
             OK,MFC不再神秘,掌握了它的來(lái)龍去脈,再看其他的MFC書(shū)籍的時(shí)候,就知道我該怎么做?為什么我要這樣做?起到了知其然又知其所以然的效果,這就是我所追求的技術(shù)境界。
             
             

            posted @ 2006-02-18 12:04 lewislau 阿木 閱讀(4037) | 評(píng)論 (3)編輯 收藏

            2006年2月1日 #

            修煉之道
                首先聲明我不是一個(gè)“人才”,我是一個(gè)垃圾,不過(guò)我還在苦心修煉成為所謂的“人才”!在我“修煉”過(guò)程中頓有所悟,寫些隨筆,隨便涂鴉之筆。
                但凡世間萬(wàn)物,無(wú)一能夠逃脫一個(gè)“道”字!我不是道教的FANS,但是我還是對(duì)其所謂的“道”,存有敬意!可惜小生修行太淺,還說(shuō)不出一個(gè)所以然出來(lái)。可是,每每回想以前自己走過(guò)的點(diǎn)點(diǎn)滴滴,若有所悟?!
                每次看書(shū)學(xué)習(xí)技術(shù)的時(shí)候,總覺(jué)得少了些什么?!
                從現(xiàn)在開(kāi)始我應(yīng)該每天問(wèn)自己:“技術(shù)進(jìn)步了嗎?!思想呢?文化呢?”
                技術(shù),是大家都非常想追求的。思想是可以交流的。文化呢?不要說(shuō)不知道文化為何物?中華五千年的傳統(tǒng)文化,各位都修煉的如何?!我知道自己已經(jīng)缺失了這一課程,所以從現(xiàn)在開(kāi)始我要努力補(bǔ)上。
                隨便涂鴉之筆,只是提醒各位,技術(shù)之路漫長(zhǎng)而遙遠(yuǎn),就算你走到盡頭也不一定稱的上“合格的人才”。
                我們經(jīng)常說(shuō)“提高知識(shí)文化”,彷佛大家沒(méi)有深刻的體味這句話:知識(shí)是知識(shí),文化是文化,兩者是不相同的!
                所以在此提醒各位“在鍵盤上耕耘未來(lái)的人”,在你修煉技術(shù)的同時(shí),別忘了文化修為的提高,當(dāng)然思想也是很重要的!
            posted @ 2006-02-01 23:00 lewislau 阿木 閱讀(485) | 評(píng)論 (1)編輯 收藏

            僅列出標(biāo)題  
            国产高潮国产高潮久久久| 一97日本道伊人久久综合影院| 久久综合久久鬼色| 激情综合色综合久久综合| 国产精品久久一区二区三区| 久久久av波多野一区二区| 久久香蕉国产线看观看精品yw| 久久精品午夜一区二区福利| 久久久久AV综合网成人| 香蕉久久夜色精品升级完成| 国产麻豆精品久久一二三| 久久免费小视频| 亚洲第一永久AV网站久久精品男人的天堂AV| 久久精品国产亚洲一区二区三区| 午夜视频久久久久一区 | 亚洲成av人片不卡无码久久| 亚洲人成无码www久久久| 久久综合久久综合亚洲| 亚洲精品无码成人片久久| 精品久久久久久国产潘金莲| 99久久精品无码一区二区毛片| 久久久久国产视频电影| 日本人妻丰满熟妇久久久久久| 久久综合欧美成人| 婷婷国产天堂久久综合五月| 久久久无码精品亚洲日韩按摩| 97久久精品无码一区二区天美| 伊人久久大香线焦综合四虎 | 久久一区二区三区免费| 无码国内精品久久综合88| 久久男人Av资源网站无码软件| 99久久精品免费看国产一区二区三区| 合区精品久久久中文字幕一区| AV无码久久久久不卡蜜桃| 久久夜色精品国产| 免费观看久久精彩视频| 欧美日韩精品久久久久| 国产成人综合久久久久久| 人妻无码αv中文字幕久久琪琪布 人妻无码久久一区二区三区免费 人妻无码中文久久久久专区 | 久久精品99久久香蕉国产色戒| 亚洲国产一成久久精品国产成人综合|