六月婷婷综合激情-六月婷婷综合-六月婷婷在线观看-六月婷婷在线-亚洲黄色在线网站-亚洲黄色在线观看网站

明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

應用MFC開發高級應用程序

[摘要]作者:茍建兵 清華大學熱能系(北京,100084) 目前在Windows下開發應用程序的工具雖然很多,但是C/C++作為一種非常成熟和高效的開發語言在大型復雜項目的開發中仍然得到了廣泛應用。為了減輕程序開發負擔,提高開發效率,各種流行的C++都提供了類庫,本文就是針對如何在Vis...
作者:茍建兵    清華大學熱能系(北京,100084)

      目前在Windows下開發應用程序的工具雖然很多,但是C/C++作為一種非常成熟和高效的開發語言在大型復雜項目的開發中仍然得到了廣泛應用。為了減輕程序開發負擔,提高開發效率,各種流行的C++都提供了類庫,本文就是針對如何在Visual C++環境中使用MFC類庫來開發高級程序所需要解決的一些問題進行了的探討,重點討論了利用MFC開發單文檔多視應用程序和DDE應用程序的方法。

文章正文

一、使用C/C++

隨著Windows系列操作系統的日益普遍,傳統的基于DOS編程逐漸轉向Windows下編程已經成為必然趨勢。目前在Windows下開發應用程序的工具很多,典型的如Borland
C++、Visual C++、Visual Baisic以及Delphi等等。每種開發工具都各有其特點, 一般來講用戶可以根據自己的使用習慣和開發項目的性質來選擇具體的開發語言。
Visual Basic是一個被軟件界稱之為劃時代的革新產品,該軟件改變了人們開發 Windows程序的方式,它采用交互式的可視化操作,使得人們開發Windows程序的每 一過程都有直觀形象的反饋,從而加速整個開發進程。Visual Basic使得Windows程 序設計人員不再只依賴于復雜的SDK編程,使得開發Windows程序非常容易,可以 說,用戶學習并使用VB來開發Windows應用的時間是最短的。Visual Basic版本幾經
演變,目前已經發展到5.0。在4.0版本中,由于完全使用了面向對象的編程概念, 同時具有Windows 3.1和Windows 95下的版本,因而使得其開發復雜程序的功能逐漸 增強。VB5.0則拋棄了Windows 3.x的用戶,只能在32位Windows中使用,據悉,該版 本吸收了Delphi的成功策略,引入了本地代碼(Native Code)編譯器,從而使得程序 執行速度大大加快,克服了以往版本由于執行文件采用P-Code代碼而導致運行速度
慢的特點,根據微軟的聲明,該版本的采用本地代碼編譯后得到的應用程序在某些 情況下執行速度較以往提高了10~20倍,執行速度可以直逼與采用Visual C++編寫的 應用,而應用開發速度則是VB的強項,因此Visual Basic 5.0非常具有競爭性。目 前Visual Basic非常廣泛地用于開發各種Windows程序,如數據庫前端應用程序和多 媒體應用等。但是,在作者看來,采用VB也有一定的缺點,原因有以下幾點:

1. Visual Basic來源于Basic語言,雖然經過微軟的不斷增強,但是仍然缺乏非常 靈活的數據類型和編程策略,因而在開發一些項目必須的復雜數據結構遇到麻煩, 如鏈表、圖和二叉樹等等。由于在中大型項目開發后期,開發工作不再以界面為 主,而是在算法設計和底層軟硬件工作,這就使VB開發項目的后期工作量大幅度增 加,為了達到項目要求,經常需要再轉向C/C++開發一些專用的動態連接庫來解決問
題。

2. Visual Basic運行速度慢,前文講過,采用P-Code代碼雖然執行文件很小,但是 在運行時需要解釋執行,并且,它的運行必須有對應的VBRUN.DLL和所使用的VBX或 者OCX支持。對于浮點操作密集或者循環嵌套很多的應用來說,VB沒有采取特別的優 化,因而執行速度遠不如用C/C++和Fortran開發的應用速度快。VB 5.0雖然通過引 入本地代碼編譯器大大彌補了這個缺陷,但是由于其只能運行于32位Windows環境因 而在16位Windows上速度問題仍然得不到解決。雖然目前轉向32位Windows的趨勢非 常強勁,但是不容忽視由于硬件的限制或者使用習慣等諸多原因,還有許多用戶仍 然在16位Windows上工作。在計算機十分普及的美國,96年使用16位Windows的用戶 仍然超過了使用32位Windows的用戶,任何進行系統軟件設計的人員都應該照顧到這 些仍然使用16位Windows的用戶。

