• <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>

            走進(jìn)我的2008

                    很早就想寫這篇日志了,2008年了嗎,一個(gè)新的開(kāi)始,總要寫點(diǎn)什么做點(diǎn)留念吧!可以一拖再拖,現(xiàn)在都1月5號(hào)了!感覺(jué)自己再不寫的話,就要到2009年才能寫這篇日志了。
                    2008年的第一個(gè)天,也就是1月1號(hào),不知道各位是怎么過(guò)的,我是基本上就躺在床上什么都不想,傻乎乎的看完了一部《雙妻時(shí)代》,因?yàn)檫@樣,那一天的時(shí)間我才能打發(fā)掉,那樣才能讓我覺(jué)得不失落。前段時(shí)間突然覺(jué)得自己畢業(yè)后的很多時(shí)間都用來(lái)去追尋所謂的愛(ài)情,結(jié)果卻TMD什么都沒(méi)有,無(wú)非讓自己更曖昧,更知道怎么才不會(huì)受傷,更知道怎么才能收放自如。。。。。。等等一切,我發(fā)現(xiàn)我開(kāi)始“變得有點(diǎn)壞”了,不是我想變,是別人把我變成這樣的,不要說(shuō)“你可以不變啊”,我是沒(méi)有想變,可是TMD我也不知道怎么就這樣了。我被人說(shuō)成了“太曖昧了、嘴巴太甜了。。。”,這兩天還被一些人定義成“一個(gè)騙子”,可我什么都沒(méi)有做啊。。。。。。難道讓我去接受一個(gè)我不喜歡的人,我就不是騙子了? 呵呵!只是有的時(shí)候覺(jué)得很好笑,以前都TMD人家不要我,原來(lái)我也可以“不要”別人的。太搞笑了!
                    不想那么多了,我知道我自己想要什么,無(wú)論別人怎么說(shuō)我,只是希望以后的她對(duì)我好就足夠了。這里再扯淡一下:有個(gè)人幫我看手上的愛(ài)情線,說(shuō)我要招惹7個(gè)女人,暈死!!我現(xiàn)在滿打滿算,還有好幾個(gè)要我招惹啊!!
                    前段時(shí)間去參加一個(gè)培訓(xùn)后,突然發(fā)現(xiàn)自己原來(lái)這一年多,都把時(shí)間花在前任女朋友身上了,太多的東西要學(xué)了,要不然真的要“掛了”,發(fā)現(xiàn)自己對(duì)技術(shù)的了解太少了,自己不知道最近一年都在干些什么,再不學(xué)習(xí)很快就要完蛋了,追,要追,最近2個(gè)月來(lái)的學(xué)習(xí)后覺(jué)的自己還是比較喜歡現(xiàn)在這樣生活,有學(xué)習(xí)才有樂(lè)趣。不是嗎? 希望到了2009年1月1號(hào)的時(shí)候,自己通過(guò)1年多的學(xué)習(xí),可以有些進(jìn)步。
                   “To Do,To Become,To Own”----用這句話勉勵(lì)自己!

            posted @ 2008-01-05 16:23 李亞 閱讀(262) | 評(píng)論 (0)編輯 收藏

            [轉(zhuǎn)] 深入探討MFC消息循環(huán)和消息泵

            深入探討MFC消息循環(huán)和消息泵

            作者:周焱

            首 先,應(yīng)該清楚MFC的消息循環(huán)(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和 MFC的消息在窗口之間的路由是兩件不同的事情。在MFC的應(yīng)用程序中(應(yīng)用程序類基于CWinThread繼承),必須要有一個(gè)消息循環(huán),他的作用是從 應(yīng)用程序的消息隊(duì)列中讀取消息,并把它派送出去(::DispatchMessage)。而消息路由是指消息派送出去之后,系統(tǒng)(USER32.DLL) 把消息投遞到哪個(gè)窗口,以及以后消息在窗口之間的傳遞是怎樣的。

            消息分為隊(duì)列消息(進(jìn)入線程的消息隊(duì)列) 和非隊(duì)列消息(不進(jìn)入線程的消息隊(duì)列)。對(duì)于隊(duì)列消息,最常見(jiàn)的是鼠標(biāo)和鍵盤觸發(fā)的消息,例如WM_MOUSERMOVE,WM_CHAR等消息;還有例 如:WM_PAINT、WM_TIMER和WM_QUIT。當(dāng)鼠標(biāo)、鍵盤事件被觸發(fā)后,相應(yīng)的鼠標(biāo)或鍵盤驅(qū)動(dòng)程序就會(huì)把這些事件轉(zhuǎn)換成相應(yīng)的消息,然后輸 送到系統(tǒng)消息隊(duì)列,由Windows系統(tǒng)負(fù)責(zé)把消息加入到相應(yīng)線程的消息隊(duì)列中,于是就有了消息循環(huán)(從消息隊(duì)列中讀取并派送消息)。還有一種是非隊(duì)列消 息,他繞過(guò)系統(tǒng)隊(duì)列和消息隊(duì)列,直接將消息發(fā)送到窗口過(guò)程。例如,當(dāng)用戶激活一個(gè)窗口系統(tǒng)發(fā)送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。創(chuàng)建窗口時(shí)發(fā)送WM_CREATE消息。在后面你將看到,MS這么設(shè)計(jì)是很有道理的,以及他的整套實(shí)現(xiàn)機(jī)制。

            這里講述MFC的消息循環(huán),消息泵。先看看程序啟動(dòng)時(shí),怎么進(jìn)入消息循環(huán)的:
            _tWinMain ->AfxWinMain ->AfxWinInit ->CWinThread::InitApplication ->CWinThread::InitInstance ->CWinThread::Run

            非對(duì)話框程序的消息循環(huán)的事情都從這CWinThread的一Run開(kāi)始...

            第一部分:非對(duì)話框程序的消息循環(huán)機(jī)制。

            //thrdcore.cpp
            // main running routine until thread exits
            int CWinThread::Run()
            {
            ASSERT_VALID(this);

            // for tracking the idle time state
            BOOL bIdle = TRUE;
            LONG lIdleCount = 0;

            // acquire and dispatch messages until a WM_QUIT message is received.
            for (;;)
            {
            // phase1: check to see if we can do idle work
            while (bIdle &&
               !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
            {
               // call OnIdle while in bIdle state
               if (!OnIdle(lIdleCount++))
                bIdle = FALSE; // assume "no idle" state
            }

            // 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));
            }    //無(wú)限循環(huán),退出條件是收到WM_QUIT消息。

            ASSERT(FALSE); // not reachable
            }

            這是一個(gè)無(wú)限循環(huán),他的退出條件是收到WM_QUIT消息:

            if (!PumpMessage())
                return ExitInstance();

            在PumpMessage中,如果收到WM_QUIT消息,那么返回FALSE,所以ExitInstance()函數(shù)執(zhí)行,跳出循環(huán),返回程序的退出代碼。所以,一個(gè)程序要退出,只用在代碼中調(diào)用函數(shù)

            VOID PostQuitMessage( int nExitCode )。指定退出代碼nExitCode就可以退出程序。

            下面討論一下這個(gè)函數(shù)Run的流程,分兩步:

            1, 第一個(gè)內(nèi)循環(huán)phase1。bIdle代表程序是否空閑。他的意思就是,如果程序是空閑并且消息隊(duì)列中沒(méi)有要處理的消息,那么調(diào)用虛函數(shù)OnIdle進(jìn)行 空閑處理。在這個(gè)處理中將更新UI界面(比如工具欄按鈕的enable和disable狀態(tài)),刪除臨時(shí)對(duì)象(比如用FromHandle得到的對(duì)象指 針。由于這個(gè)原因,在函數(shù)之間傳遞由FromHandle得到的對(duì)象指針是不安全的,因?yàn)樗麤](méi)有持久性)。OnIdle是可以重載的,你可以重載他并返回 TRUE使消息循環(huán)繼續(xù)處于空閑狀態(tài)。

            NOTE:MS用臨時(shí)對(duì)象是出于效率上的考慮,使內(nèi)存 有效利用,并能夠在空閑時(shí)自動(dòng)撤銷資源。關(guān)于由句柄轉(zhuǎn)換成對(duì)象,可以有若干種方法。一般是先申明一個(gè)對(duì)象obj,然后使用obj.Attatch來(lái)和一個(gè) 句柄綁定。這樣產(chǎn)生的對(duì)象是永久的,你必須用obj.Detach來(lái)釋放對(duì)象。

            2,第二個(gè)內(nèi)循環(huán)phase2。在這個(gè)循環(huán)內(nèi)先啟動(dòng)消息泵(PumpMessage),如果不是WM_QUIT消息,消息泵將消息發(fā)送出去(::DispatchMessage)。消息的目的地是消息結(jié)構(gòu)中的hwnd字段所對(duì)應(yīng)的窗口。
            //thrdcore.cpp
            BOOL CWinThread::PumpMessage()
            {
            ASSERT_VALID(this);

            //如果是WM_QUIT就退出函數(shù)(return FALSE),這將導(dǎo)致程序結(jié)束.
            if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) {
            #ifdef _DEBUG
               if (afxTraceFlags & traceAppMsg)
                TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n");
               m_nDisablePumpCount++; // application must die
                // Note: prevents calling message loop things in 'ExitInstance'
                // will never be decremented
            #endif
               return FALSE;
            }

            #ifdef _DEBUG
            if (m_nDisablePumpCount != 0)
            {
               TRACE0("Error: CWinThread::PumpMessage called when not permitted.\n");
               ASSERT(FALSE);
            }
            #endif

            #ifdef _DEBUG
            if (afxTraceFlags & traceAppMsg)
               _AfxTraceMsg(_T("PumpMessage"), &m_msgCur);
            #endif

            // process this message

            if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
            {
               ::TranslateMessage(&m_msgCur); //鍵轉(zhuǎn)換
               ::DispatchMessage(&m_msgCur); //派送消息
            }
            return TRUE;
            }

            在 這一步有一個(gè)特別重要的函數(shù)大家一定認(rèn)識(shí):PreTranslateMessage。這個(gè)函數(shù)在::DispatchMessage發(fā)送消息到窗口之前, 進(jìn)行對(duì)消息的預(yù)處理。PreTranslateMessage函數(shù)是CWinThread的成員函數(shù),大家重載的時(shí)候都是在View類或者主窗口類中,那 么,它是怎么進(jìn)入別的類的呢?代碼如下:
            //thrdcore.cpp
            BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
            {
            ASSERT_VALID(this);

            // 如果是線程消息,那么將會(huì)調(diào)用線程消息的處理函數(shù)
            if (pMsg->hwnd == NULL && DispatchThreadMessageEx(pMsg))
               return TRUE;

            // walk from target to main window
            CWnd* pMainWnd = AfxGetMainWnd();
            if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
               return TRUE;

            // in case of modeless dialogs, last chance route through main
            //   window's accelerator table
            if (pMainWnd != NULL)
            {
               CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
               if (pWnd->GetTopLevelParent() != pMainWnd)
                return pMainWnd->PreTranslateMessage(pMsg);
            }

            return FALSE;   // no special processing
            }

            由上面這個(gè)函數(shù)可以看出:

            第一,如果(pMsg->hwnd == NULL),說(shuō)明這是一個(gè)線程消息。調(diào)用CWinThread::DispatchThreadMessageEx到消息映射表找到消息入口,然后調(diào)用消息處理函數(shù)。

            NOTE: 一般用PostThreadMessage函數(shù)發(fā)送線程之間的消息,他和窗口消息不同,需要指定線程id,消息激被系統(tǒng)放入到目標(biāo)線程的消息隊(duì)列中;用 ON_THREAD_MESSAGE( message, memberFxn )宏可以映射線程消息和他的處理函數(shù)。這個(gè)宏必須在應(yīng)用程序類(從CWinThread繼承)中,因?yàn)橹挥袘?yīng)用程序類才處理線程消息。如果你在別的類(比 如視圖類)中用這個(gè)宏,線程消息的消息處理函數(shù)將得不到線程消息。

            第二,消息的目標(biāo)窗口的 PreTranslateMessage函數(shù)首先得到消息處理權(quán),如果函數(shù)返回FALSE,那么他的父窗口將得到消息的處理權(quán),直到主窗口;如果函數(shù)返回 TRUE(表示消息已經(jīng)被處理了),那么就不需要調(diào)用父類的PreTranslateMessage函數(shù)。這樣,保證了消息的目標(biāo)窗口以及他的父窗口都可 以有機(jī)會(huì)調(diào)用PreTranslateMessage--在消息發(fā)送到窗口之前進(jìn)行預(yù)處理(如果自己處理完然后返回FALSE的話 -_-b),如果你想要消息不傳遞給父類進(jìn)行處理的話,返回TRUE就行了。

            第三,如果消息的目標(biāo)窗口和主窗口沒(méi)有父子關(guān)系,那么再調(diào)用主 窗口的PreTranslateMessage函數(shù)。為什么這樣?由第二步知道,一個(gè)窗口的父窗口不是主窗口的話,盡管它的 PreTranslateMessage返回FALSE,主窗口也沒(méi)有機(jī)會(huì)調(diào)用PreTranslateMessage函數(shù)。我們知道,加速鍵的轉(zhuǎn)換一般 在框架窗口的PreTranslateMessage函數(shù)中。我找遍了MFC中關(guān)于加速鍵轉(zhuǎn)換的處理,只有CFrameWnd, CMDIFrameWnd,CMDIChildWnd等窗口類有。所以,第三步的意思是,如果消息的目標(biāo)窗口(他的父窗口不是主窗口,比如一個(gè)這樣的非模 式對(duì)話框)使消息的預(yù)處理繼續(xù)漫游的話(他的PreTranslateMessage返回FALSE),那么給一次機(jī)會(huì)給主窗口調(diào)用 PreTranslateMessage(萬(wàn)一他是某個(gè)加速鍵消息呢?),這樣能夠保證在有非模式對(duì)話框的情況下還能保證主窗口的加速鍵好使。
            我做了一個(gè)小例子,在對(duì)話框類的PreTranslateMessage中,返回FALSE。在主窗口顯示這個(gè)非模式對(duì)話框,在對(duì)話框擁有焦點(diǎn)的時(shí)候,仍然能夠激活主窗口的快捷鍵。

            總之,整個(gè)框架就是讓每個(gè)消息的目標(biāo)窗口(包括他的父窗口)都有機(jī)會(huì)參與消息到來(lái)之前的處理。呵呵~

            至 此,非對(duì)話框的消息循環(huán)和消息泵的機(jī)制就差不多了。這個(gè)機(jī)制在一個(gè)無(wú)限循環(huán)中,不斷地從消息隊(duì)列中獲取消息,并且保證了程序的線程消息能夠得到機(jī)會(huì)處理, 窗口消息在預(yù)處理之后被發(fā)送到相應(yīng)的窗口處理過(guò)程。那么,還有一點(diǎn)疑問(wèn),為什么要一會(huì)兒調(diào)用::PeekMessage,一會(huì)兒調(diào)用:: GetMessage呢,他們有什么區(qū)別?

            NOTE:一般來(lái)說(shuō),GetMessage被設(shè)計(jì)用來(lái)高效地從消息隊(duì)列獲取消息。如果隊(duì)列中沒(méi)有消息,那么函數(shù)GetMessage將導(dǎo)致線程休眠(讓出CPU時(shí)間)。而PeekMessage是判斷消息隊(duì)列中如果沒(méi)有消息,它馬上返回0,不會(huì)導(dǎo)致線程處于睡眠狀態(tài)。

            在 上面的phase1第一個(gè)內(nèi)循環(huán)中用到了PeekMessage,它的參數(shù)PM_NOREMOVE表示并不從消息隊(duì)列中移走消息,而是一個(gè)檢測(cè)查詢,如果 消息隊(duì)列中沒(méi)有消息他立刻返回0,如果這時(shí)線程空閑的話將會(huì)引起消息循環(huán)調(diào)用OnIdle處理過(guò)程(上面講到了這個(gè)函數(shù)的重要性)。如果將:: PeekMessage改成::GetMessage(***),那么如果消息隊(duì)列中沒(méi)有消息,線程將休眠,直到線程下一次獲得CPU時(shí)間并且有消息出現(xiàn) 才可能繼續(xù)執(zhí)行,這樣,消息循環(huán)的空閑時(shí)間沒(méi)有得到應(yīng)用,OnIdle也將得不到執(zhí)行。這就是為什么既要用::PeekMessage(查詢),又要 用::GetMessage(做實(shí)際的工作)的緣故。

            第二部分: 對(duì)話框程序的消息循環(huán)機(jī)制

            基于對(duì)話框的MFC工程和上面的消息循環(huán)機(jī)制不一樣。實(shí)際上MFC的對(duì)話框工程程序就是模式對(duì)話框。他和上面講到的非對(duì)話框程序的不同之處,主要在于應(yīng)用程序?qū)ο蟮腎nitInstance()不一樣。

            //dlg_5Dlg.cpp
            BOOL CDlg_5App::InitInstance()
            {
            AfxEnableControlContainer();
            #ifdef _AFXDLL
            Enable3dControls();    // Call this when using MFC in a shared DLL
            #else
            Enable3dControlsStatic(); // Call this when linking to MFC statically
            #endif

            CDlg_5Dlg dlg; //定義一個(gè)對(duì)話框?qū)ο?/font>
            m_pMainWnd = &dlg;
            int nResponse = dlg.DoModal(); //對(duì)話框的消息循環(huán)在這里面開(kāi)始
            if (nResponse == IDOK)
            {
               // TODO: Place code here to handle when the dialog is
               // dismissed with OK
            }
            else if (nResponse == IDCANCEL)
            {
               // TODO: Place code here to handle when the dialog is
               // dismissed with Cancel
            }

            // Since the dialog has been closed, return FALSE so that we exit the
            // application, rather than start the application's message pump.
            return FALSE;
            }

            NOTE: InitInstance函數(shù)返回FALSE,由最上面程序啟動(dòng)流程可以看出,CWinThread::Run是不會(huì)得到執(zhí)行的。也就是說(shuō),上面第一部分 說(shuō)的消息循環(huán)在對(duì)話框中是不能執(zhí)行的。實(shí)際上,對(duì)話框也有消息循環(huán),她的消息循環(huán)在CDialog::DoModal()虛函數(shù)中的一個(gè) RunModalLoop函數(shù)中。

            這個(gè)函數(shù)的實(shí)現(xiàn)體在CWnd類中:
            int CWnd::RunModalLoop(DWORD dwFlags)
            {
            ASSERT(::IsWindow(m_hWnd)); // window must be created
            ASSERT(!(m_nFlags & WF_MODALLOOP)); // window must not already be in modal state

            // for tracking the idle time state
            BOOL bIdle = TRUE;
            LONG lIdleCount = 0;
            BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE);
            HWND hWndParent = ::GetParent(m_hWnd);
            m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);
            MSG* pMsg = &AfxGetThread()->m_msgCur;

            // acquire and dispatch messages until the modal state is done
            for (;;)
            {
               ASSERT(ContinueModal());

               // phase1: check to see if we can do idle work
               while (bIdle &&
                !::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))
               {
                ASSERT(ContinueModal());

                // show the dialog when the message queue goes idle
                if (bShowIdle)
                {
                 ShowWindow(SW_SHOWNORMAL);
                 UpdateWindow();
                 bShowIdle = FALSE;
                }

                // call OnIdle while in bIdle state
                if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent != NULL && lIdleCount == 0)
                {
                 // send WM_ENTERIDLE to the parent
                 ::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd);
                }
                if ((dwFlags & MLF_NOKICKIDLE) ||
                 !SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))
                {
                 // stop idle processing next time
                 bIdle = FALSE;
                }
               }

            // phase2: pump messages while available
               do
               {
                ASSERT(ContinueModal());

                // pump message, but quit on WM_QUIT
               //PumpMessage(消息泵)的實(shí)現(xiàn)和上面講的差不多。都是派送消息到窗口。
                if (!AfxGetThread()->PumpMessage())
                {
                 AfxPostQuitMessage(0);
                 return -1;
                }

                // show the window when certain special messages rec'd
                if (bShowIdle &&
                 (pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))
                {
                 ShowWindow(SW_SHOWNORMAL);
                 UpdateWindow();
                 bShowIdle = FALSE;
                }

                if (!ContinueModal())
                 goto ExitModal;

                // reset "no idle" state after pumping "normal" message
                if (AfxGetThread()->IsIdleMessage(pMsg))
                {
                 bIdle = TRUE;
                 lIdleCount = 0;
                }

               } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));
            } //無(wú)限循環(huán)

            ExitModal:
            m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);
            return m_nModalResult;
            }

            先說(shuō)說(shuō)怎么退出這個(gè)無(wú)限循環(huán),在代碼中:
            if (!ContinueModal())
            goto ExitModal;
            決定是否退出循環(huán),消息循環(huán)函數(shù)返回也就是快要結(jié)束結(jié)束程序了。
            BOOL CWnd::ContinueModal()
            {
            return m_nFlags & WF_CONTINUEMODAL;
            }

            NOTE: CWnd::ContinueModal()函數(shù)檢查對(duì)話框是否繼續(xù)模式。返回TRUE,表示現(xiàn)在是模式的;返回FALSE,表示對(duì)話框已經(jīng)不是模式(將要結(jié)束)。

            如 果要結(jié)束對(duì)話框,在內(nèi)部最終會(huì)調(diào)用函數(shù)CWnd::EndModalLoop,它取消m_nFlags的模式標(biāo)志(消息循環(huán)中的 ContinueModal函數(shù)將返回FALSE,消息循環(huán)將結(jié)束,程序?qū)⑼顺?;然后激發(fā)消息循環(huán)讀取消息。也就是說(shuō),結(jié)束模式對(duì)話框是一個(gè)標(biāo)志,改變 這個(gè)標(biāo)志就可以了。他的代碼是:

            //wincore.cpp
            void CWnd::EndModalLoop(int nResult)
            {
            ASSERT(::IsWindow(m_hWnd));

            // this result will be returned from CWnd::RunModalLoop
            m_nModalResult = nResult;

            // make sure a message goes through to exit the modal loop
            if (m_nFlags & WF_CONTINUEMODAL)
            {
               m_nFlags &= ~WF_CONTINUEMODAL;
               PostMessage(WM_NULL);
            }
            }

            NOTE: PostMessage(NULL)是有用的。如果消息隊(duì)列中沒(méi)有消息的話,可能消息循環(huán)中的ContinueModal()不會(huì)馬上執(zhí)行,發(fā)送一個(gè)空消息是激發(fā)消息循環(huán)馬上工作。

            下面說(shuō)一下CWnd::RunModalLoop函數(shù)中的消息循環(huán)究竟干了些什么事情:
            1, 第一個(gè)內(nèi)循環(huán)。首先從消息隊(duì)列中查詢消息,如果對(duì)話框空閑,而且消息隊(duì)列中沒(méi)有消息,他做三件事情,大家應(yīng)到都能從字面上明白什么意思。最重要的是發(fā)送 WM_KICKIDLE消息。為什么呢?第一部分講到了,非對(duì)話框程序用OnIdle來(lái)更新用戶界面(UI),比如工具欄,狀態(tài)欄。那么,如果對(duì)話框中也 有工具欄和狀態(tài)欄呢,在哪里更新(網(wǎng)上有很多這樣的程序)?可以處理WM_KICKIDLE消息:

            LRESULT CDlg_5Dlg::OnKickIdle(WPARAM w,LPARAM l)
            {
                 //調(diào)用CWnd::UpdateDialogControls更新用戶界面
                 UpdateDialogControls(this, TRUE);
                 return 0;
            }

            NOTE: CWnd::UpdateDialog函數(shù)發(fā)送CN_UPDATE_COMMAND_UI消息給所有的用戶界面對(duì)話框控件。

            2, 第二個(gè)內(nèi)循環(huán)。最重要的還是PumpMessage派送消息到目標(biāo)窗口。其他的,像第二個(gè)if語(yǔ)句,0x118消息好像是WM_SYSTIMER消息(系 統(tǒng)用來(lái)通知光標(biāo)跳動(dòng)的一個(gè)消息)。也就是說(shuō),如果消息為WM_SYSTIMER或者WM_SYSKEYDOWN,并且空閑顯示標(biāo)志為真的話,就顯示窗口并 通知窗口立刻重繪。

            總之,對(duì)話框的消息循環(huán)機(jī)制和非對(duì)話框(比如SDI,MDI)還是類似 的,僅僅側(cè)重點(diǎn)不同。模式對(duì)話框是模式顯示,自然有他的特點(diǎn)。下面部分討論一下模式對(duì)話框和非模式對(duì)話框的區(qū)別。因?yàn)槟J綄?duì)話框有自己的特殊消息循環(huán);而 非模式對(duì)話框,共用程序的消息循環(huán),和普通的窗口已經(jīng)沒(méi)有什么大的區(qū)別了。

            第三部分:模式對(duì)話框和非模式對(duì)話框的區(qū)別

            這個(gè)話題已經(jīng)有很多人討論,我說(shuō)說(shuō)我所理解的意思。
            在MFC 框架中,一個(gè)對(duì)話框?qū)ο驞oModal一下就能產(chǎn)生一個(gè)模式對(duì)話框,Create一下就能產(chǎn)生一個(gè)非模式對(duì)話框。實(shí)際上,無(wú)論是模式對(duì)話框還是非模式對(duì)話 框,在MFC內(nèi)部都是調(diào)用::CreateDialogIndirect(***)函數(shù)來(lái)創(chuàng)建非模式對(duì)話框。只是模式對(duì)話框作了更多的工作,包括使父窗口 無(wú)效,然后進(jìn)入自己的消息循環(huán)等等。::CreateDialogIndirect(***)函數(shù)最終調(diào)用CreateWindowEx函數(shù)通知系統(tǒng)創(chuàng)建 窗體并返回句柄,他內(nèi)部沒(méi)有實(shí)現(xiàn)自己的消息循環(huán)。
            非模式對(duì)話框創(chuàng)建之后立即返回,并且和主程序共用一個(gè)消息循環(huán)。非模式對(duì)話框要等對(duì)話框結(jié)束之后才返回,自己有消息循環(huán)。比如下面的代碼:
            CMyDlg* pdlg = new CMyDlg;
            pdlg ->Create(IDD_DIALOG1);
            pdlg->ShowWindow(SW_SHOW);
            MessageBox("abc");
            非模式對(duì)話框和消息框MessageBox幾乎是同時(shí)彈出來(lái)。而如果將Create改成DoModal,那么,只能彈出模式對(duì)話框,在關(guān)閉了對(duì)話框之后(模式對(duì)話框自己的消息循環(huán)結(jié)束),消息框才彈出來(lái)。

            NOTE: 可以在模式對(duì)話框中調(diào)用GetParent()->EnableWindow(true);這樣,主窗口的菜單,工具欄又激活了,能用了。MFC使 用非模式對(duì)話框來(lái)模擬模式對(duì)話框,而在win32 SDK程序中,模式對(duì)話框激發(fā)他的父窗口Enable操作是沒(méi)有效果的。

            關(guān)于消息循環(huán)總結(jié):


            1, 我們站在一個(gè)什么高度看消息循環(huán)?消息循環(huán)其實(shí)沒(méi)有什么深?yuàn)W的道理。如果一個(gè)郵遞員要不斷在一個(gè)城市中送信,我們要求他做什么?要求他來(lái)回跑,但他一次只 能在一個(gè)地方出現(xiàn)。如果我們的應(yīng)用程序只有一個(gè)線程的話,我們要他不斷地為窗口傳遞消息,我們?cè)趺醋觯吭谝粋€(gè)循環(huán)中不斷的檢測(cè)消息,并將他發(fā)送到適當(dāng)?shù)拇?口。窗口可以有很多個(gè),但消息循環(huán)只有一個(gè),而且每時(shí)每刻最多只有一個(gè)地方在執(zhí)行代碼。為什么? 看第二點(diǎn)。

            2,因?yàn)槭菃尉€程的(程序進(jìn)程 啟動(dòng)的時(shí)候,只有而且有一個(gè)線程,我們稱他為主線程),所以就像郵遞員一樣,每次只能在某一個(gè)地方干活。什么意思呢?舉個(gè)例子,用:: DiapatchMessage派送消息,在窗口處理過(guò)程(WinProc,窗口函數(shù))返回之前,他是阻塞的,不會(huì)立即返回,也就是消息循環(huán)此時(shí)不能再?gòu)?消息隊(duì)列中讀取消息,直到::DispatchMessage返回。如果你在窗口函數(shù)中執(zhí)行一個(gè)死循環(huán)操作,就算你用PostQuitMessage函數(shù) 退出,程序也會(huì)down掉。
            while(1)
            {
                PostQuitMessage(0); //程序照樣down.
            }
            所 以,當(dāng)窗口函數(shù)處理沒(méi)有返回的時(shí)候,消息循環(huán)是不會(huì)從消息隊(duì)列中讀取消息的。這也是為什么在模式對(duì)話框中要自己用無(wú)限循環(huán)來(lái)繼續(xù)消息循環(huán),因?yàn)檫@個(gè)無(wú)限循 環(huán)阻塞了原來(lái)的消息循環(huán),所以,在這個(gè)無(wú)限循環(huán)中要用GetMessage,PeekMessage,DispatchMessage來(lái)從消息隊(duì)列中讀取 消息并派送消息了。要不然程序就不會(huì)響應(yīng)了,這不是我們所希望的。
            所以說(shuō),消息循環(huán)放在程序的什么的地方都基本上是過(guò)的去的,比如放在DLL里 面。但是,最好在任何時(shí)候,只有一個(gè)消息循環(huán)在工作(其他的都被阻塞了)。然后,我們要作好的一件事情,就是怎么從消息循環(huán)中退出!當(dāng)然用WM_QUIT 是可以拉~(PostThreadMessage也是個(gè)好主意),這個(gè)消息循環(huán)退出后,可能程序退出,也可能會(huì)激活另外一個(gè)被阻塞的消息循環(huán),程序繼續(xù)運(yùn) 行。這要看你怎么想,怎么去做。最后一個(gè)消息循環(huán)結(jié)束的時(shí)候,也許就是程序快結(jié)束的時(shí)候,因?yàn)橹骶€程的執(zhí)行代碼也快要完了(除非BT的再作個(gè)死循環(huán))。

            NOTE: 讓windows系統(tǒng)知道創(chuàng)建一個(gè)線程的唯一方法是調(diào)用API CreatThread函數(shù)(__beginthreadex之類的都要在內(nèi)部調(diào)用他創(chuàng)建新線程)。好像windows核心編程說(shuō),在win2000下, 系統(tǒng)用CreateRemoteThread函數(shù)來(lái)創(chuàng)建線程,CreateThread在內(nèi)部調(diào)用CreateRemoteThread。不過(guò)這不是爭(zhēng)論 的焦點(diǎn),至少win98下CreateRemoteThread并不能正常工作,還是CreateThread主持大局。

            3,在整個(gè)消息循環(huán)的機(jī)制中,還必須談到窗口函數(shù)的可重入性。什么意思?就是窗口函數(shù)(他是個(gè)回調(diào)函數(shù))的代碼什么時(shí)候都可以被系統(tǒng)(調(diào)用者一般是user32模塊)調(diào)用。比如在窗口過(guò)程中,向自己的窗口SendMessage(***);那么執(zhí)行過(guò)程是怎樣的?
            我們知道,SendMessage是要等到消息發(fā)送并被目標(biāo)窗口執(zhí)行完之后才返回的。那么窗口在處理消息,然后又等待剛才發(fā)送到本窗口的消息被處理后之后(SendMessage返回)才繼續(xù)往下執(zhí)行,程序不就互相死鎖了嗎?
            其 實(shí)是不會(huì)的。windows設(shè)計(jì)一套適合SendMessage的算法,他判斷如果發(fā)送的消息是屬于本線程創(chuàng)建的窗口的,那么直接由user32模塊調(diào)用 窗口函數(shù)(可能就有窗口重入),并將消息的處理結(jié)果結(jié)果返回。這樣做體現(xiàn)了窗口重入。上面的例子,我們調(diào)用SendMessage(***)發(fā)送消息到本 窗口,那么窗口過(guò)程再次被調(diào)用,處理完消息之后將結(jié)果返回,然后SendMessage之后的程序接著執(zhí)行。對(duì)于非隊(duì)列消息,如果沒(méi)有窗口重入,不知道會(huì) 是什么樣子。

            NOTE: 由于窗口的可重入性。在win32 SDK程序中應(yīng)盡量少用全局變量和靜態(tài)變量,因?yàn)樵诖翱诤瘮?shù)執(zhí)行過(guò)程中可能窗口重入,如果重入后將這些變量改了,但你的程序在窗口重入返回之后繼續(xù)執(zhí)行, 可能就是使用已經(jīng)改變的全局或靜態(tài)變量。在MFC中(所有窗口的窗口函數(shù)基本上都是AfxWndProc),按照類的思想進(jìn)行了組織,一般變量都是類中 的,好管理的多。

            4,MFC中窗口類(比如C**View,CFrameWnd等)中的MessageBox函數(shù),以及 AfxMessageBox函數(shù)都是阻塞原有的消息循環(huán)的。由消息框內(nèi)部的一個(gè)消息循環(huán)來(lái)從消息隊(duì)列中讀取消息,并派送消息(和模式對(duì)話框類似)。實(shí)際 上,這些消息函數(shù)最終調(diào)用的是::MessageBox,它在消息框內(nèi)部實(shí)現(xiàn)了一個(gè)消息循環(huán)(原有的主程序消息循環(huán)被阻塞了)。論壇中碰到過(guò)幾次關(guān)于計(jì)時(shí) 器和消息框的問(wèn)題,看下面的代碼:
            void CTest_recalclayoutView::OnTimer(UINT nIDEvent)
            {
            // TODO: Add your message handler code here and/or call default
            MessageBox("abc");
            while(1); //設(shè)計(jì)一個(gè)死循環(huán)
            CView::OnTimer(nIDEvent);
            }
            咱 讓OnTimer大約5秒鐘彈出一個(gè)消息框。那么,消息框不斷的被彈出來(lái),只要消息框不被關(guān)閉,那么程序就不會(huì)進(jìn)入死循環(huán)。實(shí)際上,每次彈出對(duì)話框,都是 最上層的那個(gè)消息框掌握著消息循環(huán),其他的消息循環(huán)被阻塞了。只要不關(guān)閉最上面的消息框,while(1);就得不到執(zhí)行。如果點(diǎn)了關(guān)閉,程序就進(jìn)入了死 循環(huán),只能用ctrl+alt+del來(lái)解決問(wèn)題了。

            5,消息循環(huán)在很多地方都有應(yīng)用。比如應(yīng)用在線程池中。一個(gè)線程的執(zhí)行周期一般在線程 函數(shù)返回之后結(jié)束,那么怎么延長(zhǎng)線程的生命周期呢?一種方法就是按照消息循環(huán)的思想,在線程中加入消息循環(huán),不斷地從線程隊(duì)列讀取消息,并處理消息,線程 的生命周期就保持著直到這個(gè)消息循環(huán)的退出。

            NOTE:只要線程有界面元素或者調(diào)用GetMessage,或者有線程消息發(fā)送過(guò)來(lái),系統(tǒng)就會(huì)為線程創(chuàng)建一個(gè)消息隊(duì)列。

             

            6, 在單線程程序中,如果要執(zhí)行一個(gè)長(zhǎng)時(shí)間的復(fù)雜操作而且界面要有相應(yīng)的話,可以考慮用自己的消息泵。比如,可以將一個(gè)阻塞等待操作放在一個(gè)循環(huán)中,并將超時(shí) 值設(shè)置得比較小,然后每個(gè)等待的片段中用消息泵繼續(xù)消息循環(huán),使界面能夠響應(yīng)用戶操作。等等之類,都可以應(yīng)用消息泵(調(diào)用一個(gè)類似這樣的函數(shù)):
            BOOL CChildView::PeekAndPump()
            {
            MSG msg;
            while(::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
            {
               if(!AfxGetApp()->PumpMessage())
               {
                ::PostQuitMessage(0);
                return false;
               }
            }
            return true;
            }
            其實(shí),用多線程也能解決復(fù)雜運(yùn)算時(shí)的界面問(wèn)題,但是沒(méi)有這么方便,而且一般要加入線程通信和同步,考慮的事情更多一點(diǎn)。

             

            綜上所述,MFC消息循環(huán)就那么回事,主要思想還是和SDK中差不多。這種思想主要的特點(diǎn)表現(xiàn)在迎合MFC整個(gè)框架上,為整個(gè)框架服務(wù),為應(yīng)用和功能服務(wù)。這是我的理解。呵呵~

            posted @ 2007-12-17 20:28 李亞 閱讀(382) | 評(píng)論 (0)編輯 收藏

            軟件開(kāi)發(fā)管理的75條體會(huì)[轉(zhuǎn)載]

                      1. 你們的項(xiàng)目組使用源代碼管理工具了么?

              MVM:應(yīng)該用。VSS、CVS、PVCS、ClearCase、CCC/Harvest、FireFly都可以。我的選擇是VSS。

              2. 你們的項(xiàng)目組使用缺陷管理系統(tǒng)了么?

              MVM:應(yīng)該用。ClearQuest太復(fù)雜,我的推薦是BugZilla。

              3. 你們的測(cè)試組還在用Word寫測(cè)試用例么?

              MVM:不要用Word寫測(cè)試用例(Test Case)。應(yīng)該用一個(gè)專門的系統(tǒng),可以是Test Manager,也可以是自己開(kāi)發(fā)一個(gè)ASP.NET的小網(wǎng)站。主要目的是Track和Browse。

              4. 你們的項(xiàng)目組有沒(méi)有建立一個(gè)門戶網(wǎng)站?

              MVM:要有一個(gè)門戶網(wǎng)站,用來(lái)放Contact Info、Baselined Schedule、News等等。推薦Sharepoint Portal Server 2003來(lái)實(shí)現(xiàn),15分鐘就搞定。買不起SPS 2003可以用WSS (Windows Sharepoint Service)。

              5. 你們的項(xiàng)目組用了你能買到最好的工具么?

              MVM:應(yīng)該用盡量好的工具來(lái)工作。比如,應(yīng)該用VS.NET而不是Notepad來(lái)寫C#。用Notepad寫程序多半只是一種炫耀。但也要考慮到經(jīng)費(fèi),所以說(shuō)是“你能買到最好的”。

              6. 你們的程序員工作在安靜的環(huán)境里么?

              MVM:需要安靜環(huán)境。這點(diǎn)極端重要,而且要保證每個(gè)人的空間大于一定面積。

              7. 你們的員工每個(gè)人都有一部電話么?

              MVM:需要每人一部電話。而且電話最好是帶留言功能的。當(dāng)然,上這么一套帶留言電話系統(tǒng)開(kāi)銷不小。不過(guò)至少每人一部電話要有,千萬(wàn)別搞得經(jīng)常有人站起來(lái)喊:“某某某電話”。《人件》里面就強(qiáng)烈譴責(zé)這種做法。

              8. 你們每個(gè)人都知道出了問(wèn)題應(yīng)該找誰(shuí)么?

              MVM:應(yīng)該知道。任何一個(gè)Feature至少都應(yīng)該有一個(gè)Owner,當(dāng)然,Owner可以繼續(xù)Dispatch給其他人。

              9. 你遇到過(guò)有人說(shuō)“我以為…”么?

              MVM:要消滅“我以為”。Never assume anything。

              10. 你們的項(xiàng)目組中所有的人都坐在一起么?

              MVM:需要。我反對(duì)Virtual Team,也反對(duì)Dev在美國(guó)、Test在中國(guó)這種開(kāi)發(fā)方式。能坐在一起就最好坐在一起,好處多得不得了。

              11. 你們的進(jìn)度表是否反映最新開(kāi)發(fā)進(jìn)展情況?

              MVM:應(yīng)該反映。但是,應(yīng)該用Baseline的方法來(lái)管理進(jìn)度表:維護(hù)一份穩(wěn)定的Schedule,再維護(hù)一份最新更改。Baseline的方法也應(yīng)該用于其它的Spec。Baseline是變更管理里面的一個(gè)重要手段。

              12. 你們的工作量是先由每個(gè)人自己估算的么?

              MVM:應(yīng)該讓每個(gè)人自己估算。要從下而上估算工作量,而不是從上往下分派。除非有其他原因,比如政治任務(wù)工期固定等。

              13. 你們的開(kāi)發(fā)人員從項(xiàng)目一開(kāi)始就加班么?

              MVM:不要這樣。不要一開(kāi)始就搞疲勞戰(zhàn)。從項(xiàng)目一開(kāi)始就加班,只能說(shuō)明項(xiàng)目進(jìn)度不合理。當(dāng)然,一些對(duì)日軟件外包必須天天加班,那屬于剝削的范疇。

              14. 你們的項(xiàng)目計(jì)劃中Buffer Time是加在每個(gè)小任務(wù)后面的么?

              MVM:不要。Buffer Time加在每個(gè)小任務(wù)后面,很容易輕易的就被消耗掉。Buffer Time要整段的加在一個(gè)Milestone或者checkpoint前面。

              15. 值得再多花一些時(shí)間,從95%做到100%好

              MVM:值得,非常值得。尤其當(dāng)項(xiàng)目后期人困馬乏的時(shí)候,要堅(jiān)持。這會(huì)給產(chǎn)品帶來(lái)質(zhì)的區(qū)別。

             

            16. 登記新缺陷時(shí),是否寫清了重現(xiàn)步驟?

              MVM:要。這屬于Dev和Test之間的溝通手段。面對(duì)面溝通需要,詳細(xì)填寫Repro Steps也需要。

              17. 寫新代碼前會(huì)把已知缺陷解決么?

              MVM:要。每個(gè)人的缺陷不能超過(guò)10個(gè)或15個(gè),否則必須先解決老的bug才能繼續(xù)寫新代碼。

              18. 你們對(duì)缺陷的輕重緩急有事先的約定么?

              MVM:必須有定義。Severity要分1、2、3,約定好:藍(lán)屏和Data Lost算Sev 1,F(xiàn)unction Error算Sev 2,界面上的算Sev 3。但這種約定可以根據(jù)產(chǎn)品質(zhì)量現(xiàn)狀適當(dāng)進(jìn)行調(diào)整。

              19. 你們對(duì)意見(jiàn)不一的缺陷有三國(guó)會(huì)議么?

              MVM:必須要有。要有一個(gè)明確的決策過(guò)程。這類似于CCB (Change Control Board)的概念。

              20. 所有的缺陷都是由登記的人最后關(guān)閉的么?

              MVM:Bug應(yīng)該由Opener關(guān)閉。Dev不能私自關(guān)閉Bug。

              21. 你們的程序員厭惡修改老的代碼么?

              MVM:厭惡是正常的。解決方法是組織Code Review,單獨(dú)留出時(shí)間來(lái)。XP也是一個(gè)方法。

              22. 你們項(xiàng)目組有Team Morale Activity么?

              MVM:每個(gè)月都要搞一次,吃飯、唱歌、Outing、打球、開(kāi)卡丁車等等,一定要有。不要剩這些錢。

              23. 你們項(xiàng)目組有自己的Logo么?

              MVM:要有自己的Logo。至少應(yīng)該有自己的Codename。

              24. 你們的員工有印有公司Logo的T-Shirt么?

              MVM:要有。能增強(qiáng)歸屬感。當(dāng)然,T-Shirt要做的好看一些,最好用80支的棉來(lái)做。別沒(méi)穿幾次就破破爛爛的。

              25. 總經(jīng)理至少每月參加次項(xiàng)目組會(huì)議

              MVM:要的。要讓team member覺(jué)得高層關(guān)注這個(gè)項(xiàng)目。

              26. 你們是給每個(gè)Dev開(kāi)一個(gè)分支么?

              MVM:反對(duì)。Branch的管理以及Merge的工作量太大,而且容易出錯(cuò)。

              27. 有人長(zhǎng)期不Check-In代碼么?

              MVM:不可以。對(duì)大部分項(xiàng)目來(lái)說(shuō),最多兩三天就應(yīng)該Check-In。

              28. 在Check-In代碼時(shí)都填寫注釋了么?

              MVM:要寫的,至少一兩句話,比如“解決了Bug No.225”。如果往高處拔,這也算做“配置審計(jì)”的一部分。

              29. 有沒(méi)有設(shè)定每天Check-In的最后期限?

              MVM:要的,要明確Check-In Deadline。否則會(huì)Build Break。

              30. 你們能把所有源碼一下子編譯成安裝文件嗎?

              MVM:要的。這是每日編譯(Daily Build)的基礎(chǔ)。而且必須要能夠做成自動(dòng)的。

             

            31. 你們的項(xiàng)目組做每日編譯么?

              MVM:當(dāng)然要做。有三樣?xùn)|西是軟件項(xiàng)目/產(chǎn)品開(kāi)發(fā)必備的:1. bug management; 2. source control; 3. daily build。

              32. 你們公司有沒(méi)有積累一個(gè)項(xiàng)目風(fēng)險(xiǎn)列表?

              MVM:要。Risk Inventory。否則,下個(gè)項(xiàng)目開(kāi)始的時(shí)候,又只能拍腦袋分析Risk了。

              33. 設(shè)計(jì)越簡(jiǎn)單越好

              MVM:越簡(jiǎn)單越好。設(shè)計(jì)時(shí)候多一句話,將來(lái)可能就帶來(lái)無(wú)窮無(wú)盡的煩惱。應(yīng)該從一開(kāi)始就勇敢的砍。這叫scope management。

              34. 盡量利用現(xiàn)有的產(chǎn)品、技術(shù)、代碼

              MVM:千萬(wàn)別什么東西都自己Coding。BizTalk和Sharepoint就是最好的例子,有這兩個(gè)作為基礎(chǔ),可以把起點(diǎn)提高很多。或者可以盡量多用現(xiàn)成的Control之類的。或者盡量用XML,而不是自己去Parse一個(gè)文本文件;盡量用RegExp,而不是自己從頭操作字符串,等等等等。這就是“軟件復(fù)用”的體現(xiàn)。

              35. 你們會(huì)隔一段時(shí)間就停下來(lái)夯實(shí)代碼么?

              MVM:要。最好一個(gè)月左右一次。傳言去年年初Windows組在Stevb的命令下停過(guò)一個(gè)月增強(qiáng)安全。Btw,“夯”這個(gè)字念“hang”,第一聲。

              36. 你們的項(xiàng)目組每個(gè)人都寫Daily Report么?

              MVM:要寫。五分鐘就夠了,寫10句話左右,告訴自己小組的人今天我干了什么。一則為了溝通,二則鞭策自己(要是游手好閑一天,自己都會(huì)不好意思寫的)。

              37. 你們的項(xiàng)目經(jīng)理會(huì)發(fā)出Weekly Report么?

              MVM:要。也是為了溝通。內(nèi)容包括目前進(jìn)度,可能的風(fēng)險(xiǎn),質(zhì)量狀況,各種工作的進(jìn)展等。

              38. 你們項(xiàng)目組是否至少每周全體開(kāi)會(huì)一次?

              MVM:要。一定要開(kāi)會(huì)。程序員討厭開(kāi)會(huì),但每個(gè)禮拜開(kāi)會(huì)時(shí)間加起來(lái)至少應(yīng)該有4小時(shí)。包括team meeting, spec review meeting, bug triage meeting。千萬(wàn)別大家悶頭寫code。

              39. 你們項(xiàng)目組的會(huì)議、討論都有記錄么?

              MVM:會(huì)前發(fā)meeting request和agenda,會(huì)中有人負(fù)責(zé)主持和記錄,會(huì)后有人負(fù)責(zé)發(fā)meeting minutes,這都是effective meeting的要點(diǎn)。而且,每個(gè)會(huì)議都要形成agreements和action items。

              40. 其他部門知道你們項(xiàng)目組在干什么么?

              MVM:要發(fā)一些Newsflash給整個(gè)大組織。Show your team’s value。否則,當(dāng)你坐在電梯里面,其他部門的人問(wèn):“你們?cè)诟陕?#8221;,你回答“ABC項(xiàng)目”的時(shí)候,別人全然不知,那種感覺(jué)不太好。

              41. 通過(guò)Email進(jìn)行所有正式溝通

              MVM:Email的好處是免得抵賴。但也要避免矯枉過(guò)正,最好的方法是先用電話和當(dāng)面說(shuō),然后Email來(lái)確認(rèn)。

              42. 為項(xiàng)目組建立多個(gè)Mailing Group

              MVM:如果在AD+Exchange里面,就建Distribution List。比如,我會(huì)建ABC Project Core Team,ABC Project Dev Team,ABC Project All Testers,ABC Project Extended Team等等。這樣發(fā)起Email來(lái)方便,而且能讓該收到email的人都收到、不該收到不被騷擾。

              43. 每個(gè)人都知道哪里可以找到全部的文檔么?

              MVM:應(yīng)該每個(gè)人都知道。這叫做知識(shí)管理(Knowledge Management)。最方便的就是把文檔放在一個(gè)集中的File Share,更好的方法是用Sharepoint。

              44. 你做決定、做變化時(shí),告訴大家原因了么?

              MVM:要告訴大家原因。Empower team member的手段之一是提供足夠的information,這是MSF一開(kāi)篇的幾個(gè)原則之一。的確如此,tell me why是人之常情,tell me why了才能有understanding。中國(guó)人做事喜歡搞限制,限制信息,似乎能夠看到某一份文件的人就是有身份的人。大錯(cuò)特錯(cuò)。權(quán)威、權(quán)力,不在于是不是能access information/data,而在于是不是掌握資源。

              45. Stay agile and expect change

              MVM:要這樣。需求一定會(huì)變的,已經(jīng)寫好的代碼一定會(huì)被要求修改的。做好心理準(zhǔn)備,對(duì)change不要抗拒,而是expect change。

             

            46. 你們有沒(méi)有專職的軟件測(cè)試人員?

              MVM:要有專職測(cè)試。如果人手不夠,可以peer test,交換了測(cè)試。千萬(wàn)別自己測(cè)試自己的。

              47. 你們的測(cè)試有一份總的計(jì)劃來(lái)規(guī)定做什么和怎么做么?

              MVM:這就是Test Plan。要不要做性能測(cè)試?要不要做Usability測(cè)試?什么時(shí)候開(kāi)始測(cè)試性能?測(cè)試通過(guò)的標(biāo)準(zhǔn)是什么?用什么手段,自動(dòng)的還是手動(dòng)的?這些問(wèn)題需要用Test Plan來(lái)回答。

              48. 你是先寫Test Case然后再測(cè)試的么?

              MVM:應(yīng)該如此。應(yīng)該先設(shè)計(jì)再編程、先test case再測(cè)試。當(dāng)然,事情是靈活的。我有時(shí)候在做第一遍測(cè)試的同時(shí)補(bǔ)上test case。至于先test case再開(kāi)發(fā),我不喜歡,因?yàn)椴涣?xí)慣,太麻煩,至于別人推薦,那試試看也無(wú)妨。

              49. 你是否會(huì)為各種輸入組合創(chuàng)建測(cè)試用例?

              MVM:不要,不要搞邊界條件組合。當(dāng)心組合爆炸。有很多test case工具能夠自動(dòng)生成各種邊界條件的組合——但要想清楚,你是否有時(shí)間去運(yùn)行那么多test case。

              50. 你們的程序員能看到測(cè)試用例么?

              MVM:要。讓Dev看到Test Case吧。我們都是為了同一個(gè)目的走到一起來(lái)的:提高質(zhì)量。

              51. 你們是否隨便抓一些人來(lái)做易用性測(cè)試?

              MVM:要這么做。自己看自己寫的程序界面,怎么看都是順眼的。這叫做審美疲勞——臭的看久了也就不臭了,不方便的永久了也就習(xí)慣了。

              52. 你對(duì)自動(dòng)測(cè)試的期望正確么?

              MVM:別期望太高。依我看,除了性能測(cè)試以外,還是暫時(shí)先忘掉“自動(dòng)測(cè)試”吧,忘掉WinRunner和LoadRunner吧。對(duì)于國(guó)內(nèi)的軟件測(cè)試的現(xiàn)狀來(lái)說(shuō),只能“矯枉必須過(guò)正”了。

              53. 你們的性能測(cè)試是等所有功能都開(kāi)發(fā)完才做的么?

              MVM:不能這樣。性能測(cè)試不能被歸到所謂的“系統(tǒng)測(cè)試”階段。早測(cè)早改正,早死早升天。

              54. 你注意到測(cè)試中的殺蟲(chóng)劑效應(yīng)了么?

              MVM:蟲(chóng)子有抗藥性,Bug也有。發(fā)現(xiàn)的新Bug越來(lái)越少是正常的。這時(shí)候,最好大家交換一下測(cè)試的area,或者用用看其他工具和手法,就又會(huì)發(fā)現(xiàn)一些新bug了。

              55. 你們項(xiàng)目組中有人能說(shuō)出產(chǎn)品的當(dāng)前整體質(zhì)量情況么?

              MVM:要有。當(dāng)老板問(wèn)起這個(gè)產(chǎn)品目前質(zhì)量如何,Test Lead/Manager應(yīng)該負(fù)責(zé)回答。

              56. 你們有單元測(cè)試么?

              MVM:?jiǎn)卧獪y(cè)試要有的。不過(guò)沒(méi)有單元測(cè)試也不是不可以,我做過(guò)沒(méi)有單元測(cè)試的項(xiàng)目,也做成功了——可能是僥幸,可能是大家都是熟手的關(guān)系。還是那句話,軟件工程是非常實(shí)踐、非常工程、非常靈活的一套方法,某些方法在某些情況下會(huì)比另一些方法好,反之亦然。

              57. 你們的程序員是寫完代碼就扔過(guò)墻的么?

              MVM:大忌。寫好一塊程序以后,即便不做單元測(cè)試,也應(yīng)該自己先跑一跑。雖然有了專門的測(cè)試人員,做開(kāi)發(fā)的人也不可以一點(diǎn)測(cè)試都不做。微軟還有Test Release Document的說(shuō)法,程序太爛的話,測(cè)試有權(quán)踢回去。

              58. 你們的程序中所有的函數(shù)都有輸入檢查么?

              MVM:不要。雖然說(shuō)做輸入檢查是write secure code的要點(diǎn),但不要做太多的輸入檢查,有些內(nèi)部函數(shù)之間的參數(shù)傳遞就不必檢查輸入了,省點(diǎn)功夫。同樣的道理,未必要給所有的函數(shù)都寫注釋。寫一部分主要的就夠了。

              59. 產(chǎn)品有統(tǒng)一的錯(cuò)誤處理機(jī)制和報(bào)錯(cuò)界面么?

              MVM:要有。最好能有統(tǒng)一的error message,然后每個(gè)error message都帶一個(gè)error number。這樣,用戶可以自己根據(jù)error number到user manual里面去看看錯(cuò)誤的具體描述和可能原因,就像SQL Server的錯(cuò)誤那樣。同樣,ASP.NET也要有統(tǒng)一的Exception處理。可以參考有關(guān)的Application Block。

              60. 你們有統(tǒng)一的代碼書寫規(guī)范么?

              MVM:要有。Code Convention很多,搞一份來(lái)發(fā)給大家就可以了。當(dāng)然,要是有FxCop這種工具來(lái)檢查代碼就更好了。

             

             61. 你們的每個(gè)人都了解項(xiàng)目的商業(yè)意義么?

              MVM:要。這是Vision的意思。別把項(xiàng)目只當(dāng)成工作。有時(shí)候要想著自己是在為中國(guó)某某行業(yè)的信息化作先驅(qū)者,或者時(shí)不時(shí)的告訴team member,這個(gè)項(xiàng)目能夠?yàn)槟衬衬硣?guó)家部門每年節(jié)省多少多少百萬(wàn)的納稅人的錢,這樣就有動(dòng)力了。平凡的事情也是可以有個(gè)崇高的目標(biāo)的。

              62. 產(chǎn)品各部分的界面和操作習(xí)慣一致么?

              MVM:要這樣。要讓用戶覺(jué)得整個(gè)程序好像是一個(gè)人寫出來(lái)的那樣。

              63. 有可以作為宣傳亮點(diǎn)的Cool Feature么?

              MVM:要。這是增強(qiáng)團(tuán)隊(duì)凝聚力、信心的。而且,“一俊遮百丑”,有亮點(diǎn)就可以掩蓋一些問(wèn)題。這樣,對(duì)于客戶來(lái)說(shuō),會(huì)感覺(jué)產(chǎn)品從質(zhì)量角度來(lái)說(shuō)還是acceptable的。或者說(shuō),cool feature或者說(shuō)亮點(diǎn)可以作為質(zhì)量問(wèn)題的一個(gè)事后彌補(bǔ)措施。

              64. 盡可能縮短產(chǎn)品的啟動(dòng)時(shí)間

              MVM:要這樣。軟件啟動(dòng)時(shí)間(Start-Up time)是客戶對(duì)性能好壞的第一印象。

              65. 不要過(guò)于注重內(nèi)在品質(zhì)而忽視了第一眼的外在印象

              MVM:程序員容易犯這個(gè)錯(cuò)誤:太看重性能、穩(wěn)定性、存儲(chǔ)效率,但忽視了外在感受。而高層經(jīng)理、客戶正相反。這兩方面要兼顧,協(xié)調(diào)這些是PM的工作。

              66. 你們根據(jù)詳細(xì)產(chǎn)品功能說(shuō)明書做開(kāi)發(fā)么?

              MVM:要這樣。要有設(shè)計(jì)才能開(kāi)發(fā),這是必須的。設(shè)計(jì)文檔,應(yīng)該說(shuō)清楚這個(gè)產(chǎn)品會(huì)怎么運(yùn)行,應(yīng)該采取一些講故事的方法。設(shè)計(jì)的時(shí)候千萬(wàn)別鉆細(xì)節(jié),別鉆到數(shù)據(jù)庫(kù)、代碼等具體實(shí)現(xiàn)里面去,那些是后面的事情,一步步來(lái)不能著急。

              67. 開(kāi)始開(kāi)發(fā)和測(cè)試之前每個(gè)人都仔細(xì)審閱功能設(shè)計(jì)么?

              MVM:要做。Function Spec review是用來(lái)統(tǒng)一思想的。而且,review過(guò)以后形成了一致意見(jiàn),將來(lái)再也沒(méi)有人可以說(shuō)“你看,當(dāng)初我就是反對(duì)這么設(shè)計(jì)的,現(xiàn)在吃苦頭了吧”

              68. 所有人都始終想著The Whole Image么?

              MVM:要這樣。項(xiàng)目里面每個(gè)人雖然都只是在制造一片葉子,但每個(gè)人都應(yīng)該知道自己在制造的那片葉子所在的樹(shù)是怎么樣子的。我反對(duì)軟件藍(lán)領(lǐng),反對(duì)過(guò)分的把軟件制造看成流水線、車間。參見(jiàn)第61條。

              69. Dev工作的劃分是單純縱向或橫向的么?

              MVM:不能單純的根據(jù)功能模塊分,或者單純根據(jù)表現(xiàn)層、中間層、數(shù)據(jù)庫(kù)層分。我推薦這么做:首先根據(jù)功能模塊分,然后每個(gè)“層”都有一個(gè)Owner來(lái)Review所有人的設(shè)計(jì)和代碼,保證consistency。

              70. 你們的程序員寫程序設(shè)計(jì)說(shuō)明文檔么?

              MVM:要。不過(guò)我聽(tīng)說(shuō)微軟的程序員1999年以前也不寫。所以說(shuō),寫不寫也不是絕對(duì)的,偷懶有時(shí)候也是可以的。參見(jiàn)第56條。

              71. 你在招人面試時(shí)讓他寫一段程序么?

              MVM:要的。我最喜歡讓人做字符串和鏈表一類的題目。這種題目有很多循環(huán)、判斷、指針、遞歸等,既不偏向過(guò)于考算法,也不偏向過(guò)于考特定的API。

              72. 你們有沒(méi)有技術(shù)交流講座?

              MVM:要的。每一兩個(gè)禮拜搞一次內(nèi)部的Tech Talk或者Chalk Talk吧。讓組員之間分享技術(shù)心得,這筆花錢送到外面去培訓(xùn)劃算。

              73. 你們的程序員都能專注于一件事情么?

              MVM:要讓程序員專注一件事。例如說(shuō),一個(gè)部門有兩個(gè)項(xiàng)目和10個(gè)人,一種方法是讓10個(gè)人同時(shí)參加兩個(gè)項(xiàng)目,每個(gè)項(xiàng)目上每個(gè)人都花50%時(shí)間;另一種方法是5個(gè)人去項(xiàng)目A,5個(gè)人去項(xiàng)目B,每個(gè)人都100%在某一個(gè)項(xiàng)目上。我一定選后面一種。這個(gè)道理很多人都懂,但很多領(lǐng)導(dǎo)實(shí)踐起來(lái)就把屬下當(dāng)成可以任意拆分的資源了。

              74. 你們的程序員會(huì)夸大完成某項(xiàng)工作所需要的時(shí)間么?

              MVM:會(huì)的,這是常見(jiàn)的,尤其會(huì)在項(xiàng)目后期夸大做某個(gè)change所需要的時(shí)間,以次來(lái)抵制change。解決的方法是坐下來(lái)慢慢磨,磨掉程序員的逆反心理,一起分析,并把估算時(shí)間的顆粒度變小。

              75. 盡量不要用Virtual Heads

              MVM:最好不要用Virtual Heads。Virtual heads意味著resource is not secure,shared resource會(huì)降低resource的工作效率,容易增加出錯(cuò)的機(jī)會(huì),會(huì)讓一心二用的人沒(méi)有太多時(shí)間去review spec、review design。一個(gè)dedicated的人,要強(qiáng)過(guò)兩個(gè)只能投入50%時(shí)間和精力的人。我是吃過(guò)虧的:7個(gè)part time的tester,發(fā)現(xiàn)的Bug和干的活,加起來(lái)還不如兩個(gè)full-time的。參見(jiàn)第73條。73條是針對(duì)程序員的,75條是針對(duì)Resource Manager的。

            posted @ 2007-12-07 22:10 李亞 閱讀(378) | 評(píng)論 (1)編輯 收藏

            追MM與設(shè)計(jì)模式的有趣見(jiàn)解

            創(chuàng)建型模式

            1
            FACTORY MM少不了請(qǐng)吃飯了,麥當(dāng)勞的雞翅和肯德基的雞翅都是MM愛(ài)吃的東西,雖然口味有所不同,但不管你帶MM去麥當(dāng)勞或肯德基,只管向服務(wù)員說(shuō)來(lái)四個(gè)雞翅就行了。麥當(dāng)勞和肯德基就是生產(chǎn)雞翅的Factory

            工廠模式:客戶類和工廠類分開(kāi)。消費(fèi)者任何時(shí)候需要某種產(chǎn)品,只需向工廠請(qǐng)求即可。消費(fèi)者無(wú)須修改就可以接納新產(chǎn)品。缺點(diǎn)是當(dāng)產(chǎn)品修改時(shí),工廠類也要做相應(yīng)的修改。如:如何創(chuàng)建及如何向客戶端提供。

            2
            BUILDER —MM最愛(ài)聽(tīng)的就是我愛(ài)你這句話了,見(jiàn)到不同地方的MM,要能夠用她們的方言跟她說(shuō)這句話哦,我有一個(gè)多種語(yǔ)言翻譯機(jī),上面每種語(yǔ)言都有一個(gè)按鍵,見(jiàn)到MM我只要按對(duì)應(yīng)的鍵,它就能夠用相應(yīng)的語(yǔ)言說(shuō)出我愛(ài)你這句話了,國(guó)外的MM也可以輕松搞掂,這就是我的我愛(ài)你”builder。(這一定比美軍在伊拉克用的翻譯機(jī)好賣)

            建造模式:將產(chǎn)品的內(nèi)部表象和產(chǎn)品的生成過(guò)程分割開(kāi)來(lái),從而使一個(gè)建造過(guò)程生成具有不同的內(nèi)部表象的產(chǎn)品對(duì)象。建造模式使得產(chǎn)品內(nèi)部表象可以獨(dú)立的變化,客戶不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)。建造模式可以強(qiáng)制實(shí)行一種分步驟進(jìn)行的建造過(guò)程。

            3
            FACTORY METHOD請(qǐng)MM去麥當(dāng)勞吃漢堡,不同的MM有不同的口味,要每個(gè)都記住是一件煩人的事情,我一般采用Factory Method模式,帶著MM到服務(wù)員那兒,說(shuō)要一個(gè)漢堡,具體要什么樣的漢堡呢,讓MM直接跟服務(wù)員說(shuō)就行了。

            工廠方法模式:核心工廠類不再負(fù)責(zé)所有產(chǎn)品的創(chuàng)建,而是將具體創(chuàng)建的工作交給子類去做,成為一個(gè)抽象工廠角色,僅負(fù)責(zé)給出具體工廠類必須實(shí)現(xiàn)的接口,而不接觸哪一個(gè)產(chǎn)品類應(yīng)當(dāng)被實(shí)例化這種細(xì)節(jié)。

            4
            PROTOTYPEMMQQ聊天,一定要說(shuō)些深情的話語(yǔ)了,我搜集了好多肉麻的情話,需要時(shí)只要copy出來(lái)放到QQ里面就行了,這就是我的情話prototype了。(100塊錢一份,你要不要)

            原始模型模式:通過(guò)給出一個(gè)原型對(duì)象來(lái)指明所要?jiǎng)?chuàng)建的對(duì)象的類型,然后用復(fù)制這個(gè)原型對(duì)象的方法創(chuàng)建出更多同類型的對(duì)象。原始模型模式允許動(dòng)態(tài)的增加或減少產(chǎn)品類,產(chǎn)品類不需要非得有任何事先確定的等級(jí)結(jié)構(gòu),原始模型模式適用于任何的等級(jí)結(jié)構(gòu)。缺點(diǎn)是每一個(gè)類都必須配備一個(gè)克隆方法。

            5
            SINGLETON俺有6個(gè)漂亮的老婆,她們的老公都是我,我就是我們家里的老公Sigleton,她們只要說(shuō)道老公,都是指的同一個(gè)人,那就是我(剛才做了個(gè)夢(mèng)啦,哪有這么好的事)

            單例模式:?jiǎn)卫J酱_保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例單例模式。單例模式只應(yīng)在有真正的單一實(shí)例的需求時(shí)才可使用。

            結(jié)構(gòu)型模式

            6
            ADAPTER在朋友聚會(huì)上碰到了一個(gè)美女Sarah,從香港來(lái)的,可我不會(huì)說(shuō)粵語(yǔ),她不會(huì)說(shuō)普通話,只好求助于我的朋友kent了,他作為我和Sarah之間的Adapter,讓我和Sarah可以相互交談了(也不知道他會(huì)不會(huì)耍我)

            適配器模式:把一個(gè)類的接口變換成客戶端所期待的另一種接口,從而使原本因接口原因不匹配而無(wú)法一起工作的兩個(gè)類能夠一起工作。適配類可以根據(jù)參數(shù)返還一個(gè)合適的實(shí)例給客戶端。

            7
            BRIDGE 早上碰到MM,要說(shuō)早上好,晚上碰到MM,要說(shuō)晚上好;碰到MM穿了件新衣服,要說(shuō)你的衣服好漂亮哦,碰到MM新做的發(fā)型,要說(shuō)你的頭發(fā)好漂亮哦。不要問(wèn)我早上碰到MM新做了個(gè)發(fā)型怎么說(shuō)這種問(wèn)題,自己用BRIDGE組合一下不就行了

            橋梁模式:將抽象化與實(shí)現(xiàn)化脫耦,使得二者可以獨(dú)立的變化,也就是說(shuō)將他們之間的強(qiáng)關(guān)聯(lián)變成弱關(guān)聯(lián),也就是指在一個(gè)軟件系統(tǒng)的抽象化和實(shí)現(xiàn)化之間使用組合/聚合關(guān)系而不是繼承關(guān)系,從而使兩者可以獨(dú)立的變化。

            8
            COMPOSITE —Mary今天過(guò)生日。我過(guò)生日,你要送我一件禮物。”“嗯,好吧,去商店,你自己挑。”“這件T恤挺漂亮,買,這條裙子好看,買,這個(gè)包也不錯(cuò),買。”“喂,買了三件了呀,我只答應(yīng)送一件禮物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻煩你包起來(lái)。”“……”MM都會(huì)用Composite模式了,你會(huì)了沒(méi)有?

            合成模式:合成模式將對(duì)象組織到樹(shù)結(jié)構(gòu)中,可以用來(lái)描述整體與部分的關(guān)系。合成模式就是一個(gè)處理對(duì)象的樹(shù)結(jié)構(gòu)的模式。合成模式把部分與整體的關(guān)系用樹(shù)結(jié)構(gòu)表示出來(lái)。合成模式使得客戶端把一個(gè)個(gè)單獨(dú)的成分對(duì)象和由他們復(fù)合而成的合成對(duì)象同等看待。

            9
            DECORATOR —Mary過(guò)完輪到Sarly過(guò)生日,還是不要叫她自己挑了,不然這個(gè)月伙食費(fèi)肯定玩完,拿出我去年在華山頂上照的照片,在背面寫上最好的的禮物,就是愛(ài)你的Fita”,再到街上禮品店買了個(gè)像框(賣禮品的MM也很漂亮哦),再找隔壁搞美術(shù)設(shè)計(jì)的Mike設(shè)計(jì)了一個(gè)漂亮的盒子裝起來(lái)……,我們都是Decorator,最終都在修飾我這個(gè)人呀,怎么樣,看懂了嗎?

            裝飾模式:裝飾模式以對(duì)客戶端透明的方式擴(kuò)展對(duì)象的功能,是繼承關(guān)系的一個(gè)替代方案,提供比繼承更多的靈活性。動(dòng)態(tài)給一個(gè)對(duì)象增加功能,這些功能可以再動(dòng)態(tài)的撤消。增加由一些基本功能的排列組合而產(chǎn)生的非常大量的功能。

            10
            FAÇADE我有一個(gè)專業(yè)的Nikon相機(jī),我就喜歡自己手動(dòng)調(diào)光圈、快門,這樣照出來(lái)的照片才專業(yè),但MM可不懂這些,教了半天也不會(huì)。幸好相機(jī)有Facade設(shè)計(jì)模式,把相機(jī)調(diào)整到自動(dòng)檔,只要對(duì)準(zhǔn)目標(biāo)按快門就行了,一切由相機(jī)自動(dòng)調(diào)整,這樣MM也可以用這個(gè)相機(jī)給我拍張照片了。

            門面模式:外部與一個(gè)子系統(tǒng)的通信必須通過(guò)一個(gè)統(tǒng)一的門面對(duì)象進(jìn)行。門面模式提供一個(gè)高層次的接口,使得子系統(tǒng)更易于使用。每一個(gè)子系統(tǒng)只有一個(gè)門面類,而且此門面類只有一個(gè)實(shí)例,也就是說(shuō)它是一個(gè)單例模式。但整個(gè)系統(tǒng)可以有多個(gè)門面類。

            11
            FLYWEIGHT每天跟MM發(fā)短信,手指都累死了,最近買了個(gè)新手機(jī),可以把一些常用的句子存在手機(jī)里,要用的時(shí)候,直接拿出來(lái),在前面加上MM的名字就可以發(fā)送了,再不用一個(gè)字一個(gè)字敲了。共享的句子就是FlyweightMM的名字就是提取出來(lái)的外部特征,根據(jù)上下文情況使用。

            享元模式FLYWEIGHT在拳擊比賽中指最輕量級(jí)。享元模式以共享的方式高效的支持大量的細(xì)粒度對(duì)象。享元模式能做到共享的關(guān)鍵是區(qū)分內(nèi)蘊(yùn)狀態(tài)和外蘊(yùn)狀態(tài)。內(nèi)蘊(yùn)狀態(tài)存儲(chǔ)在享元內(nèi)部,不會(huì)隨環(huán)境的改變而有所不同。外蘊(yùn)狀態(tài)是隨環(huán)境的改變而改變的。外蘊(yùn)狀態(tài)不能影響內(nèi)蘊(yùn)狀態(tài),它們是相互獨(dú)立的。將可以共享的狀態(tài)和不可以共享的狀態(tài)從常規(guī)類中區(qū)分開(kāi)來(lái),將不可以共享的狀態(tài)從類里剔除出去。客戶端不可以直接創(chuàng)建被共享的對(duì)象,而應(yīng)當(dāng)使用一個(gè)工廠對(duì)象負(fù)責(zé)創(chuàng)建被共享的對(duì)象。享元模式大幅度的降低內(nèi)存中對(duì)象的數(shù)量。

            12
            PROXY MM在網(wǎng)上聊天,一開(kāi)頭總是“hi,你好”,“你從哪兒來(lái)呀?”“你多大了?”“身高多少呀?這些話,真煩人,寫個(gè)程序做為我的Proxy吧,凡是接收到這些話都設(shè)置好了自動(dòng)的回答,接收到其他的話時(shí)再通知我回答,怎么樣,酷吧。

            代理模式:代理模式給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)源對(duì)象的引用。代理就是一個(gè)人或一個(gè)機(jī)構(gòu)代表另一個(gè)人或者一個(gè)機(jī)構(gòu)采取行動(dòng)。某些情況下,客戶不想或者不能夠直接引用一個(gè)對(duì)象,代理對(duì)象可以在客戶和目標(biāo)對(duì)象直接起到中介的作用。客戶端分辨不出代理主題對(duì)象與真實(shí)主題對(duì)象。代理模式可以并不知道真正的被代理對(duì)象,而僅僅持有一個(gè)被代理對(duì)象的接口,這時(shí)候代理對(duì)象不能夠創(chuàng)建被代理對(duì)象,被代理對(duì)象必須有系統(tǒng)的其他角色代為創(chuàng)建并傳入。

            行為模式

            13
            CHAIN OF RESPONSIBLEITY晚上去上英語(yǔ)課,為了好開(kāi)溜坐到了最后一排,哇,前面坐了好幾個(gè)漂亮的MM哎,找張紙條,寫上“Hi,可以做我的女朋友嗎?如果不愿意請(qǐng)向前傳,紙條就一個(gè)接一個(gè)的傳上去了,糟糕,傳到第一排的MM把紙條傳給老師了,聽(tīng)說(shuō)是個(gè)老處女呀,快跑!

            責(zé)任鏈模式:在責(zé)任鏈模式中,很多對(duì)象由每一個(gè)對(duì)象對(duì)其下家的引用而接起來(lái)形成一條鏈。請(qǐng)求在這個(gè)鏈上傳遞,直到鏈上的某一個(gè)對(duì)象決定處理此請(qǐng)求。客戶并不知道鏈上的哪一個(gè)對(duì)象最終處理這個(gè)請(qǐng)求,系統(tǒng)可以在不影響客戶端的情況下動(dòng)態(tài)的重新組織鏈和分配責(zé)任。處理者有兩個(gè)選擇:承擔(dān)責(zé)任或者把責(zé)任推給下家。一個(gè)請(qǐng)求可以最終不被任何接收端對(duì)象所接受。

            14
            COMMAND俺有一個(gè)MM家里管得特別嚴(yán),沒(méi)法見(jiàn)面,只好借助于她弟弟在我們倆之間傳送信息,她對(duì)我有什么指示,就寫一張紙條讓她弟弟帶給我。這不,她弟弟又傳送過(guò)來(lái)一個(gè)COMMAND,為了感謝他,我請(qǐng)他吃了碗雜醬面,哪知道他說(shuō):我同時(shí)給我姐姐三個(gè)男朋友送COMMAND,就數(shù)你最小氣,才請(qǐng)我吃面。

            命令模式:命令模式把一個(gè)請(qǐng)求或者操作封裝到一個(gè)對(duì)象中。命令模式把發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分割開(kāi),委派給不同的對(duì)象。命令模式允許請(qǐng)求的一方和發(fā)送的一方獨(dú)立開(kāi)來(lái),使得請(qǐng)求的一方不必知道接收請(qǐng)求的一方的接口,更不必知道請(qǐng)求是怎么被接收,以及操作是否執(zhí)行,何時(shí)被執(zhí)行以及是怎么被執(zhí)行的。系統(tǒng)支持命令的撤消。

            15
            INTERPRETER俺有一個(gè)《泡MM真經(jīng)》,上面有各種泡MM的攻略,比如說(shuō)去吃西餐的步驟、去看電影的方法等等,跟MM約會(huì)時(shí),只要做一個(gè)Interpreter,照著上面的腳本執(zhí)行就可以了。

            解釋器模式:給定一個(gè)語(yǔ)言后,解釋器模式可以定義出其文法的一種表示,并同時(shí)提供一個(gè)解釋器。客戶端可以使用這個(gè)解釋器來(lái)解釋這個(gè)語(yǔ)言中的句子。解釋器模式將描述怎樣在有了一個(gè)簡(jiǎn)單的文法后,使用模式設(shè)計(jì)解釋這些語(yǔ)句。在解釋器模式里面提到的語(yǔ)言是指任何解釋器對(duì)象能夠解釋的任何組合。在解釋器模式中需要定義一個(gè)代表文法的命令類的等級(jí)結(jié)構(gòu),也就是一系列的組合規(guī)則。每一個(gè)命令對(duì)象都有一個(gè)解釋方法,代表對(duì)命令對(duì)象的解釋。命令對(duì)象的等級(jí)結(jié)構(gòu)中的對(duì)象的任何排列組合都是一個(gè)語(yǔ)言。

            16
            ITERATOR我愛(ài)上了Mary,不顧一切的向她求婚。
               Mary
            想要我跟你結(jié)婚,得答應(yīng)我的條件
              
            我:什么條件我都答應(yīng),你說(shuō)吧
               Mary
            我看上了那個(gè)一克拉的鉆石
              
            我:我買,我買,還有嗎?
               Mary
            我看上了湖邊的那棟別墅
              
            我:我買,我買,還有嗎?
               Mary
            你的小弟弟必須要有50cm長(zhǎng)
              
            我腦袋嗡的一聲,坐在椅子上,一咬牙:我剪,我剪,還有嗎?
               ……

            迭代子模式:迭代子模式可以順序訪問(wèn)一個(gè)聚集中的元素而不必暴露聚集的內(nèi)部表象。多個(gè)對(duì)象聚在一起形成的總體稱之為聚集,聚集對(duì)象是能夠包容一組對(duì)象的容器對(duì)象。迭代子模式將迭代邏輯封裝到一個(gè)獨(dú)立的子對(duì)象中,從而與聚集本身隔開(kāi)。迭代子模式簡(jiǎn)化了聚集的界面。每一個(gè)聚集對(duì)象都可以有一個(gè)或一個(gè)以上的迭代子對(duì)象,每一個(gè)迭代子的迭代狀態(tài)可以是彼此獨(dú)立的。迭代算法可以獨(dú)立于聚集角色變化。

            17
            MEDIATOR 四個(gè)MM打麻將,相互之間誰(shuí)應(yīng)該給誰(shuí)多少錢算不清楚了,幸虧當(dāng)時(shí)我在旁邊,按照各自的籌碼數(shù)算錢,賺了錢的從我這里拿,賠了錢的也付給我,一切就OK啦,俺得到了四個(gè)MM的電話。

            調(diào)停者模式:調(diào)停者模式包裝了一系列對(duì)象相互作用的方式,使得這些對(duì)象不必相互明顯作用。從而使他們可以松散偶合。當(dāng)某些對(duì)象之間的作用發(fā)生改變時(shí),不會(huì)立即影響其他的一些對(duì)象之間的作用。保證這些作用可以彼此獨(dú)立的變化。調(diào)停者模式將多對(duì)多的相互作用轉(zhuǎn)化為一對(duì)多的相互作用。調(diào)停者模式將對(duì)象的行為和協(xié)作抽象化,把對(duì)象在小尺度的行為上與其他對(duì)象的相互作用分開(kāi)處理。

            18
            MEMENTO同時(shí)跟幾個(gè)MM聊天時(shí),一定要記清楚剛才跟MM說(shuō)了些什么話,不然MM發(fā)現(xiàn)了會(huì)不高興的哦,幸虧我有個(gè)備忘錄,剛才與哪個(gè)MM說(shuō)了什么話我都拷貝一份放到備忘錄里面保存,這樣可以隨時(shí)察看以前的記錄啦。

            備忘錄模式:備忘錄對(duì)象是一個(gè)用來(lái)存儲(chǔ)另外一個(gè)對(duì)象內(nèi)部狀態(tài)的快照的對(duì)象。備忘錄模式的用意是在不破壞封裝的條件下,將一個(gè)對(duì)象的狀態(tài)捉住,并外部化,存儲(chǔ)起來(lái),從而可以在將來(lái)合適的時(shí)候把這個(gè)對(duì)象還原到存儲(chǔ)起來(lái)的狀態(tài)。

            19
            OBSERVER —想知道咱們公司最新MM情報(bào)嗎?加入公司的MM情報(bào)郵件組就行了,tom負(fù)責(zé)搜集情報(bào),他發(fā)現(xiàn)的新情報(bào)不用一個(gè)一個(gè)通知我們,直接發(fā)布給郵件組,我們作為訂閱者(觀察者)就可以及時(shí)收到情報(bào)啦

            觀察者模式:觀察者模式定義了一種一隊(duì)多的依賴關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽(tīng)某一個(gè)主題對(duì)象。這個(gè)主題對(duì)象在狀態(tài)上發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,使他們能夠自動(dòng)更新自己。

            20
            STATEMM交往時(shí),一定要注意她的狀態(tài)哦,在不同的狀態(tài)時(shí)她的行為會(huì)有不同,比如你約她今天晚上去看電影,對(duì)你沒(méi)興趣的MM就會(huì)說(shuō)有事情啦,對(duì)你不討厭但還沒(méi)喜歡上的MM就會(huì)說(shuō)好啊,不過(guò)可以帶上我同事么?,已經(jīng)喜歡上你的MM就會(huì)說(shuō)幾點(diǎn)鐘?看完電影再去泡吧怎么樣?,當(dāng)然你看電影過(guò)程中表現(xiàn)良好的話,也可以把MM的狀態(tài)從不討厭不喜歡變成喜歡哦。

            狀態(tài)模式:狀態(tài)模式允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變的時(shí)候改變行為。這個(gè)對(duì)象看上去象是改變了它的類一樣。狀態(tài)模式把所研究的對(duì)象的行為包裝在不同的狀態(tài)對(duì)象里,每一個(gè)狀態(tài)對(duì)象都屬于一個(gè)抽象狀態(tài)類的一個(gè)子類。狀態(tài)模式的意圖是讓一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變的時(shí)候,其行為也隨之改變。狀態(tài)模式需要對(duì)每一個(gè)系統(tǒng)可能取得的狀態(tài)創(chuàng)立一個(gè)狀態(tài)類的子類。當(dāng)系統(tǒng)的狀態(tài)變化時(shí),系統(tǒng)便改變所選的子類。

            21
            STRATEGY跟不同類型的MM約會(huì),要用不同的策略,有的請(qǐng)電影比較好,有的則去吃小吃效果不錯(cuò),有的去海邊浪漫最合適,單目的都是為了得到MM的芳心,我的追MM錦囊中有好多Strategy哦。

            策略模式:策略模式針對(duì)一組算法,將每一個(gè)算法封裝到具有共同接口的獨(dú)立的類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶端的情況下發(fā)生變化。策略模式把行為和環(huán)境分開(kāi)。環(huán)境類負(fù)責(zé)維持和查詢行為類,各種算法在具體的策略類中提供。由于算法和環(huán)境獨(dú)立開(kāi)來(lái),算法的增減,修改都不會(huì)影響到環(huán)境和客戶端。

            22
            TEMPLATE METHOD ——看過(guò)《如何說(shuō)服女生上床》這部經(jīng)典文章嗎?女生從認(rèn)識(shí)到上床的不變的步驟分為巧遇、打破僵局、展開(kāi)追求、接吻、前戲、動(dòng)手、愛(ài)撫、進(jìn)去八大步驟(Template method),但每個(gè)步驟針對(duì)不同的情況,都有不一樣的做法,這就要看你隨機(jī)應(yīng)變啦(具體實(shí)現(xiàn))

            模板方法模式:模板方法模式準(zhǔn)備一個(gè)抽象類,將部分邏輯以具體方法以及具體構(gòu)造子的形式實(shí)現(xiàn),然后聲明一些抽象方法來(lái)迫使子類實(shí)現(xiàn)剩余的邏輯。不同的子類可以以不同的方式實(shí)現(xiàn)這些抽象方法,從而對(duì)剩余的邏輯有不同的實(shí)現(xiàn)。先制定一個(gè)頂級(jí)邏輯框架,而將邏輯的細(xì)節(jié)留給具體的子類去實(shí)現(xiàn)。

            23
            VISITOR情人節(jié)到了,要給每個(gè)MM送一束鮮花和一張卡片,可是每個(gè)MM送的花都要針對(duì)她個(gè)人的特點(diǎn),每張卡片也要根據(jù)個(gè)人的特點(diǎn)來(lái)挑,我一個(gè)人哪搞得清楚,還是找花店老板和禮品店老板做一下Visitor,讓花店老板根據(jù)MM的特點(diǎn)選一束花,讓禮品店老板也根據(jù)每個(gè)人特點(diǎn)選一張卡,這樣就輕松多了;

            訪問(wèn)者模式:訪問(wèn)者模式的目的是封裝一些施加于某種數(shù)據(jù)結(jié)構(gòu)元素之上的操作。一旦這些操作需要修改的話,接受這個(gè)操作的數(shù)據(jù)結(jié)構(gòu)可以保持不變。訪問(wèn)者模式適用于數(shù)據(jù)結(jié)構(gòu)相對(duì)未定的系統(tǒng),它把數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作之間的耦合解脫開(kāi),使得操作集合可以相對(duì)自由的演化。訪問(wèn)者模式使得增加新的操作變的很容易,就是增加一個(gè)新的訪問(wèn)者類。訪問(wèn)者模式將有關(guān)的行為集中到一個(gè)訪問(wèn)者對(duì)象中,而不是分散到一個(gè)個(gè)的節(jié)點(diǎn)類中。當(dāng)使用訪問(wèn)者模式時(shí),要將盡可能多的對(duì)象瀏覽邏輯放在訪問(wèn)者類中,而不是放到它的子類中。訪問(wèn)者模式可以跨過(guò)幾個(gè)類的等級(jí)結(jié)構(gòu)訪問(wèn)屬于不同的等級(jí)結(jié)構(gòu)的成員類。

            posted @ 2007-12-05 20:35 李亞 閱讀(270) | 評(píng)論 (0)編輯 收藏

            .net中清除EXCEL進(jìn)程的方法

                    最近用C#寫winform,將EXCEL文件中的數(shù)據(jù)寫入數(shù)據(jù)庫(kù)中,將DataGrid中的數(shù)據(jù)導(dǎo)出為EXCEL格式。最后發(fā)現(xiàn)EXCEL內(nèi)存泄漏,在應(yīng)用程序不退出的情況下,總是有一個(gè)EXCEL進(jìn)程不能清除,下面這個(gè)方法可以解決問(wèn)題:

            1、對(duì)excel操作做成一個(gè)函數(shù),然后調(diào)用此函數(shù)。
            在函數(shù)中調(diào)用GC.Collect();無(wú)用,因?yàn)镚C不回收調(diào)用自己的那一段代碼塊!

            2、在函數(shù)的下面調(diào)用GC.Collect();語(yǔ)句。你會(huì)發(fā)現(xiàn)EXCEL進(jìn)程沒(méi)有了!
            例如:

            private void Import() 
            {
                 Excel.Application myExcel  
            = new Excel.Application();
                 myExcel.Workbooks.Add(openFileDialog1.FileName);
                
            //..
               
            //讀取EXCEL文件,導(dǎo)入到數(shù)據(jù)庫(kù).
               
            //清除excel垃圾進(jìn)程
               myExcel.Workbooks.Close(); 
               myExcel.Quit(); 
               System.Runtime.InteropServices.Marshal.ReleaseComObject(myExcel);
               myExcel 
            = null;
            }
             
            private void ExcelImport() {
               Import();
               GC.Collect();
             }


            2.去調(diào)用ExcelImport()這個(gè)方法就可以了!

            posted @ 2007-12-05 20:33 李亞 閱讀(542) | 評(píng)論 (2)編輯 收藏

            正則表達(dá)式簡(jiǎn)單資料

              正則表達(dá)式是使用一套特殊符號(hào)模式做為表達(dá)格式的字符串,主要用處是描述和解析文本。許多程序員(甚至一些不錯(cuò)的高手)都無(wú)視(也不用)正則表達(dá)式,我認(rèn)為這是一個(gè)恥辱,因?yàn)樵诮鉀Q很多問(wèn)題的時(shí)候,正則表達(dá)式常常讓我們有得心應(yīng)手的感覺(jué)。一旦你掌握了,就會(huì)發(fā)現(xiàn)它能解決無(wú)數(shù)真實(shí)世界的問(wèn)題。

              正則表達(dá)式的工作方式就象Windows或者*nix系統(tǒng)里面的文件名替代符 - 你可以使用特定的*或者?來(lái)指定一系列文件。但是使用正則表達(dá)式的特殊字符或者metacharacters(元字符)來(lái)表示這類事情會(huì)更準(zhǔn)確。

              正則表達(dá)式把大多數(shù)字符當(dāng)作直接字符,就好像正則表達(dá)式 mike,將只會(huì)匹配按順序的字符序列m - i - k - e。與此同時(shí)正則表達(dá)式使用一個(gè)采用元字符的擴(kuò)展集合,可以表示非常復(fù)雜的文字匹配。

              認(rèn)識(shí)元字符: ^[](){}.*?\|+$ 以及在某些時(shí)候出現(xiàn)的 -
            我知道它們看上去很恐怖,但是一旦你了解它們就會(huì)知道它們是很可愛(ài)的符號(hào)。

              行定位點(diǎn): ‘^’ 和 ‘$’
            ‘^’ (讀成:caret) 和 ‘$’ (讀成:dollar) 這兩個(gè)元字符分別代表一行文字的開(kāi)始和結(jié)束。就象我前面舉的例子,正則表達(dá)式mike會(huì)匹配字符序列m - i - k – e,可是它會(huì)匹配一行中的所有位置 (比如,它會(huì)匹配 “I’m mike”或者 “carmike”)。 ‘^’字符被用來(lái)限定匹配行的開(kāi)始,因此^mike 將只會(huì)尋找以mike開(kāi)始的行。同樣,表達(dá)式mike$將只會(huì)尋找m - i - k - e在一行末尾的(當(dāng)然還是會(huì)匹配 ‘carmike’)。

              如果我們聯(lián)合使用這兩個(gè)行定位點(diǎn)字符,我們可以搜索在多行文字中尋找包含的特殊字符串序列。比如:表達(dá)式 ^mike$ 將只會(huì)匹配占有單獨(dú)一行的單詞mike,一個(gè)字不多一個(gè)字不少。同樣,表達(dá)式 ^$ 對(duì)于發(fā)現(xiàn)空行(一行開(kāi)始就是本行結(jié)束的那種)很有用。

              字符分類: ‘[]’
              一對(duì)方括號(hào)被稱為一個(gè)字符分類, 你可以用來(lái)匹配任何一個(gè)或多個(gè)字符。假設(shè)你想匹配單詞 ‘gray’,同時(shí)也想找一下被拼寫成 ‘grey’的單詞。 使用一個(gè)字符分類將允許你匹配這兩者 -- 正則表達(dá)式 gr[ea]y 被解讀成 “匹配這樣的字符串 - 一個(gè)g, 跟著是r, 跟著或者是一個(gè)e或者是一個(gè)a, 跟著一個(gè)y”。

              如果你用 [^ ... ] 代替 [ ... ], 這個(gè)分類將匹配后面列出來(lái)字符以外的任何字符。首字符 ^ 表示“否定"列表 - 不同于你列出所有希望包含的字符,你是去列出所有不想包含的字符。 注意在這里使用的^ (caret) 字符,它在字符分類方式之外使用表示另外的意思 - 用來(lái)匹配文字行的開(kāi)始(見(jiàn)文章前面部分)。

              字符分類中的元字符: ‘-’
              在一個(gè)字符分類中,字符分類中的元字符 ‘-’ (dash) 用來(lái)指出一個(gè)字符范圍。考慮字符分類 [01234567890abcdefABCDEF],采用’-’的話我們可以這樣寫[0-9a-fA-F],方便了不少吧。有一點(diǎn)大家要注意的,這個(gè)’-’符號(hào)只有用一個(gè)字符分類中才被認(rèn)為是元字符,在其他位置,它只是簡(jiǎn)單的匹配普通的’-’字符,沒(méi)有任何其他意義。

              但是且慢,我看到有人舉手質(zhì)疑。假如在一個(gè)字符分類里面,’-’字符做為第一個(gè)字符出現(xiàn)的時(shí)候,會(huì)把它認(rèn)為成什么呢?比如[-A-F],問(wèn)題很好,注意:這是一個(gè)例外,如果在字符分類中,’-’字符是第一個(gè)出現(xiàn)的字符,那我們把它當(dāng)作普通字符而不是元字符處理(因?yàn)閷?shí)際上它不可能表示一個(gè)字符范圍,范圍需要有開(kāi)始和結(jié)束字符),這個(gè)時(shí)候它只會(huì)匹配一個(gè)普通的’-’字符。引申開(kāi)來(lái),我們?cè)僬f(shuō)一個(gè)例外:S’?’和’.’在大多數(shù)情況下都是正則表達(dá)式的元字符,但是有個(gè)例外是在字符分類中,當(dāng)它們?cè)谧址诸愔械臅r(shí)候(比如在:[-0-9.?],它們只是代表一個(gè)普通字符,唯一的特殊字符(元字符)是0和9中間的’-’)。

              用一個(gè)句點(diǎn): ‘.’匹配任何字符
              ‘.’ 元字符(一般讀成a dot 或者point)是一種匹配任何字符的寫法。在你想在一個(gè)字符串的指定位置匹配一個(gè)任意字符的時(shí)候,它顯得非常可愛(ài)。再?gòu)?qiáng)調(diào)一遍,在字符分類中,’.’就不是一個(gè)元字符了。到現(xiàn)在為止,你開(kāi)始看出一些門道來(lái)了吧?哪些是元字符哪些不是元字符在字符分類里面和外面是不一樣的。

              選擇性元字符: ‘|’
              ‘|’ 元字符(讀成pipe)的意思是“or”。它允許你把多個(gè)表達(dá)式合成到一個(gè)表達(dá)式,然后匹配里面任何單個(gè)表達(dá)式的結(jié)果。這些子表達(dá)式被稱為備選項(xiàng)。

              例如:Mike 和 Michael 是兩個(gè)獨(dú)立的正則表達(dá)式,但是Mike|Michael 這樣來(lái)寫的話,這個(gè)正則表達(dá)式匹配任意一個(gè)單詞。

              圓括號(hào)在這里可以被用來(lái)限制備選的范圍。我們可以使用圓括號(hào)來(lái)達(dá)到和上面這個(gè)正則表達(dá)式同樣的目的,同時(shí)縮短它長(zhǎng)度,正則表達(dá)式Mi(ke|chael) 同樣匹配Mike或者M(jìn)ichael。當(dāng)然,在實(shí)際程序中我還是會(huì)用第一種寫法,雖然長(zhǎng)了一點(diǎn),可是更容易理解,因此也更容易維護(hù)。

              匹配可選項(xiàng): ‘?’
              ‘?’ 元字符(讀成:question mark)意味著可選。它放在正則表達(dá)式的某個(gè)位置的一個(gè)字符后面,這個(gè)字符允許在匹配結(jié)果中出現(xiàn),也可以不出現(xiàn)。當(dāng)然,我們可以肯定的是:這個(gè)’?’字符只能跟在一個(gè)普通字符而不是元字符后面。

              如果我想匹配英式或者美式拼法的單詞‘flavor’ ,我會(huì)用正則表達(dá)式flavou?r,它被解讀成:“匹配一個(gè)字符串:f,跟著一個(gè)l,跟著一個(gè)a,跟著一個(gè)v,跟著一個(gè)o,跟著一個(gè)可選的u,跟著一個(gè)r”。

              數(shù)量符號(hào): ‘+’ and ‘*’
              象’?’字符一樣,‘+’ (讀成plus)和‘*’(讀成star)元字符影響前導(dǎo)字符(就是在這個(gè)符號(hào)前面的字符)可以在匹配字符串中出現(xiàn)的數(shù)量 (使用前面說(shuō)的‘?’的話,相當(dāng)于前導(dǎo)字符可以出現(xiàn)0次或一次)。元字符‘+’ 匹配前面出現(xiàn)的項(xiàng)目一次或更多次,而‘*’ 則表示匹配任何次,包括0次。

              如果我想通過(guò)在一場(chǎng)足球比賽中解說(shuō)員說(shuō)’goal’的聲音次數(shù)來(lái)統(tǒng)計(jì)比分的話,我應(yīng)該用正則表達(dá)式go+al, 它可以匹配‘goal’,也可以匹配一些激情主播的‘gooooooooooooooooal’ (但肯定不會(huì)是 ‘gal’)。

              前面的三個(gè)元字符:’?’、’+’、’*’一般又叫做計(jì)量符。因?yàn)樗鼈冇绊懬懊骓?xiàng)目的數(shù)量。

              數(shù)量范圍: ‘{}’
              ‘{最小, 最大}’ 這個(gè)元字符序列允許你指定特定項(xiàng)目可以被匹配的最少和最大次數(shù)。例如go{1,5}al 可以用來(lái)限制我們上面的例子,只匹配1到5次o。同樣的{0,1} 其實(shí)就等同于一個(gè)’?’元字符。

              轉(zhuǎn)義字符: ‘\’
              ‘\’ 元字符(讀成:backslash)被用來(lái)轉(zhuǎn)換指定的元字符的含義,以便于你可以把它們當(dāng)成普通字符來(lái)匹配。例如,你打算匹配字符’?’或者’\’,你就可以在它們前面加上一個(gè)’\’字符,這樣它們就被轉(zhuǎn)換成普通字符的含義,就好像這樣寫:‘\?’ or ‘\\’.

              如果在一個(gè)非元字符前面使用’\’的話,那么根據(jù)你使用正則表達(dá)式的語(yǔ)言不同,會(huì)有不同的含義,必須參閱相應(yīng)的手冊(cè)。比較普遍采用的是perl兼容的正則表達(dá)式(PCREs),你可以在這里查看the perldoc page for perl regular expressions. PCREs用得非常普遍,在PHP、 Ruby和ECMAScript/Javascript還有很多語(yǔ)言中都可以使用。

              用圓括號(hào)匹配: ‘()’
              大部分正則表達(dá)式工具允許你用圓括號(hào)設(shè)定一個(gè)特定的表達(dá)式子集。比如,我們可以用一個(gè)正則表達(dá)式http://([^/]+)去匹配一個(gè)URL的域名部分。下面讓我們把這個(gè)正則表達(dá)式分解開(kāi),看看它是如何工作的。

              這個(gè)表達(dá)式的起始部分非常直白:它必須匹配“h - t - t - p - : - / - /”這樣的字符序列。這個(gè)初始序列之后就是圓括號(hào)了,它被用來(lái)捕捉符合它們包圍的子表達(dá)式的字符。在現(xiàn)在的例子中,子表達(dá)式是‘[^/]+’,用上面學(xué)到的知識(shí),我們知道它實(shí)際上是匹配除了‘/’字符以外的任何字符一次到多次。對(duì)于一個(gè)像是 http://immike.net/blog/Some-blog-post的URL,‘immike.net’ 將會(huì)被這個(gè)圓括號(hào)里面的表達(dá)式所匹配。

            posted @ 2007-12-05 20:24 李亞 閱讀(452) | 評(píng)論 (1)編輯 收藏

            添加上下文菜單的方法

            首先要在在文件首定義菜單項(xiàng):
            #define ID_MENU_EDIT   5001 
            #define ID_MENU_DELETE 
            5002
            然后添加對(duì)話框的WM_CONTEXTMENU消息函數(shù),函數(shù)內(nèi)容為:
                CMenu menuPopup;
                
            if(menuPopup.CreatePopupMenu())
                {
                     menuPopup.AppendMenu(MF_STRING,ID_MENU_EDIT,
            "修改(&E)");
                     menuPopup.AppendMenu(MF_STRING,ID_MENU_DELETE,
            "刪除(&D)");
                     menuPopup.TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this);    
                }
            然后定義菜單相應(yīng)函數(shù),

            1,在頭文件中添加函數(shù)定義語(yǔ)句:

            // Generated message map functions
             
            //{{AFX_MSG(CAdo2Dlg)
             virtual BOOL OnInitDialog();
             afx_msg void onInfoEdit(); 
            //  這個(gè)是編輯菜單的響應(yīng)函數(shù)
             afx_msg void onInfoDelete();  
            //這個(gè)是刪除菜單的響應(yīng)函數(shù)
             afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
             afx_msg void OnPaint();
             afx_msg HCURSOR OnQueryDragIcon();
             afx_msg void OnButton1();
             afx_msg void OnButton2();
             afx_msg void OnRdblclkList1(NMHDR
            * pNMHDR, LRESULT* pResult);
             afx_msg void OnDblclkList1(NMHDR
            * pNMHDR, LRESULT* pResult);
             afx_msg void OnContextMenu(CWnd
            * pWnd, CPoint point);
             
            //}}AFX_MSG
             DECLARE_MESSAGE_MAP()

             

            2,在cpp文件中添加函數(shù)體:

            void CAdo2Dlg::OnInfoEdit() 
            {
                AfxMessageBox(
            "edit");
            }

            void CAdo2Dlg::OnInfoDelete() 
            {
                AfxMessageBox(
            "delete");
            }

             

            3,然后在cpp文件中添加影射:

            BEGIN_MESSAGE_MAP(CAdo2Dlg, CDialog)
                
            //{{AFX_MSG_MAP(CAdo2Dlg)
                ON_COMMAND(ID_MENU_EDIT,    OnInfoEdit)
                ON_COMMAND(ID_MENU_DELETE,  OnInfoDelete)
                
            //}}AFX_MSG_MAP
            END_MESSAGE_MAP()

             

            所有的工作完成了!

            posted @ 2007-12-05 20:19 李亞 閱讀(334) | 評(píng)論 (0)編輯 收藏

            VC動(dòng)態(tài)鏈接庫(kù)的創(chuàng)建和使用

            一,MFC擴(kuò)展DLL
            創(chuàng)建:
            1,新建一個(gè)MFC擴(kuò)展DLL ,名字為dll5,添加頭文件,名為dll5
            2,頭文件中加入:
            extern __declspec(dllexport) CString concatA(CString x,CString y);
            3,在cpp文件中加入:
            extern __declspec(dllexport) CString concatA(CString x,CString y)
            {
             return x + y;
            }
            4,在cpp文件中加入:
            #include "dll5.h"

            5,編譯,生成dll
            使用:
            1,新建一個(gè)單文檔應(yīng)用程序,名為Usedll5
            2,將剛才生成的dll5.lib文件和dll5.h文件拷貝到當(dāng)前應(yīng)用程序路徑下,
              將dll5.dll 文件拷貝到 當(dāng)前應(yīng)用程序下的debug下
            3,在當(dāng)前應(yīng)用程序中用到該dll5的導(dǎo)出方法(concatA)的文件(或類)上添加如下語(yǔ)句:
            #include "dll5.h"
            假設(shè)將其加到 Usedll5View.cpp中。
            4,在Usedll5View類中建立消息映射入口,在消息函數(shù)中添加如下語(yǔ)句:
            CString a=concatA("中國(guó)北車集團(tuán)","長(zhǎng)春軌道客車股份有限公司");
            MessageBox(a);
            5,在 工程/設(shè)置/連接/對(duì)象庫(kù)/模塊 中加入:dll5.lib

            6,編譯執(zhí)行該應(yīng)用程序,并觸發(fā)該消息,則輸出:

            中國(guó)北車集團(tuán)長(zhǎng)春軌道客車股份有限公司

            之后只要定義不更改,函數(shù)體無(wú)論怎么更改。我們只要將編譯好的dll拷貝過(guò)來(lái)即可。如果定義有了修改,則需要將h文件和lib 文件拷貝過(guò)來(lái),并需要重新編譯。

             

            二,動(dòng)態(tài)鏈接庫(kù)使用共享MFC DLL
            創(chuàng)建:
            1,新建一個(gè) DLL(選 動(dòng)態(tài)鏈接庫(kù)使用共享MFC DLL)
            2,頭文件中加入:
            _declspec(dllexport) CString WINAPI concatA(CString x,CString y);
            3,在cpp文件末尾加入:
            _declspec(dllexport) CString WINAPI concatA(CString x,CString y)
            {
             return x + y;
            }
            4,編譯,生成dll
            使用:
            1,新建一個(gè)單文檔應(yīng)用程序,名為Usedll8
            2,將剛才生成的dll8.lib文件拷貝到當(dāng)前應(yīng)用程序路徑下,
              將dll8.dll 文件拷貝到 c:\winnt\system32下
            3,在當(dāng)前應(yīng)用程序中用到該dll5的導(dǎo)出方法(concatA)的 類的頭文件上添加如下語(yǔ)句:
            extern CString WINAPI concatA(CString x,CString y);
            假設(shè)將其加到 Usedll8View.h中。
            4,在Usedll8View類中建立消息映射入口,在消息函數(shù)中添加如下語(yǔ)句:
            CString a=concatA("中國(guó)北車集團(tuán)","長(zhǎng)春軌道客車股份有限公司");
            MessageBox(a);
            5,在 工程/設(shè)置/連接/對(duì)象庫(kù)/模塊 中加入:dll8.lib
            6,編譯執(zhí)行該應(yīng)用程序,并觸發(fā)該消息,則輸出:

            中國(guó)北車集團(tuán)長(zhǎng)春軌道客車股份有限公司

            posted @ 2007-12-05 20:18 李亞 閱讀(1744) | 評(píng)論 (0)編輯 收藏

            VSS 6.0d,VSS 2005,TFS的簡(jiǎn)單比較

             

            現(xiàn)在有三種比較常用微軟Source Control 工具:VSS 6.0dVSS 2005TFS(Team Foundation Server),做個(gè)簡(jiǎn)單的比較。

                整體功能上,前兩者屬于小型的管理工具,一般在15個(gè)人以下的開(kāi)發(fā)團(tuán)隊(duì)中使用比較合適,適合小型的公司或者團(tuán)隊(duì)使用。Team Foundation Server Visual Studio Team System 產(chǎn)品線發(fā)布的最后一個(gè)組件。它為團(tuán)隊(duì)項(xiàng)目開(kāi)發(fā)提供了高度集成化的工作平臺(tái),可以在大規(guī)模的團(tuán)隊(duì)開(kāi)發(fā)中使用,不僅可以提供源代碼管理功能,還可以提供其他的功能.

            1. VSS 6.0d是比較早期的工具,最大支持2GB的數(shù)據(jù)空間,但是不能支持遠(yuǎn)程訪問(wèn)。

            2. VSS 2005是可以支持最大4GB的數(shù)據(jù)空間,可以提供遠(yuǎn)程訪問(wèn),支持多人簽出。

            3TFS是企業(yè)級(jí)的,支持50個(gè)人以上的團(tuán)隊(duì),但是要求必須在Windows 2003 sp1,Sql Server 2005的基礎(chǔ)之上,強(qiáng)烈建議干凈的安裝環(huán)境。

            posted @ 2007-12-05 20:00 李亞 閱讀(2392) | 評(píng)論 (0)編輯 收藏

            移動(dòng)回訪電話...

            聲明:
            1.本人男
            2.下面的事情是真實(shí)的事情,前天下午四點(diǎn)多發(fā)生
            --------------------------------------------------------------------
            前天下午,
            我接到移動(dòng)的10086打電話回訪,
            我都"喂"好幾聲了,移動(dòng)的那位MM竟然說(shuō)“你是李小姐嗎。。。”,
            當(dāng)時(shí)我差點(diǎn)沒(méi)暈倒,崩潰掉...
            估計(jì)那位MM 也超汗!

            posted @ 2007-12-04 07:59 李亞 閱讀(368) | 評(píng)論 (0)編輯 收藏

            僅列出標(biāo)題
            共4頁(yè): 1 2 3 4 
            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            導(dǎo)航

            統(tǒng)計(jì)

            • 隨筆 - 32
            • 文章 - 0
            • 評(píng)論 - 5
            • 引用 - 0

            公告

            這世界并不會(huì)在意你的自尊,這世界指望你在自我感覺(jué)良好之前先要有所成就!

            常用鏈接

            留言簿(3)

            隨筆分類(32)

            隨筆檔案(32)

            相冊(cè)

            最新隨筆

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            伊人久久精品影院| 麻豆AV一区二区三区久久 | 国产精品免费福利久久| 国产精品免费久久久久电影网| 精品久久久一二三区| 欧美伊香蕉久久综合类网站| 波多野结衣久久精品| 国产99久久久久久免费看| 亚洲国产另类久久久精品黑人| 欧美久久亚洲精品| 日韩一区二区久久久久久| 亚洲精品无码久久久久久| 麻豆久久| 国产香蕉97碰碰久久人人| 久久超乳爆乳中文字幕| 久久精品国产久精国产果冻传媒 | 久久久久久久综合日本| 精品国产福利久久久| 久久人妻少妇嫩草AV无码专区| 久久久久久久综合狠狠综合| 久久电影网| 精品99久久aaa一级毛片| 狠狠色噜噜狠狠狠狠狠色综合久久| 久久夜色精品国产噜噜麻豆| 伊人久久大香线蕉综合5g| 久久亚洲av无码精品浪潮| 狠狠人妻久久久久久综合蜜桃| 国产欧美一区二区久久| 国内精品久久久久久99| 久久一日本道色综合久久| 久久精品国产AV一区二区三区| 色狠狠久久综合网| 亚洲精品tv久久久久| 香蕉久久永久视频| 一本一本久久a久久精品综合麻豆| 久久免费大片| 香蕉久久永久视频| 欧美亚洲国产精品久久| 久久人人爽人人爽人人片AV不 | 伊人久久大香线蕉成人| 日韩亚洲国产综合久久久|