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

            思勤無邪

            上學(xué)時,因我年齡最小,個頭也最小,上課時,就像大猩猩堆里的猴一般。如今,這猴偶爾也把最近的一些情況寫在這里。

               :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
              132 Posts :: 1 Stories :: 178 Comments :: 0 Trackbacks

            公告

                 吾日常三省吾身,曰思、曰勤、曰無邪。

            積分與排名

            • 積分 - 183678
            • 排名 - 141

            最新隨筆

            最新評論

            閱讀排行榜

            評論排行榜

            ?

            摘一段以前的文章,原文作者:茍建兵 清華大學(xué)熱能系(北京,100084)。原文很長,這里只摘了一段。

            采用MFC開發(fā)Windows程序之所以能夠大幅度提高開發(fā)速度和效率主要是因為MFC在類
            層次封裝了大量Windows SDK函數(shù)和典型Windows應(yīng)用的缺省處理,這樣,用戶只需
            要較少的編程就可以實現(xiàn)自己的開發(fā)任務(wù)。如果在MFC基礎(chǔ)上再配合Visual C++提供
            的AppWizard、ClassWizard和AppStudio工具那么更可以大幅度加快開發(fā)進程。MFC
            提供大量的基類供程序員使用,常見的如CWinApp類、CFrameWnd類、CMDIFrameWnd
            類、CMDIChildWnd類、CView類、CDC類和CDocument類等等。通過從這些基類中派生
            出用戶自己的類,然后重載特殊的幾個函數(shù)就可以生成一個獨立的應(yīng)用程序。可以
            說,采用MFC編寫Windows應(yīng)用程序是非常方便的,雖然其學(xué)習(xí)過程并不簡單,但是
            其提供的靈活高效性足以使任何Windows程序開發(fā)人員為之付出努力。如果用戶不曾
            使用過MFC,那么用戶可以通過附錄中所列的參考書去學(xué)習(xí)MFC的強大功能。
            采用MFC應(yīng)用框架產(chǎn)生的應(yīng)用程序使用了標(biāo)準(zhǔn)化的結(jié)構(gòu),因而使得采用MFC編寫的程
            序的在不同平臺上的移植變得非常容易,事實上,MFC的16位和32位版本之間差別很
            小。MFC提供的標(biāo)準(zhǔn)化結(jié)構(gòu)是經(jīng)過眾多專家分析調(diào)研后總結(jié)編寫出來的,一般情況下
            可以滿足絕大多數(shù)用戶的要求,但有時用戶也可以通過重載一些函數(shù)來修改其缺省
            的風(fēng)格從而實現(xiàn)自己特有的風(fēng)格,如自定義應(yīng)用圖表和灰色背景等。在MFC提供的文
            檔視結(jié)構(gòu)中,文檔、視和資源之間的聯(lián)系是通過定義文檔模板來實現(xiàn)的,如:
            m_pSimuTemplate = new CMultiDocTemplate(
            IDR_SIMUTYPE,
            RUNTIME_CLASS(CSimuDoc),
            RUNTIME_CLASS(CMyChild), // Derived MDI child frame
            RUNTIME_CLASS(CSimuView));
            上中第一項IDR_SIMUTYPE就包括了視口的菜單,加速鍵和圖表等資源,如果用戶使
            用AppWizard來產(chǎn)生的應(yīng)用基本框架,那么其也同時產(chǎn)生了缺省的圖標(biāo),如果用戶不
            滿意缺省圖標(biāo)(實際上用戶很少滿足于缺省圖標(biāo)),只需要將缺省圖標(biāo)刪除,然后
            編輯或者直接引入一個新的圖標(biāo),在存儲這一圖標(biāo)時只需要使用與被刪除圖標(biāo)同樣
            的ID即可實現(xiàn)替代。
            熟悉Windows程序開發(fā)的人都知道,在Windows上通過使用灰色背景可以增強應(yīng)用程
            序的視覺效果,曾有人戲稱,灰色是圖形界面永恒的顏色。使用MFC產(chǎn)生的應(yīng)用程序
            的背景缺省為白色,如果用戶希望改變成灰色或者其它顏色,那就需要使用單獨處
            理,解決的辦法很多,如在每次視口的OnPaint()事件中采用灰色刷子人為填充背
            景,但是這不是最好的辦法。筆者發(fā)現(xiàn)最好的辦法就是采用AfxRegisterWndClass()
            函數(shù)注冊一個使用灰色背景刷的新的窗口類,這需要重載PreCreateWindow()函數(shù)來
            實現(xiàn)這一點,如下程序代碼片段所示:
            BOOL CSimuView::PreCreateWindow(CREATESTRUCT& cs)
            {
            HBRUSH hbkbrush=CreateSolidBrush(RGB(192,192,192));//創(chuàng)建灰色背景刷
            LPCSTR lpMyOwnClass=AfxRegisterWndClass(CS_HREDRAW
            |CS_VREDRAW|CS_OWNDC,0,hbkbrush);//注冊新類
            cs.lpszClass=lpMyOwnClass;//修改缺省的類風(fēng)格
            return TRUE;
            }
            采用這種方法速度最快,也最省力。同時,還可以在PreCreateWindow()函數(shù)定義所
            希望的任何窗口風(fēng)格,如窗口大小,光標(biāo)式樣等。

            使用單文檔-多視結(jié)構(gòu)

            如果用戶使用過MFC進行編程,那么就會發(fā)現(xiàn)借助于AppWizard基于MFC無論編寫SDI
            (單文檔界面)還是編寫MDI(多文檔界面)都是十分方便的。MDI應(yīng)用程序目前使用越
            來越普遍,人們熟悉的Microsoft公司的Office系列產(chǎn)品以及Visual系列產(chǎn)品都是典
            型的多文檔應(yīng)用程序。這種多文檔界面具有多窗口的特點,因而人們可以在一個程
            序中使用多個子窗口來實現(xiàn)不同數(shù)據(jù)的瀏覽查看。如果用戶要實現(xiàn)在MDI各個窗口之
            間針對同一數(shù)據(jù)進行不同的可視化就是一件比較麻煩的事情。值得慶幸的是,MFC提
            供的文檔-視結(jié)構(gòu)大大簡化了這一工作。文檔-視結(jié)構(gòu)通過將數(shù)據(jù)從用戶對數(shù)據(jù)的觀
            察中分離出來,從而方便實現(xiàn)多視,亦即多個視口針對同一數(shù)據(jù),如果一個視口中
            數(shù)據(jù)發(fā)生改變,那么其它相關(guān)視口中的內(nèi)容也會隨之發(fā)生改變以反映數(shù)據(jù)的變化。
            SDI和MDI這兩種Windows標(biāo)準(zhǔn)應(yīng)用程序框架并不是總能滿足用戶的需要,就作者的工
            作而言,就特別需要一種被稱為單文檔多視的應(yīng)用程序,英文可以縮寫為SDMV。通
            過SDMV應(yīng)用我們可以利用文檔類來統(tǒng)一管理應(yīng)用程序的所有數(shù)據(jù),同時需要采用多
            窗口以多種方式來可視化這些的數(shù)據(jù),如棒圖,趨勢圖和參數(shù)列表,從而方便用戶
            從不同角度來觀察數(shù)據(jù)。MDI雖然具有多窗口的特點,但是其為多文檔,即通常情況
            下,一個視口對應(yīng)一個文檔,視口+文檔便構(gòu)成一個子窗口。在各個子窗口之間數(shù)據(jù)
            相互獨立,如果要保持?jǐn)?shù)據(jù)同步更新就需要采用特殊的技術(shù)了,采用這種方式既費
            時又費力。通過筆者的實踐發(fā)現(xiàn),利用MFC本身提供的多視概念通過適當(dāng)改造MDI窗
            口應(yīng)用程序就可以實現(xiàn)上述SDMV結(jié)構(gòu)。
            所謂SDMV應(yīng)用程序本質(zhì)上仍然是一個MDI應(yīng)用程序,只是在程序中我們?nèi)藶榭刂剖蛊?br />只能生成一個文檔類,這個文檔在第一個視口創(chuàng)建時創(chuàng)建,注意,這里并不需要限
            制各個視口的創(chuàng)建先后順序。此后與MDI窗口固有特性不同的是,所有新創(chuàng)建的子窗
            口都不再創(chuàng)建獨立文檔,而是把該新視口直接連接到已有的文檔對象上,這樣就使
            其成為單文檔多視的結(jié)構(gòu),所有相關(guān)數(shù)據(jù)都存儲在文檔對象中,一旦文擋中數(shù)據(jù)發(fā)
            生改變,通過UpdateAllViews()函數(shù)通知所有相關(guān)視口,各個視口就可以在
            OnUpdate()中相應(yīng)數(shù)據(jù)的變化。這種響應(yīng)機制如下圖所示:

            圖 1 文檔-視結(jié)構(gòu)數(shù)據(jù)更新機制
            由于MDI本質(zhì)上并不是為這種單文檔多視機制服務(wù)的,因而在實際應(yīng)用時需要解決一
            些問題。
            1、窗口標(biāo)題問題
            窗口標(biāo)題本來不應(yīng)該成為問題,缺省情況下MDI窗口通過在文檔模板中提供的資源ID
            所提供的對應(yīng)字符串來確定窗口標(biāo)題。但是對于SDMV應(yīng)用,由于各個視口實質(zhì)上是
            對應(yīng)于同一個文擋,因此每個視口都具有相同標(biāo)題,只不過增加了一個數(shù)據(jù)用于指
            示這是第幾個視口。如果在各個視口中指明具體的窗口名字,那么由不同的視口啟
            動創(chuàng)建文檔產(chǎn)生的窗口標(biāo)題就不同,這個名字會影響到后繼視口。為了作到不同類
            型的視口如棒圖視口和曲線視口具有不同的標(biāo)題,這就需要一定的技術(shù)處理。根據(jù)
            筆者的摸索發(fā)現(xiàn)可以采用如下步驟實現(xiàn):
            首先在從標(biāo)準(zhǔn)的MDI子窗口基類CMDIChildWnd派生一個自己的子窗口類,姑且命名為
            CMyChild,然后在其成員變量中增加一個CString型變量用以存儲當(dāng)前窗口標(biāo)題:
            CString winTitle;
            然后在不同的視口創(chuàng)建過程中通過獲取父窗口指針按自己的意愿對上述變量進行賦
            值,程序片段如下:
            pChild=(CMyChild*)GetParent();
            pChild->winTitle="棒圖顯示窗口";
            最后在CMyChild派生類中重載CMDIChildWnd基類中的OnUpdateFrameTitle()函數(shù)來
            強制實現(xiàn)窗口標(biāo)題的個性化,這一函數(shù)在各種類庫手冊上和聯(lián)機幫助中都沒有,但
            的確有這樣一個具有保護屬性的函數(shù)用來實現(xiàn)窗口標(biāo)題的更新操作,這可以從MFC類
            庫的源代碼中找到該函數(shù)的實現(xiàn)。重載后的源代碼如下:
            void CMyChild::OnUpdateFrameTitle(BOOL bAddToTitle)
            {
            // update our parent window first
            GetMDIFrame()->OnUpdateFrameTitle(bAddToTitle);

            if ((GetStyle() & FWS_ADDTOTITLE) == 0)
            return; // leave child window alone!

            CDocument* pDocument = GetActiveDocument();
            if (bAddToTitle && pDocument != NULL)
            {
            char szOld[256];
            GetWindowText(szOld, sizeof(szOld));
            char szText[256];

            lstrcpy(szText,winTitle); //Modified by author!
            if (m_nWindow > 0)
            wsprintf(szText + lstrlen(szText), ":%d", m_nWindow);

            // set title if changed, but don't remove completely
            if (lstrcmp(szText, szOld) != 0)
            SetWindowText(szText);
            }
            }
            2、如何創(chuàng)建SDMV應(yīng)用
            如何創(chuàng)建SDMV應(yīng)用比較麻煩,下面通過舉例來具體說明。該例子假設(shè)用戶需要建棒
            圖類型和曲線形式的兩種視口,假設(shè)用戶已經(jīng)利用CView基類派生并且實現(xiàn)了這兩個
            類,分別對應(yīng)于CMyChart和CMyTraceView兩個類。
            1) 在應(yīng)用類(從CWinApp派生出來的類)的頭文件中加入下列變量和函數(shù)原型說
            明:
            CMultiDocTemplate* m_pMyTraceTemplate;
            CMultiDocTemplate* m_pMyChartTemplate;
            int ExitInstance();
            2) 在應(yīng)用類的InitInstance成員函數(shù)中刪除對AddDocTemplate函數(shù)的調(diào)用和
            OpenFileNew()語句,并且加入如下代碼:
            m_pMyTraceTemplate = new CMultiDocTemplate(
            IDR_MYTRACEVIEW,
            RUNTIME_CLASS(CSimuDoc),
            RUNTIME_CLASS(CMyChild), // Derived MDI child frame
            RUNTIME_CLASS(CMyTraceView));

            m_pMyChartTemplate = new CMultiDocTemplate(
            IDR_MYCHART,
            RUNTIME_CLASS(CSimuDoc),
            RUNTIME_CLASS(CMyChild), // Derived MDI child frame
            RUNTIME_CLASS(CMyChart));
            3) 實現(xiàn)ExitInstance()函數(shù),在其中刪除所用的兩個輔助模板:
            int CTestApp::ExitInstance()
            {
            if(m_pMyChartTemplate) delete m_pMyChartTemplate;
            if(m_pMyTraceTemplate) delete m_pMyTraceTemplate;
            return TRUE;
            }
            4) 在菜單資源中去掉File菜單中的New和Open項,加入New Chart View和New
            Trace View兩項,在對應(yīng)的菜單命令中實現(xiàn)如下:
            void CMainFrame::OnNewMychart()
            {
            // TODO: Add your command handler code here
            OnNewView(((CSimuApp*)AfxGetApp())->m_pMyChartTemplate);
            }
            void CMainFrame::OnNewMyTrace()
            {
            // TODO: Add your command handler code here
            OnNewView(((CSimuApp*)AfxGetApp())->m_pMyTraceTemplate);
            }
            上中OnNewView的實現(xiàn)如下:
            BOOL CMainFrame::OnNewView(CMultiDocTemplate* pDocTemplate)
            {
            CMDIChildWnd* pActiveChild = MDIGetActive();
            CDocument* pDocument;
            if (pActiveChild == NULL ||
            (pDocument = pActiveChild->GetActiveDocument()) == NULL)
            {
            TRACE0("Now New the specify view\n");
            ASSERT(pDocTemplate != NULL);
            ASSERT(pDocTemplate->IsKindOf(RUNTIME_CLASS(CDocTemplate)));
            pDocTemplate->OpenDocumentFile(NULL);
            return TRUE;
            }

            // otherwise we have a new frame to the same document!
            CMultiDocTemplate* pTemplate = pDocTemplate;
            ASSERT_VALID(pTemplate);
            CFrameWnd* pFrame = pTemplate->CreateNewFrame(pDocument, pActiveChild);
            if (pFrame == NULL)
            {
            TRACE0("Warning: failed to create new frame\n");
            return FALSE; // command failed
            }
            pTemplate->InitialUpdateFrame(pFrame, pDocument);
            return TRUE;
            }
            OnNewView是整個SDMV應(yīng)用的核心組成,它的任務(wù)是創(chuàng)建一個新的指定類型的視口,
            它首先判斷是否有活動視口存在,文檔是否已經(jīng)創(chuàng)建,正常情況下活動視口存在則
            表明文檔存在,如果不存在則利用所指定的文檔模板創(chuàng)建一個新的活動視口,否則
            則只創(chuàng)建視口,同時將其連接到已存在的文檔對象上。
            通過以上步驟就可以實現(xiàn)SDMV應(yīng)用,在其后的具體應(yīng)用中利用文檔對象的
            UpdateAllViews()函數(shù)和視口的OnUpdate()函數(shù)就可以很好的工作了。

            使用DDE服務(wù)

            Windows 3.x是一個分時多任務(wù)操作環(huán)境,在此環(huán)境下,多個應(yīng)用程序可以并發(fā)地執(zhí)
            行。為了在并發(fā)執(zhí)行的多個任務(wù)之間共享數(shù)據(jù)和資源,Windows 提供了幾種機制,
            主要是通過剪貼板(Clipboard)和動態(tài)數(shù)據(jù)交換(Dynamic Data Exchange)。前者對
            于用戶需要直接參與的數(shù)據(jù)交換來說,是一個非常方便的工具,但是如果希望數(shù)據(jù)
            交換自動進行時就必須依靠DDE技術(shù)了。編寫DDE應(yīng)用的技術(shù)也發(fā)展了好幾代,從最
            初的基于消息的DDE到基于DDEML(動態(tài)數(shù)據(jù)交換管理庫),再到現(xiàn)在流行的OLE技
            術(shù)。DDE技術(shù)的發(fā)展使得程序開發(fā)人員編寫DDE應(yīng)用更為簡潔。從發(fā)展趨勢來看,基
            于OLE的數(shù)據(jù)交換是最好的,它特別符合當(dāng)今軟件領(lǐng)域的客戶-服務(wù)器機制
            (Client-Server)。為適應(yīng)多平臺和Internet的需要,在OLE基礎(chǔ)上微軟又開發(fā)了
            ActiveX技術(shù)。但是不容忽視的是,基于傳統(tǒng)的DDE數(shù)據(jù)交換也自有它的應(yīng)用空間,
            使用仍然廣泛。目前在Windows 3.x下,基于OLE的遠程數(shù)據(jù)交換還很不成熟,但是
            在WFW(Windows for Workgroup)下基于網(wǎng)絡(luò)動態(tài)數(shù)據(jù)交換的技術(shù)卻很成熟,目前也
            應(yīng)用非常普遍。關(guān)于DDE應(yīng)用的開發(fā)和NetDDE的應(yīng)用可以參看附錄7。
            1、回調(diào)函數(shù)的處理
            由于DDEML機制需要使用回調(diào)函數(shù),因此使用DDEML的關(guān)鍵是解決在MFC編程體系中回
            調(diào)函數(shù)的使用。回調(diào)函數(shù)(Callback function)大量用于Windows的系統(tǒng)服務(wù),通過
            它,程序員可以安裝設(shè)備驅(qū)動程序和消息過濾系統(tǒng),以控制Windows的有效使用。
            許多程序員都發(fā)現(xiàn),利用MFC或者其它的C++應(yīng)用編寫回調(diào)函數(shù)是非常麻煩的,其根
            本原因是回調(diào)函數(shù)是基于C編程的Windows SDK的技術(shù),不是針對C++的,程序員可以
            將一個C函數(shù)直接作為回調(diào)函數(shù),但是如果試圖直接使用C++的成員函數(shù)作為回調(diào)函
            數(shù)將發(fā)生錯誤,甚至編譯就不能通過。通過查詢資料發(fā)現(xiàn),其錯誤是普通的C++成員
            函數(shù)都隱含了一個傳遞函數(shù)作為參數(shù),亦即“this”指針,C++通過傳遞一個指向自
            身的指針給其成員函數(shù)從而實現(xiàn)程序函數(shù)可以訪問C++的數(shù)據(jù)成員。這也可以理解為
            什么C++類的多個實例可以共享成員函數(shù)但是確有不同的數(shù)據(jù)成員。由于this指針的
            作用,使得將一個CALLBACK型的成員函數(shù)作為回調(diào)函數(shù)安裝時就會因為隱含的this
            指針使得函數(shù)參數(shù)個數(shù)不匹配,從而導(dǎo)致回調(diào)函數(shù)安裝失敗。要解決這一問題的關(guān)
            鍵就是不讓this指針起作用,通過采用以下兩種典型技術(shù)可以解決在C++中使用回調(diào)
            函數(shù)所遇到的問題。這種方法具有通用性,適合于任何C++。
            1. 不使用成員函數(shù),直接使用普通C函數(shù),為了實現(xiàn)在C函數(shù)中可以訪問類的成員變
            量,可以使用友元操作符(friend),在C++中將該C函數(shù)說明為類的友元即可。這種
            處理機制與普通的C編程中使用回調(diào)函數(shù)一樣。
            2. 使用靜態(tài)成員函數(shù),靜態(tài)成員函數(shù)不使用this指針作為隱含參數(shù),這樣就可以作
            為回調(diào)函數(shù)了。靜態(tài)成員函數(shù)具有兩大特點:其一,可以在沒有類實例的情況下使
            用;其二,只能訪問靜態(tài)成員變量和靜態(tài)成員函數(shù),不能訪問非靜態(tài)成員變量和非
            靜態(tài)成員函數(shù)。由于在C++中使用類成員函數(shù)作為回調(diào)函數(shù)的目的就是為了訪問所有
            的成員變量和成員函數(shù),如果作不到這一點將不具有實際意義。解決的辦法也很簡
            單,就是使用一個靜態(tài)類指針作為類成員,通過在類創(chuàng)建時初始化該靜態(tài)指針,如
            pThis=this,然后在回調(diào)函數(shù)中通過該靜態(tài)指針就可以訪問所有成員變量和成員函
            數(shù)了。這種處理辦法適用于只有一個類實例的情況,因為多個類實例將共享靜態(tài)類
            成員和靜態(tài)成員函數(shù),這就導(dǎo)致靜態(tài)指針指向最后創(chuàng)建的類實例。為了避免這種情
            況,可以使用回調(diào)函數(shù)的一個參數(shù)來傳遞this指針,從而實現(xiàn)數(shù)據(jù)成員共享。這種
            方法稍稍麻煩,這里就不再贅述。
            2、在MFC中使用DDEML
            對于典型的MFC應(yīng)用程序,主框架窗口類(CMainFrame)只有一個實例,因此可以使用
            靜態(tài)成員函數(shù)作為回調(diào)函數(shù),從而實現(xiàn)DDE機制。具體的代碼片段如下:
            (1) 在CMainFrame類中聲明如下靜態(tài)成員:
            static CMainFrame* pThis;
            static DWORD idInst;
            static HDDEDATA CALLBACK EXPORT DdeCallback(UINT,UINT,HCONV,HSZ,HSZ, HDDEDATA,DWORD,DWORD);
            (2) 在類的創(chuàng)建代碼(OnCreate())中作如下說明:
            pThis=this;
            lpDdeCallback=MakeProcInstance((FARPROC)DdeCallback,hInstance);
            if(DdeInitialize(&idInst,(PFNCALLBACK)lpDdeCallback,CBF_FAIL_EXECUTES
            |CBF_SKIP_REGISTRATIONS|CBF_SKIP_UNREGISTRATIONS,0L))
            {
            AfxMessageBox("不能初始化DDE服務(wù)","錯誤");
            DestroyWindow();
            }
            (3) 回調(diào)函數(shù)實現(xiàn)如下:
            HDDEDATA FAR PASCAL _export CMainFrame::DdeCallback(UINT iType,UINT iFmt, HCONV hConv,HSZ hsz1,HSZ hsz2,HDDEDATA hData,DWORD dwData1,DWORD dwData2)
            {
            char szBuffer[16];
            int i;

            switch(iType)
            {
            case XTYP_CONNECT: //hsz1=topiv, hsz2=service
            return (HDDEDATA)TRUE;//TRUE;
            case XTYP_ADVSTART: //hsz1=topic, hsz2=item
            case XTYP_REQUEST:
            case XTYP_ADVREQ:
            case XTYP_POKE: //hsz1=Topic, hsz2=item, hData=data
            case XTYP_ADVSTOP:
            return NULL;
            }
            }
            3、避免變量類型沖突
            如果在MFC應(yīng)用直接使用DDEML服務(wù),那么該MFC應(yīng)用在編譯時將會遇到變量類型HSZ
            重復(fù)定義錯誤。經(jīng)過追蹤發(fā)現(xiàn),錯誤在于在DDEML.H對HSZ作了如下定義:
            DECLARE_HANDLE32(HSZ);
            而在AFXEXT.H(通過stdafx.h引入)中對HSZ又作了如下說明:
            typedef BPSTR FAR* HSZ; // Long handle to a string
            兩個定義一個為32位整數(shù),一個為BASIC字符串指針,當(dāng)然會發(fā)生編譯器不能作變量
            類型轉(zhuǎn)換的錯誤。實際上,將HSZ聲明為BASIC字符串指針主要用于在MFC應(yīng)用中使用
            VBX控制。要改正這一錯誤,就必須保證不要在同一個代碼模塊中使用DDEML和VBX支
            持,通過將使用DDEML和VBX的代碼分開,并在使用DDEML代碼的模塊中最開頭定義如
            下編譯器宏就可以解決上述問題:
            #define NO_VBX_SUPPORT

            使用3D控制

            毫無疑問,3D控制的使用可以顯著提高Windows應(yīng)用程序的界面友好性,目前,許多
            流行的Windows應(yīng)用程序都使用了3D控制,典型的如Microsoft公司的Office系列軟
            件,而且,在Windows 95和Windows NT 4.0中,3D控制更是作為操作系統(tǒng)的一部分
            直接提供,這意味著在其上運行的軟件不需要作任何特殊處理,就具有3D界面效
            果,但是,很遺憾的是,在Windows 3.x中,除了命令按鈕控制使用3D控制以外,其
            余所有的控制,如編輯框,列表框,檢查框等都只使用2D控制,要想使用3D控制,
            程序設(shè)計人員就必須在自己的程序中作一定的修改,考慮到目前3D效果的流行,這
            點努力是值得的。
            為了支持3D效果,Microsoft公司提供了一個專門用于3D控制的動態(tài)連接庫,即
            CTL3D.DLL,但是在其Visual C++中卻沒有如何使用3D控制的討論,并且,Visual
            C++也不直接支持3D編碼,因為它不包括使用3D控制所必須的頭文件。但是,這并不
            意味著在Visual C++中不能使用3D控制,只不過用戶需要從其它地方獲取技術(shù)支持
            罷了。由于使用的是動態(tài)連接庫機制,因此,任何其它語言提供的3D頭文件和
            CTL3D.DLL的輸入庫都是可用的。作者使用的就是Borland公司的Borland C++中提供
            的CTL3D.H和CTL3D.LIB。在C/C++中使用3D控制的方法也有很多種,在這里,為節(jié)約
            篇幅,只討論與本文相關(guān)的主題,即使用MFC編程時如何使用3D控制。
            在MFC的所有對話框中使用3D控制可以遵循如下步驟:
            1. 在CWinApp::InitInstance函數(shù)中調(diào)用Ctl3dRegister和Ctl3dAutosubclass函
            數(shù):
            Ctl3dRegister(AfxGetInstanceHandle());
            Ctl3dAutoSubclass(AfxGetInstanceHandle());
            值得一提的是,在AppWizard產(chǎn)生的應(yīng)用框架的CWinApp::InitInstance中有一個函
            數(shù)調(diào)用為SetDialogBkColor,此函數(shù)的作用是將所有對話框的背景顏色設(shè)置為灰
            色,這個功能與3D界面實現(xiàn)相同的功能,可以移去此語句。
            由于CTL3D在初始化時讀入所有的系統(tǒng)顏色并自己維持,為了使應(yīng)用程序能夠正確反
            映系統(tǒng)顏色的變化,MFC應(yīng)用程序可以在WM_SYSCOLORCHANGE消息中調(diào)用
            Ctl3dColorChange函數(shù)。
            2. 在MFC應(yīng)用程序的CWinApp類中的ExitInstance函數(shù)中調(diào)用Ctl3dUnregister函
            數(shù),以方便Windows對CTL3D庫的正確管理。
            3. 在MFC應(yīng)用程序的項目文件中加入CTL3D.LIB(可以用IMPORT.EXE產(chǎn)生)。
            使用上述CTL3D的自動子類化的機制可以大大簡化使用3D控制,如果這不滿足你的要
            求,那么你就必須單獨在需要使用3D控制的對話框的OnInitDialog()中自行子類化
            相關(guān)的控制類了,典型的如下代碼片斷所示:
            BOOL CMyDialog::OnInitDialog()
            {
            Ctl3dSubclassDlgEx(m_hWnd,CTL3D_ALL);
            return TRUE;
            }
            上面講了在對話框中使用3D效果的辦法,如果用戶想在非對話框中使用3D控制,典
            型的在FormView導(dǎo)出類中使用,可以在導(dǎo)出類的OnInitialUpdate函數(shù)中進行適當(dāng)修
            改,修改的大小取決于你是否使用了3D控制的自動子類化機制。如果使用前面提到
            的自動子類化方法,那么僅需要在相應(yīng)的OnInitialUpdate函數(shù)中調(diào)用
            Ctl3dSubclassDlg函數(shù)了,如下代碼片斷所示:
            void CMyView::OnInitialUpdate()
            {
            Ctl3dSubclassDlg(m_hWnd,CTL3D_ALL);
            }
            否則,則需要修改如下:
            void CMyView::OnInitialUpdate()
            {
            Ctl3dSubclassDlgEx(m_hWnd,CTL3D_ALL);
            }

            使用自定義消息

            1、MFC的消息映射機制
            Windows是一個典型的消息驅(qū)動的操作系統(tǒng),程序的運行是靠對各種消息的響應(yīng)來實
            現(xiàn)的,這些消息的來源非常廣泛,既包括Windows系統(tǒng)本身,如WM_CLOSE、
            WM_PAINT、WM_CREATE和WM_TIMER等常用消息,又包括用戶菜單選擇、鍵盤加速
            鍵以及工具條和對話框按鈕等等,如果應(yīng)用程序要與其它程序協(xié)同工作,那么消息的來
            源還包括其它應(yīng)用程序發(fā)送的消息,串行口和并行口等硬件發(fā)送的消息等等。總
            之,Windows程序的開發(fā)是圍繞著對眾多消息的合理響應(yīng)和實現(xiàn)來實現(xiàn)程序的各種功
            能的。使用過C語言來開發(fā)Windows程序的人都知道,在Windows程序的窗口回調(diào)函數(shù)
            中需要安排Switch語句來響應(yīng)大量的消息,同時由于消息的間斷性使得不同的消息
            響應(yīng)之間信息的傳遞是通過大量的全局變量或者靜態(tài)數(shù)據(jù)來實現(xiàn)的。
            人們常用的兩種類庫OWL和MFC都提供了消息映射機制用以加速開發(fā)速度,使用者只
            需要按規(guī)定定義好對應(yīng)消息的處理函數(shù)自身即可,至于實際調(diào)用由類庫本身所提供
            的機制進行,或采用虛函數(shù),或采用消息映射宏。為了有效節(jié)約內(nèi)存,MFC并不大量
            采用虛函數(shù)機制,而是采用宏來將特定的消息映射到派生類中的響應(yīng)成員函數(shù)。這
            種機制不但適用于Windows自身的140條消息,而且適用于菜單命令消息和按鈕控制
            消息。MFC提供的消息映射機制是非常強大的,它允許在類的各個層次上對消息進行
            控制,而不簡單的局限于消息產(chǎn)生者本身。在應(yīng)用程序接收到窗口命令時,MFC將按
            如下次序?qū)ふ蚁鄳?yīng)的消息控制函數(shù):
            SDI應(yīng)用
            MDI應(yīng)用
            視口
            視口
            文檔
            文檔
            SDI主框架
            MDI子框架
            應(yīng)用
            MDI主框架

            應(yīng)用
            大多數(shù)應(yīng)用對每一個命令通常都只有一個特定的命令控制函數(shù),而這個命令控制函
            數(shù)也只屬于某一特定的類,但是如果在應(yīng)用中對同一消息有多個命令控制函數(shù),那
            么只有優(yōu)先級較高的命令控制函數(shù)才會被調(diào)用。為了簡化對常用命令的處理,MFC在
            基類中提供并實現(xiàn)了許多消息映射的入口,如打印命令,打印預(yù)覽命令,退出命令
            以及聯(lián)機幫助命令等,這樣在派生類中就繼承了所有的基類中的消息映射函數(shù),從
            而可以大大簡化編程。如果我們要在自己派生類中實現(xiàn)對消息的控制,那么必須在
            派生類中加上相應(yīng)的控制函數(shù)和映射入口。
            2、使用自己的消息
            在程序設(shè)計的更深層次,人們常常會發(fā)現(xiàn)只依賴于菜單和命令按鈕產(chǎn)生的消息是不
            夠的,常常因為程序運行的邏輯結(jié)構(gòu)和不同視口之間數(shù)據(jù)的同步而需要使用一些自
            定義的消息,這樣通過在相應(yīng)層次上安排消息響應(yīng)函數(shù)就可以實現(xiàn)自己的特殊需
            要。比如如果我們要在特定的時間間隔內(nèi)通知所有數(shù)據(jù)輸出視口重新取得新數(shù)據(jù),
            要依靠菜單命令和按鈕命令實現(xiàn)不夠理想,比較理想的解決辦法是采用定時器事件
            進行特定的計算操作,操作完成后再采用SendMessage發(fā)送自己的特定消息,只有當(dāng)
            這一消息得到處理后才會返回主控程序進行下一時間計算。通過在文檔層次上安排
            對消息的響應(yīng)取得最新計算數(shù)據(jù),而后通過UpdateAllViews()成員函數(shù)來通知所有
            相關(guān)視口更新數(shù)據(jù)的顯示。視口通過重載OnUpdate()成員函數(shù)就可以實現(xiàn)特定數(shù)據(jù)
            的更新顯示。
            如果用戶能夠熟練使用SendMessage()函數(shù)和PostMessage()函數(shù),那么要發(fā)送自定
            義消息并不難,通常有兩種選擇,其一是發(fā)送WM_COMMAND消息,通過消息的WORD
            wParam參數(shù)傳遞用戶的命令I(lǐng)D,舉例如下:
            SendMessage(WM_COMMAND,IDC_GETDATA,0); //MFC主框架發(fā)送
            然后在文檔層次上安排消息映射入口:
            ON_COMMAND(IDC_GETDATA, OnGetData)
            同時在文檔類中實現(xiàn)OnGetData()函數(shù):
            void CSimuDoc::OnGetData()
            {
            TRACE("Now in SimuDoc,From OnGetData\n");
            UpdateAllViews(NULL);
            }
            注意在上中的消息映射入口需要用戶手工加入,Visual C++提供的ClassWizard并不
            能替用戶完成這一工作。上中例子沒有使用PostMessage函數(shù)而使用SendMessage函
            數(shù)的原因是利用了SendMessage函數(shù)的特點,即它只有發(fā)送消息得到適當(dāng)處理后方才
            返回,這樣有助于程序控制。
            另一種發(fā)送自定義消息的辦法是直接發(fā)送命令I(lǐng)D,在控制層次上采用ON_MESSAGE來
            實現(xiàn)消息映射入口,注意這時的命令控制函數(shù)的原型根據(jù)Windows本身消息處理的規(guī)
            定必須如下:
            afx_msg LONG OnCaculationOnce(WPARAM wParam,LPARAM lParam);
            相對來講,這種機制不如上述機制簡單,也就不再贅述。

            使用不帶文擋-視結(jié)構(gòu)的MFC應(yīng)用

            文檔-視結(jié)構(gòu)的功能是非常強大的,可以適合于大多數(shù)應(yīng)用程序,但是有時我們只需
            要非常簡單的程序,為了減少最終可執(zhí)行文件尺寸和提高運行速度,我們沒有必要
            使用文擋-視結(jié)構(gòu),典型的有簡單SDI應(yīng)用和基于對話框的應(yīng)用。
            1、簡單SDI應(yīng)用
            此時只需要使用CWinApp和CFrameWnd兩個類就完全可以了。由于CWinApp類封裝了
            WinMain函數(shù)和消息處理循環(huán),因此任何使用MFC進行編程的程序都不能脫離開該
            類。實際上使用CWinApp類非常簡單,主要是派生一個用戶自己的應(yīng)用類,如
            CMyApp,然后只需重載CWinApp類的InitInstance()函數(shù):

            BOOL CMyApp::InitInstance()
            {
            m_pMainWnd=new CMainFrame();
            ASSERT(m_pMainWnd!=NULL); //error checking only
            m_pMainWnd->ShowWindow(m_nCmdShow);
            m_pMainWnd->UpdateWindow();
            return TRUE;
            }
            至于所需要的主框架類,則可以直接使用ClassWizard實用程序生成,該類的頭文件
            與實現(xiàn)代碼可以與CMyApp類的頭文件和實現(xiàn)代碼放在一起。注意,這里由一個技
            巧,由于ClassWizard的使用需要有相應(yīng)的CLW文件存在,而收工建代碼時沒有對應(yīng)
            的CLW文件,因此不能直接使用,解決辦法是進入App Studio實用工具后使用
            ClassWizard,此時系統(tǒng)會發(fā)覺不存在相應(yīng)的CLW文件,系統(tǒng)將提示你重建CLW文件并
            彈出相應(yīng)對話框,這時候你不需要選擇任何文件就直接選擇OK按鈕,這樣系統(tǒng)將為
            你產(chǎn)生一個空的CLW文件,這樣就可以使用ClassWizard實用工具了。為了將CWinApp
            和CFrameWnd的派生類有機地結(jié)合在一起,只需在CFrameWnd派生類的構(gòu)造函數(shù)中進
            行窗口創(chuàng)建即可。典型代碼如下:
            CMainFrame::CMainFrame()
            {
            Create(NULL,"DDE Client Application",WS_OVERLAPPEDWINDOW,rectDefault,
            NULL,MAKEINTRESOURCE(IDR_MAINFRAME));
            }
            采用ClassWizard實用程序生成相關(guān)類代碼后,所有的類的其它實現(xiàn)和維護就同普通
            由AppWizard實用程序產(chǎn)生的代碼一樣了。
            2、基于對話框的程序
            有些主要用于數(shù)據(jù)的輸入和輸出等的應(yīng)用在使用時沒有必要改變窗口大小,典型的
            如各種聯(lián)機注冊程序,這些使用對話框作為應(yīng)用的主界面就足夠了,而且開發(fā)此類
            應(yīng)用具有方便快捷的特點,代碼也比較短小,如果直接采用各種控制類生成所需要
            的控制就特別麻煩。在Visual C++ 4.x版本中使用AppWizard就可以直接生成基于對
            話框的應(yīng)用。在Visual 1.x中沒有此功能,因此這類應(yīng)用需要程序員自己實現(xiàn)。
            實際上使用MFC實現(xiàn)基于對話框的應(yīng)用非常簡單,同樣只使用兩個MFC類作為基類,
            這兩個類為CWinApp類和CDialog類。所使用的對話框主界面同樣可以先用App
            Studio編輯對話框界面,再使用ClassWizard產(chǎn)生相應(yīng)代碼框架,然后修改CMyApp類
            的聲明,增加一個該對話框類的成員變量m_Mydlg,最后修改CMyApp類的
            InitInstance()函數(shù)如下:
            BOOL CMyApp::InitInstance()
            {
            m_Mydlg.DoModal();
            return TRUE;
            }

            MFC應(yīng)用的人工優(yōu)化

            使用C/C++編寫Windows程序的優(yōu)點就是靈活高效,運行速度快,Visual C++編譯器
            本身的優(yōu)化工作相當(dāng)出色,但這并不等于不需要進行適當(dāng)?shù)娜斯?yōu)化,為了提高程
            序的運行速度,程序員可以從以下幾方面努力:
            1) 減少不必要的重復(fù)顯示
            相對來講,Windows的GDI操作是比較慢的,因此在程序中我們應(yīng)該盡可能地控制整
            個視口的顯示和更新,如果前后兩此數(shù)據(jù)不發(fā)生變化,那么就不要重新進行視口的
            GDI圖形操作,尤其對于背景圖顯示時非萬不得已時不要重繪,同時不要經(jīng)常五必要
            的刷新整個窗口。
            2) 在視口極小化時不要進行更新屏幕操作
            在窗口處于極小化時沒有必要繼續(xù)進行視口更新工作,這樣可以顯著提高速度。為
            此需要在子窗口一級捕獲上述信息(視口不能捕獲該類信息),再在視口中進行相
            應(yīng)操作。如下代碼片段所示:
            首先在子窗口類中添加如下程序段:
            void CMyChild::OnSysCommand(UINT nID,LPARAM lparam)
            {
            CMDIChildWnd::OnSysCommand(nID,lparam);
            if(nID==SC_MINIMIZE){
            RedrawFlag=0;
            }
            else
            RedrawFlag=1;
            }
            再在視口更新時中修改如下:
            void CMyChart::OnUpdate( CView* pSender, LPARAM lHint, CObject* pHint )
            {
            if(pChild->RedrawFlag)
            {
            InvalidateRect(&r,FALSE);
            TRACE("Now In CMyChart::OnUpdate\n");
            }
            }
            至于上中pChild指針可以在視口創(chuàng)建的例程中獲取:
            pChild=(CMyChild*)GetParent();
            3) 使用永久性的資源
            在頻繁進行GDI輸出的視口中,如在監(jiān)控軟件中常常使用的趨勢圖顯示和棒圖顯示等
            等,應(yīng)該考慮在類層次上建立頻繁使用的每種畫筆和刷子,這可以避免頻繁的在堆
            中創(chuàng)建和刪除GDI對象,從而提高速度。
            4) 使用自有設(shè)備描述句柄
            亦即在創(chuàng)建視口時通過指定WM_OWNDC風(fēng)格來擁有自己的顯示設(shè)備句柄,這雖然會多
            消耗一些內(nèi)存,一個DC大約占800字節(jié)的內(nèi)存,但是這避免了每次進行GDI操作前創(chuàng)
            建并合理初始化顯示設(shè)備句柄這些重復(fù)操作。特別是要自定義坐標(biāo)系統(tǒng)和使用特殊
            字體的視口這一點尤其重要。在16M機器日益普遍的今天為了節(jié)約一點點內(nèi)存而降低
            速度的做法并不可取。
            5) 優(yōu)化編譯時指定/G3選項和/FPix87選項
            /G3選項將強迫編譯器使用386處理器的處理代碼,使用嵌入式協(xié)處理器指令對那些
            頻繁進行浮點運算的程序很有幫助。采用這兩種編譯開關(guān)雖然提高了對用戶機型的
            要求,但在386逐漸被淘汰,486市場大幅度萎縮,586市場日益普及的今天上述問題
            已經(jīng)不再成為問題了。


            posted on 2006-07-11 09:54 思勤無邪 閱讀(1385) 評論(0)  編輯 收藏 引用 所屬分類: C++
            久久精品无码午夜福利理论片| 亚洲AⅤ优女AV综合久久久| 欧美久久一区二区三区| 精品久久8x国产免费观看| 亚洲AV乱码久久精品蜜桃| 99久久香蕉国产线看观香| 久久无码国产| 午夜福利91久久福利| 伊人色综合九久久天天蜜桃| 久久人妻无码中文字幕| 久久久久亚洲av成人网人人软件| 伊人色综合久久天天人守人婷| 久久这里都是精品| 久久综合色老色| 日本欧美久久久久免费播放网| 欧美噜噜久久久XXX| 999久久久无码国产精品| 九九久久99综合一区二区| 国产精品99久久久久久宅男 | 精品午夜久久福利大片| 精品久久久久中文字幕日本| 色噜噜狠狠先锋影音久久| 久久久久亚洲av毛片大| 亚洲欧洲久久久精品| 色综合久久久久无码专区| 国产精品美女久久久久| 久久婷婷色综合一区二区| 少妇熟女久久综合网色欲| 国产精品美女久久久久网| 久久精品国产亚洲Aⅴ蜜臀色欲| 2020久久精品亚洲热综合一本| 精品久久久久久久久午夜福利| 国内精品伊人久久久久影院对白| 日本精品久久久久久久久免费| 亚洲精品无码久久久久sm| 久久精品一区二区国产| 亚洲美日韩Av中文字幕无码久久久妻妇 | 久久亚洲国产精品一区二区| 久久人人爽人爽人人爽av | 久久这里有精品| 嫩草影院久久国产精品|