3. VB不能靈活地使用系統資源。熟悉Windows編程的人都知道,如果要直接訪問硬 件或者要編寫對系統進行有效訪問的應用程序,沒有Windows API函數的幫助則非常 困難,但是令VB程序員失望的是,API函數是用C語言和匯編語言實現的,是為C編程 準備的,如果要在VB里面使用這些上千個API函數則比較麻煩,特別是,如果設計人 員不懂C語言則尤其困難。由于API函數的復雜性,而其本身不是為了方便VB編程而 提供的,因此在VB里面調用API函數需要一定的技巧,這些技巧足夠用一本很厚的書 來表述。VB程序員可以從書店里找到好多本類似的書籍?梢哉f,任何一個VB程序 員發展到一定階段都需要與眾多的API函數打交道。另外,由于VB不支持端口操作, 因此,如果要編寫類似數據采集等需要與硬件交互的程序則需要求救于C/C++語言。

4. Visual Basic項目分發和管理困難,其原因同上講的,VB應用的運行不能脫離VB 的運行庫和所使用的控件,因此,如果開發人員要將VB應用分發給用戶那么一定要 帶上VB的運行庫和所使用的控件,并且要保證正確安裝,這就導致即使一個非常簡 單的應用也需要附帶大量其它相關支撐庫程序,對于VB 4.0及更高版本,由于大量 的使用了OLE控件(在VB中稱為OCX),其安裝更為復雜。 Delphi軟件是國際寶蘭公司(Borland)的得意之作,也是備受軟件界推崇,與VB一 樣,它完全是一個交互式的可視化開發平臺,支持Client/Server應用程序的開發 其最新版本2.0可以開發Windows 3.x、Windows 95和Windows NT的應用程 序。Delphi開發速度也非?欤cVB相比,由于具有本地代碼編譯器因此它產生的 可執行文件執行速度大大加快。Delphi軟件是一個非常有競爭力的軟件,采用的是 面向對象的Object pascal語言,支持硬件操作和API調用。但是由于采用的編程語 言為Pascal,這種語言并不非常流行,許多程序設計人員完全不熟悉這種語言,因 此極大地限制了該軟件的使用,如果寶蘭公司能夠將Delphi軟件提供的RAD開發機制
引入到其Borland C++中,則可能會形成一個非常成功的產品(目前該版本已經推 出,即C++ Builder,筆者注)。 VB和Delphi引入的可視化開發方法還有一個共同的缺點就是各個版本之間的兼容問 題。一般來講,采用這些可視化開發工具開發的應用程序在移植到高版本時不會遇 到太大困難,但是一旦往相反方向移植則困難重重,有時甚至不可能。C/C++語言則 不具有這種局限性,各個版本之間的相互移植并不困難,高版本往低版本移植一般 只需重建工程文件即可大功告成。
綜上所述,根據作者的觀點,如果要開發一個大型復雜的應用程序首選的還是 C/C++,特別是在16位Windows下。雖然這會使前期工作增加,但是在項目的中后期 將逐漸會領略到其優越性和開發效率,其靈活高效的執行代碼適合于對速度和應用 程序之間的協同性要求很高的場合。純粹基于Windows SDK來開發Windows程序是一 項艱巨的工程,值得慶幸的是目前各種流行的C/C++開發工具都提供了類庫開發框架
來簡化整個開發過程而又不失其固有的靈活高效性,不同的開發語言所提供的類庫 開發框架不同,如Borland C++提供的OWL(Object Windows Library)和 Visual C++ 提供的MFC(Microsoft Fundmental Class),這兩種類庫都封裝了大量的Windows API和Windows的開發元素而使得開發任務簡化,兩種類庫各有其優點,據作者掌握 的資料,采用MFC編寫的應用程序執行代碼更小,執行速度也更快,這大概是因為該
軟件的開發者是開發Windows操作系統的Microsoft公司的緣故吧,現在MFC正逐漸成 為Windows下的類庫開發標準,正被越來越多的其它C/C++編譯工具所支持,如 Watcom C++。使用MC類庫同時配合Visual C++提供的AppWizard、ClassWizard和 AppStudio可以大幅度提高開發效率。筆者在工作中積累了一些MFC的使用經驗現在 提出來供大家參考,希望對廣大同行有所幫助,尤其是那些仍然致力于16位Windows 編程的程序員。本文使用的Visual C++ 1.51編譯器,但是其方法同樣適用于其它 VC++版本,包括Visual C++ 4.x。

二、MFC編程綜述

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

三、使用單文檔-多視結構

如果用戶使用過MFC進行編程,那么就會發現借助于AppWizard基于MFC無論編寫SDI (單文檔界面)還是編寫MDI(多文檔界面)都是十分方便的。MDI應用程序目前使用越 來越普遍,人們熟悉的Microsoft公司的Office系列產品以及Visual系列產品都是典 型的多文檔應用程序。這種多文檔界面具有多窗口的特點,因而人們可以在一個程 序中使用多個子窗口來實現不同數據的瀏覽查看。如果用戶要實現在MDI各個窗口之
間針對同一數據進行不同的可視化就是一件比較麻煩的事情。值得慶幸的是,MFC提 供的文檔-視結構大大簡化了這一工作。文檔-視結構通過將數據從用戶對數據的觀 察中分離出來,從而方便實現多視,亦即多個視口針對同一數據,如果一個視口中 數據發生改變,那么其它相關視口中的內容也會隨之發生改變以反映數據的變化。 SDI和MDI這兩種Windows標準應用程序框架并不是總能滿足用戶的需要,就作者的工
作而言,就特別需要一種被稱為單文檔多視的應用程序,英文可以縮寫為SDMV。通 過SDMV應用我們可以利用文檔類來統一管理應用程序的所有數據,同時需要采用 窗口以多種方式來可視化這些的數據,如棒圖,趨勢圖和參數列表,從而方便用戶 從不同角度來觀察數據。MDI雖然具有多窗口的特點,但是其為多文檔,即通常情況 下,一個視口對應一個文檔,視口+文檔便構成一個子窗口。在各個子窗口之間數據
相互獨立,如果要保持數據同步更新就需要采用特殊的技術了,采用這種方式既費 時又費力。通過筆者的實踐發現,利用MFC本身提供的多視概念通過適當改造MDI窗 口應用程序就可以實現上述SDMV結構

所謂SDMV應用程序本質上仍然是一個MDI應用程序,只是在程序中我們人為控制使其 只能生成一個文檔類,這個文檔在第一個視口創建時創建,注意,這里并不需要限 制各個視口的創建先后順序。此后與MDI窗口固有特性不同的是,所有新創建的子窗 口都不再創建獨立文檔,而是把該新視口直接連接到已有的文檔對象上,這樣就使 其成為單文檔多視的結構,所有相關數據都存儲在文檔對象中,一旦文擋中數據發
生改變,通過UpdateAllViews()函數通知所有相關視口,各個視口就可以在 OnUpdate()中相應數據的變化。這種響應機制如下圖所示:

圖 1 文檔-視結構數據更新機制
由于MDI本質上并不是為這種單文檔多視機制服務的,因而在實際應用時需要解決一 些問題。

1、窗口標題問題

窗口標題本來不應該成為問題,缺省情況下MDI窗口通過在文檔模板中提供的資源ID 所提供的對應字符串來確定窗口標題。但是對于SDMV應用,由于各個視口實質上是 對應于同一個文擋,因此每個視口都具有相同標題,只不過增加了一個數據用于指 示這是第幾個視口。如果在各個視口中指明具體的窗口名字,那么由不同的視口啟 動創建文檔產生的窗口標題就不同,這個名字會影響到后繼視口。為了作到不同類 型的視口如棒圖視口和曲線視口具有不同的標題,這就需要一定的技術處理。根據 筆者的摸索發現可以采用如下步驟實現:
首先在從標準的MDI子窗口基類CMDIChildWnd派生一個自己的子窗口類,姑且命名為 CMyChild,然后在其成員變量中增加一個CString型變量用以存儲當前窗口標題:
CString winTitle;
然后在不同的視口創建過程中通過獲取父窗口指針按自己的意愿對上述變量進行賦 值,程序片段如下:
pChild=(CMyChild*)GetParent();
pChild->winTitle="棒圖顯示窗口";
最后在CMyChild派生類中重載CMDIChildWnd基類中的OnUpdateFrameTitle()函數來 強制實現窗口標題的個性化,這一函數在各種類庫手冊上和聯機幫助中都沒有,但 的確有這樣一個具有保護屬性的函數用來實現窗口標題的更新操作,這可以從MFC類庫的源代碼中找到該函數的實現。重載后的源代碼如下:
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、如何創建SDMV應用
如何創建SDMV應用比較麻煩,下面通過舉例來具體說明。該例子假設用戶需要建棒 圖類型和曲線形式的兩種視口,假設用戶已經利用CView基類派生并且實現了這兩 類,分別對應于CMyChart和CMyTraceView兩個類。
1) 在應用類(從CWinApp派生出來的類)的頭文件中加入下列變量和函數原型說 明:
CMultiDocTemplate* m_pMyTraceTemplate;
CMultiDocTemplate* m_pMyChartTemplate;
int ExitInstance();
2) 在應用類的InitInstance成員函數中刪除對AddDocTemplate函數的調用和 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) 實現ExitInstance()函數,在其中刪除所用的兩個輔助模板:
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兩項,在對應的菜單命令中實現如下:
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的實現如下:
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應用的核心組成,它的任務是創建一個新的指定類型的視口, 它首先判斷是否有活動視口存在,文檔是否已經創建,正常情況下活動視口存在則 表明文檔存在,如果不存在則利用所指定的文檔模板創建一個新的活動視口,否則 則只創建視口,同時將其連接到已存在的文檔對象上。
通過以上步驟就可以實現SDMV應用,在其后的具體應用中利用文檔對象的 UpdateAllViews()函數和視口的OnUpdate()函數就可以很好的工作了。

四、使用DDE服務

Windows 3.x是一個分時多任務操作環境,在此環境下,多個應用程序可以并發地執 行。為了在并發執行的多個任務之間共享數據和資源,Windows 提供了幾種機制, 主要是通過剪貼板(Clipboard)和動態數據交換(Dynamic Data Exchange)。前者對 于用戶需要直接參與的數據交換來說,是一個非常方便的工具,但是如果希望數據 交換自動進行時就必須依靠DDE技術了。編寫DDE應用的技術也發展了好幾代,從最 初的基于消息的DDE到基于DDEML(動態數據交換管理庫),再到現在流行的OLE技 術。DDE技術的發展使得程序開發人員編寫DDE應用更為簡潔。從發展趨勢來看,基 于OLE的數據交換是最好的,它特別符合當今軟件領域的客戶-服務器機制 (Client-Server)。為適應多平臺和Internet的需要,在OLE基礎上微軟又開發了
ActiveX技術。但是不容忽視的是,基于傳統的DDE數據交換也自有它的應用空間, 使用仍然廣泛。目前在Windows 3.x下,基于OLE的遠程數據交換還很不成熟,但是 在WFW(Windows for Workgroup)下基于網絡動態數據交換的技術卻很成熟,目前也 應用非常普遍。關于DDE應用的開發和NetDDE的應用可以參看附錄7。
1、回調函數的處理

由于DDEML機制需要使用回調函數,因此使用DDEML的關鍵是解決在MFC編程體系中回 調函數的使用;卣{函數(Callback function)大量用于Windows的系統服務,通過 它,程序員可以安裝設備驅動程序和消息過濾系統,以控制Windows的有效使用。 許多程序員都發現,利用MFC或者其它的C++應用編寫回調函數是非常麻煩的,其根 本原因是回調函數是基于C編程的Windows SDK的技術,不是針對C++的,程序員可以 將一個C函數直接作為回調函數,但是如果試圖直接使用C++的成員函數作為回調函 數將發生錯誤,甚至編譯就不能通過。通過查詢資料發現,其錯誤是普通的C++成員 函數都隱含了一個傳遞函數作為參數,亦即“this”指針,C++通過傳遞一個指向自 身的指針給其成員函數從而實現程序函數可以訪問C++的數據成員。這也可以理解為 什么C++類的多個實例可以共享成員函數但是確有不同的數據成員。由于this指針的 作用,使得將一個CALLBACK型的成員函數作為回調函數安裝時就會因為隱含的this 指針使得函數參數個數不匹配,從而導致回調函數安裝失敗。要解決這一問題的關 鍵就是不讓this指針起作用,通過采用以下兩種典型技術可以解決在C++中使用回調 函數所遇到的問題。這種方法具有通用性,適合于任何C++

1. 不使用成員函數,直接使用普通C函數,為了實現在C函數中可以訪問類的成員變 量,可以使用友元操作符(friend),在C++中將該C函數說明為類的友元即可。這種 處理機制與普通的C編程中使用回調函數一樣。

2. 使用靜態成員函數,靜態成員函數不使用this指針作為隱含參數,這樣就可以作 為回調函數了。靜態成員函數具有兩大特點:其一,可以在沒有類實例的情況下使 用;其二,只能訪問靜態成員變量和靜態成員函數,不能訪問非靜態成員變量和非 靜態成員函數。由于在C++中使用類成員函數作為回調函數的目的就是為了訪問所有 的成員變量和成員函數,如果作不到這一點將不具有實際意義。解決的辦法也很簡 單,就是使用一個靜態類指針作為類成員,通過在類創建時初始化該靜態指針,如 pThis=this,然后在回調函數中通過該靜態指針就可以訪問所有成員變量和成員函 數了。這種處理辦法適用于只有一個類實例的情況,因為多個類實例將共享靜態類 成員和靜態成員函數,這就導致靜態指針指向最后創建的類實例。為了避免這種情 況,可以使用回調函數的一個參數來傳遞this指針,從而實現數據成員共享。這種 方法稍稍麻煩,這里就不再贅述。

2、在MFC中使用DDEML

對于典型的MFC應用程序,主框架窗口類(CMainFrame)只有一個實例,因此可以使用 靜態成員函數作為回調函數,從而實現DDE機制。具體的代碼片段如下:
(1) 在CMainFrame類中聲明如下靜態成員:
static CMainFrame* pThis;
static DWORD idInst;
static HDDEDATA CALLBACK EXPORT DdeCallback(UINT,UINT,HCONV,HSZ,HSZ, HDDEDATA,DWORD,DWORD);
(2) 在類的創建代碼(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服務","錯誤");
DestroyWindow();
}
(3) 回調函數實現如下:
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應用直接使用DDEML服務,那么該MFC應用在編譯時將會遇到變量類型HSZ 重復定義錯誤。經過追蹤發現,錯誤在于在DDEML.H對HSZ作了如下定義:
DECLARE_HANDLE32(HSZ);
而在AFXEXT.H(通過stdafx.h引入)中對HSZ又作了如下說明:
typedef BPSTR FAR* HSZ; // Long handle to a string
兩個定義一個為32位整數,一個為BASIC字符串指針,當然會發生編譯器不能作變量 類型轉換的錯誤。實際上,將HSZ聲明為BASIC字符串指針主要用于在MFC應用中使用 VBX控制。要改正這一錯誤,就必須保證不要在同一個代碼模塊中使用DDEML和VBX支 持,通過將使用DDEML和VBX的代碼分開,并在使用DDEML代碼的模塊中最開頭定義如 下編譯器宏就可以解決上述問題:
#define NO_VBX_SUPPORT

五、使用3D控制

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

六、使用自定義消息

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

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

七、使用不帶文擋-視結構的MFC應用

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

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實用程序生成,該類的頭文件 與實現代碼可以與CMyApp類的頭文件和實現代碼放在一起。注意,這里由一個技 巧,由于ClassWizard的使用需要有相應的CLW文件存在,而收工建代碼時沒有對應 的CLW文件,因此不能直接使用,解決辦法是進入App Studio實用工具后使用 ClassWizard,此時系統會發覺不存在相應的CLW文件,系統將提示你重建CLW文件并 彈出相應對話框,這時候你不需要選擇任何文件就直接選擇OK按鈕,這樣系統將為 你產生一個空的CLW文件,這樣就可以使用ClassWizard實用工具了。為了將CWinApp 和CFrameWnd的派生類有機地結合在一起,只需在CFrameWnd派生類的構造函數中進 行窗口創建即可。典型代碼如下:
CMainFrame::CMainFrame()
{
Create(NULL,"DDE Client Application",WS_OVERLAPPEDWINDOW,rectDefault, NULL,MAKEINTRESOURCE(IDR_MAINFRAME));
}
采用ClassWizard實用程序生成相關類代碼后,所有的類的其它實現和維護就同普通由AppWizard實用程序產生的代碼一樣了。

2、基于對話框的程序

有些主要用于數據的輸入和輸出等的應用在使用時沒有必要改變窗口大小,典型的 如各種聯機注冊程序,這些使用對話框作為應用的主界面就足夠了,而且開發此類 應用具有方便快捷的特點,代碼也比較短小,如果直接采用各種控制類生成所需要 的控制就特別麻煩。在Visual C++ 4.x版本中使用AppWizard就可以直接生成基于對 話框的應用。在Visual 1.x中沒有此功能,因此這類應用需要程序員自己實現 實際上使用MFC實現基于對話框的應用非常簡單,同樣只使用兩個MFC類作為基類, 這兩個類為CWinApp類和CDialog類。所使用的對話框主界面同樣可以先用App Studio編輯對話框界面,再使用ClassWizard產生相應代碼框架,然后修改CMyApp類 的聲明,增加一個該對話框類的成員變量m_Mydlg,最后修改CMyApp類的
InitInstance()函數如下:
BOOL CMyApp::InitInstance()
{
m_Mydlg.DoModal();
return TRUE;
}

八、MFC應用的人工優化

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

九、結束語

總體上講,使用Visual C++和MFC類庫進行Windows編程是非常方便的,本文中所提 到的一些看法只代表本人的觀點,經驗也只是筆者根據近年使用MFC進行Windows編 程的總結,在此寫出來是希望對那些使用VC和MFC進行Windows編程的同行有所幫 助,如有不同看法歡迎與筆者聯系討論。

十、參考文獻:

[1]. David J.Kruglinski ,Visual C++技術內幕,清華大學出版社,1995
[2]. 鄭雪明,Visual C++基礎類庫參考大全,學苑出版社,1994
[3]. B.R. Overland,Visual C++程序設計精髓,科學出版社,1995
[4]. Mike Klein,Windows程序員使用指南--DLL和內存管理,清華大學出版社,
1995
[5]. Richard Wilton,Microsoft Windows軟件開發環境與技術, 清華大學出版
社,1993
[6]. 芶建兵,倪維斗,Windows下網絡DDE的使用,電子與電腦,1997.2

作者聯系地址:清華大學熱能系動控教研組
聯系電話:(010)-62781739
BP:68213388-50610


主站蜘蛛池模板: 亚欧免费视频一区二区三区 | 日韩免费在线观看视频 | 欧美性美 | 亚洲综合色就色手机在线观看 | 日本人成免费大片 | 日韩a级毛片 | 日韩欧美高清视频 | 午夜久久久久久亚洲国产精品 | 欧美亚洲性色影视在线 | 性生大片免费看 | 午夜国产精品福利在线观看 | 伊人中文字幕在线观看 | 日本国产亚洲 | 青草伊伊| 欧美怡红院视频一区二区三区 | 日韩色视频在线观看 | 天堂网免费 | 一本久久精品一区二区 | 日本伦理中文字幕 | 天天婷婷 | 日韩中文字幕精品视频在线 | 午夜国产精品无套 | 天天干天天做天天操 | 五月天激情丁香 | 日本久久综合网 | 欧美性猛交xxxx乱大交be | 影音先锋国产 | 日本理论片免费观看在线视频 | 欧美性猛交一区二区三区 | 亚欧在线精品免费观看一区 | 亚洲第一精品夜夜躁人人爽 | 中文字幕成人免费视频 | 日本高清视频网址 | 日本高清视频不卡 | 欧美在线高清视频播放免费 | 日韩精品一区二区三区中文3d | 色综合综合 | 日韩三级在线 | 青春草在线视频观看 | 日本不卡视频一区二区 | 一久久|