本書(shū)介紹了在Microsoft Windows 98、Microsoft Windows NT 4.0和Windows NT 5.0下程序寫(xiě)作的方法。這些程序用C語(yǔ)言編寫(xiě)并使用原始的Windows Application Programming Interface(API)。如在本章稍后所討論的,這不是寫(xiě)作Windows程序的唯一方法。然而,無(wú)論最終您使用什么方式寫(xiě)作程序,了解Windows API都是非常重要的。
正如您可能知道的,Windows 98已成為使用Intel 32位微處理器(例如486和Pentium)的IBM兼容型個(gè)人計算機環(huán)境上最新的圖形操作系統之代表。Windows NT是IBM PC兼容機種以及一些RISC(精簡(jiǎn)指令集計算機)工作站上使用的Windows工業(yè)增強型版本。
使用本書(shū)有三個(gè)先決條件。首先,您應該從使用者的角度熟悉Windows 98。不要期望可以在不了解Windows使用者接口的情形下開(kāi)發(fā)其應用程序。因此,我建議您在開(kāi)發(fā)程序(或在進(jìn)行其它工作)時(shí)使用執行Windows的機器來(lái)跑Windows應用程序。
第二,您應了解C語(yǔ)言。如果要寫(xiě)Windows程序,一開(kāi)始卻不想了解C語(yǔ)言,那不是一個(gè)好主意。我建議您在文字控制臺環(huán)境中,例如在Windows 98 MS-DOS命令提示窗口下提供的環(huán)境中學(xué)習C語(yǔ)言。Windows程序設計有時(shí)包括一些非文字模式程序設計的C語(yǔ)言部分;在這些情況下,我將針對這些問(wèn)題提供討論。但大多數情況下,您應非常熟悉該語(yǔ)言,特別是C語(yǔ)言的結構和指針。了解標準C語(yǔ)言執行期鏈接庫的一些相關(guān)知識是有幫助的,但不是必要的。
第三,您應該在機器上安裝一個(gè)適于進(jìn)行Windows程序設計的32位C語(yǔ)言編譯器和開(kāi)發(fā)環(huán)境。在本書(shū)中,假定您正在使用Microsoft Visual C++ 6.0,該軟件包可獨立購買(mǎi),也可作為Visual Studio 6.0軟件包的一部分購買(mǎi)。
到此為止,我將不再假設您具有任何圖形使用者接口(如Windows)的程序寫(xiě)作經(jīng)驗。
Windows幾乎不需要介紹。然而人們很容易忘記Windows給辦公室和家庭桌上型計算機所帶來(lái)的重大改變。Windows在其早期曾經(jīng)走過(guò)一段坎坷的道路,征服桌上型計算機市場(chǎng)的前途一度相當渺茫。
Windows簡(jiǎn)史
在1981年秋天IBM PC推出之后不久,MS-DOS就已經(jīng)很明顯成為PC上的主流操作系統。MS-DOS代表Microsoft Disk Operating System(磁盤(pán)操作系統)。MS-DOS是一個(gè)小型的操作系統。MS-DOS提供給用戶(hù)一種命令列接口,提供如DIR和TYPE的命令,也可以將應用程序加載內存執行。對于應用程序寫(xiě)作者,它提供了一組函數呼叫,進(jìn)行文件的輸入輸出(I/O )。對于其它的外圍處理-尤其是將文字或圖形寫(xiě)到顯示器上-應用程序可以直接存取PC的硬件。
由于內存和硬件的限制,成熟的圖形環(huán)境緩慢地才到來(lái)。當蘋(píng)果計算機公司不幸的Lisa計算機在1983年1月發(fā)表時(shí),它提供了不同于文字模式環(huán)境的另一種選擇,并在1984年1月成為Macintosh上圖形環(huán)境的一種標準。盡管Macintosh的市場(chǎng)占有率在下降,但是它仍然被認為是衡量所有其它圖形環(huán)境的標準。包括Macintosh和Windows的所有圖形環(huán)境,其實(shí)都要歸功于Xerox Palo Alto Research Center(PARC)在70年代中期所作的開(kāi)拓性研究工作。
Windows是由微軟在1983年11月(在Lisa之后,Macintosh之前)宣布,并在兩年后(1985年11月)發(fā)行。在此后的兩年中,緊隨著(zhù)Microsoft Windows早期版本1.0之后,又推出了幾種改進(jìn)版本,以支持國際商業(yè)市場(chǎng),并提供新型視訊顯示器和打印機的驅動(dòng)程序。
Windows版本2.0是在1987年11月正式在市場(chǎng)上推出的。該版本對使用者接口做了一些改進(jìn)。這些改進(jìn)中最有效的是使用了可重迭式窗口,而Windows 1.0中使用的是并排式窗口。Windows 2.0還增強了鍵盤(pán)和鼠標接口,特別是加入了菜單和對話(huà)框。
至此,Windows還只要求Intel 8086或者8088等級的微處理器,以「實(shí)際模式」執行,只能存取地址在1MB以下的內存。Windows/386(在Windows 2.0之后不久發(fā)行的)使用Intel 386微處理器的「虛擬8086」模式,實(shí)現將直接存取硬件的多個(gè)MS-DOS程序窗口化和多任務(wù)化。為了統一起見(jiàn),Windows版本2.1被更名為Windows/286。
Windows 3.0是在1990年5月22日發(fā)表的。它將Windows/286和Windows/386結合到同一種產(chǎn)品中。Windows 3.0有了一個(gè)很大的改變,這就是對Intel的286、386和486微處理器保護模式的支持。這能使Windows和Windows應用程序能存取高達16MB的內存。Windows用于執行程序和維護文件的「外殼」程序得到了全面的改進(jìn)。Windows 3.0是第一個(gè)在家用和辦公室市場(chǎng)上取得立足點(diǎn)的版本。
任何Windows的歷史介紹都必須包括一些OS/2的說(shuō)明,OS/2是對DOS和Windows的另一種選擇,最初是由Microsoft和IBM合作開(kāi)發(fā)的。OS/2版本1.0(只有文字模式)在Intel 286(或者后來(lái)的)微處理器上運行,在1987年末發(fā)布。在1988年10月的OS/2版本1.1中出現了管理圖形使用者接口的PM(Presentation Manager)。PM最初的設計構想是成為Windows的一種保護模式版本,但是圖形API改變程度太大,致使軟件生產(chǎn)廠(chǎng)商很難提供對這兩種平臺的支持。
到1990年9月,IBM和Microsoft之間的沖突達到了高峰,導致這兩個(gè)公司最后分道揚鑣。IBM接管了OS/2,而Microsoft明確表示W(wǎng)indows將是他們操作系統策略的中心。雖然OS/2仍然擁有一些狂熱的崇拜者,但是它遠不及Windows這樣的普及程度。
Microsoft Windows版本3.1是1992年4月發(fā)布的,其中包括的幾個(gè)重要特性是TrueType字體技術(shù)(給Windows帶來(lái)可縮放的輪廓字體)、多媒體(聲音和音樂(lè ))、對象連結和嵌入(OLE:Object Linking and Embedding)和通用對話(huà)框。跟OS/2一樣,Windows 3.1只能在保護模式下運作,并且要求至少配置了1MB內存的286或386處理器。
在1993年7月發(fā)表的Windows NT是第一個(gè)支持Intel 386、486和Pentium微處理器32位保護模式的Windows版本。Windows NT提供32位平坦尋址,并使用32位的指令集。(本章后面我會(huì )談到一些尋址空間的問(wèn)題)。Windows NT還可以移植到非Intel處理器上,并在幾種使用RISC芯片的工作站上執行。
Windows 95是在1995年8月發(fā)布的。和Windows NT一樣,Windows 95也支持Intel 386或更高等級處理器的32位保護模式。雖然它缺少Windows NT中的某些功能,諸如高安全性和對RISC機器的可移植性等,但是Windows 95具有需要較少硬件資源的優(yōu)點(diǎn)。
Windows 98在1998年6月發(fā)布,具有許多加強功能,包括執行效能的提高、更好的硬件支持以及與因特網(wǎng)和全球信息網(wǎng)(WWW)更緊密的結合。
Windows方面
Windows 98和Windows NT都是支持32位優(yōu)先權式多任務(wù)(preemptive multitasking)及多線(xiàn)程的圖形操作系統。Windows擁有圖形使用者接口(GUI ),這種使用者界面也稱(chēng)作「可視化接口」或「圖形窗口環(huán)境」。有關(guān)GUI的概念可追溯至70年代中期,在A(yíng)lto和Star等機器上以及SmallTalk等環(huán)境中由Xerox PARC所作的研究工作。該項研究的成果后來(lái)被Apple Computer和Microsoft引入主流并流行起來(lái)。雖然有一些爭議,但現在已非常清楚,GUI是(Microsoft的Charles Simonyi的說(shuō)法)一個(gè)在個(gè)人計算機工業(yè)史上集各方面技術(shù)大成于一體的最重要產(chǎn)物。
所有GUI都在點(diǎn)矩陣對應的視訊顯示器上處理圖形。圖形提供了使用屏幕的最佳方式、傳遞信息的可視化豐富多彩環(huán)境,以及能夠WYSIWYG(what you see is what you get:所見(jiàn)即所得)的圖形視訊顯示和為書(shū)面文件準備好格式化文字輸出內容。
在早期,視訊顯示器僅用于響應使用者通過(guò)鍵盤(pán)輸入的文字。在圖形使用者接口中,視訊顯示器自身成為使用者輸入的一個(gè)來(lái)源。視訊顯示器以圖標和輸入設備(例如按鈕和滾動(dòng)條)的形式顯示多種圖形對象。使用者可以使用鍵盤(pán)(或者更直接地使用鼠標等指向設備)直接在屏幕上操縱這些對象,拖動(dòng)圖形對象、按下鼠標按鈕以及滾動(dòng)滾動(dòng)條。
因此,使用者與程序的交流變得更為親密。這不再是一種從鍵盤(pán)到程序,再到視訊顯示器的單向信息流動(dòng),使用者已經(jīng)能夠與顯示器上的對象直接交互作用了。
使用者不再需要花費長(cháng)時(shí)間學(xué)習如何使用計算機或掌握新程序了。Windows讓這一切成真,因為所有應用程序都有相同的基本外觀(guān)和感覺(jué)。程序占據一個(gè)窗口-屏幕上的一塊矩形區域。每個(gè)窗口由一個(gè)標題列標識。大多數程序功能由程序的菜單開(kāi)始。用戶(hù)可使用滾動(dòng)條觀(guān)察那些無(wú)法在一個(gè)屏幕中裝下的信息。某些菜單項目觸發(fā)對話(huà)框,用戶(hù)可在其中輸入額外的信息。幾乎在每個(gè)大的Windows程序中都有一個(gè)用于開(kāi)啟文件的特殊對話(huà)框。該對話(huà)框在所有這些Windows程序中看起來(lái)都一樣(或接近相同),而且幾乎總是從同一菜單選項中啟動(dòng)。
一旦您了解使用一個(gè)Windows程序的方法,您就非常容易學(xué)習其它的Windows程序。菜單和對話(huà)框允許用戶(hù)試驗一個(gè)新程序并探究它的功能。大多數Windows程序同時(shí)具有鍵盤(pán)接口和鼠標接口。雖然Windows程序的大多數功能可通過(guò)鍵盤(pán)控制,但使用鼠標要容易得多。
從程序寫(xiě)作者的角度看,一致的使用者接口來(lái)自于Windows建構菜單和對話(huà)框的內置程序。所有菜單都有同樣的鍵盤(pán)和鼠標接口,因為這項工作是由Windows處理,而不是由應用程序處理。
為便于多個(gè)程序的使用,以及這些程序間信息的交換,Windows支持多任務(wù)。在同一時(shí)刻能有多個(gè)Windows程序顯示并運行。每個(gè)程序在屏幕上占據一個(gè)窗口。用戶(hù)可在屏幕上移動(dòng)窗口,改變它們的大小,在不同程序間切換,并從一個(gè)程序向另一個(gè)程序傳送數據。因為這些窗口看起來(lái)有些像桌面上的紙(當然,這是計算機還未占據辦公桌之前的年代),Windows有時(shí)被稱(chēng)作:一個(gè)顯示多個(gè)程序的「具象化桌面」。
Windows的早期版本使用一種「非優(yōu)先權式(non-preemptive)」的多任務(wù)系統。這意味著(zhù)Windows不使用系統定時(shí)器將處理時(shí)間分配給系統中運行的多個(gè)應用程序,程序必須自愿放棄控制以便其它程序運行。在Windows NT和Windows 98中,多任務(wù)是優(yōu)先權式的,而且程序自身可分割成近乎同時(shí)執行的多個(gè)執行緒。
操作系統不對內存進(jìn)行管理便無(wú)法實(shí)現多任務(wù)。當新程序啟動(dòng)、舊程序終止時(shí),內存會(huì )出現碎裂空間。系統必須能夠將閑置的內存空間組織在一起,因此系統必須能夠移動(dòng)內存中的程序代碼和數據塊。
即使是在8088微處理器上跑的Windows 1.0也能進(jìn)行這類(lèi)內存管理。在實(shí)際模式限制下,這種能力被認為是軟件工程一個(gè)令人驚訝的成就。在Windows 1.0中,PC硬件結構的640KB內存限制,在不要求任何額外內存的情況下被有效地擴展了。但Microsoft并未就此停步:Windows 2.0允許Windows應用程序存取擴充內存(EMS);Windows 3.0在保護模式下,允許Windows應用程序存取高達16MB的擴展內存。Windows NT和Windows 98通過(guò)成熟的32位操作系統及平坦尋址空間,擺脫了這些舊的限制。
Windows上執行的程序可共享在稱(chēng)為「動(dòng)態(tài)鏈接庫」的文件中的例程。Windows包括一個(gè)機制,能夠在執行時(shí)連結使用動(dòng)態(tài)鏈接庫中例程的程序。Windows自身基本上就是一個(gè)動(dòng)態(tài)鏈接庫的集合。
Windows是一個(gè)圖形接口,Windows程序能夠在視訊顯示器和打印機上充分利用圖形和格式化文字。圖形接口不僅在外觀(guān)上更有吸引力,而且還能夠讓使用者傳遞高層次的信息。
Windows應用程序不能直接存取屏幕和打印機等圖形顯示設備硬件。相反,Windows提供一種圖形程序語(yǔ)言(稱(chēng)作圖形設備接口,或者GDI),使顯示圖形和格式化文字更容易。Windows虛擬化了顯示硬件,使為Windows編寫(xiě)的程序可使用任何具有Windows設備驅動(dòng)程序的視頻卡或打印機,而程序無(wú)需確定系統相連的設備類(lèi)型。
對Windows開(kāi)發(fā)者來(lái)說(shuō),將與設備無(wú)關(guān)的圖形接口輸出到IBM PC上不是件輕松的事。PC的設計是基于開(kāi)放式架構的原則,鼓勵第三方硬件制造商為PC開(kāi)發(fā)接口設備,而且開(kāi)發(fā)了大量這樣的設備。雖然出現了多種標準,PC上的傳統MS-DOS程序仍不得不各自支持許多不同的硬設備。這對MS-DOS字處理軟件來(lái)說(shuō)非常普遍,它們連同1到2張有許多小文件的磁盤(pán)一同銷(xiāo)售,每個(gè)文件支持一種特定的打印機。Windows程序不要求每個(gè)應用程序都自行開(kāi)發(fā)這些驅動(dòng)程序,因為這種支持是Windows的一部分。
動(dòng)態(tài)鏈接
Windows運作機制的核心是一個(gè)稱(chēng)作「動(dòng)態(tài)鏈接」的概念。Windows提供了應用程序豐富的可呼叫函數,大多數用于實(shí)作其使用者接口和在視訊顯示器上顯示文字和圖形。這些函數采用動(dòng)態(tài)鏈接庫(Dynamic Linking Library,DLL)的方式撰寫(xiě)。這些動(dòng)態(tài)鏈接庫是些具有.DLL或者有時(shí)是.EXE擴展名的文件,在Windows 98中通常位于\WINDOWS\SYSTEM子目錄中,在Windows NT中通常位于\WINNT\SYSTEM和\WINNT\SYSTEM32子目錄中。
在早期,Windows的主要部分僅通過(guò)三個(gè)動(dòng)態(tài)鏈接庫實(shí)作。這代表了Windows的三個(gè)主要子系統,它們被稱(chēng)作Kernel、User和GDI。當子系統的數目在Windows最近版本中增多時(shí),大多數典型的Windows程序產(chǎn)生的函數呼叫仍對應到這三個(gè)模塊之一。Kernel(日前由16位的KRNL386.EXE和32位的KERNEL32.DLL實(shí)現)處理所有在傳統上由操作系統核心處理的事務(wù)-內存管理、文件I/O和多任務(wù)管理。User(由16位的USER.EXE和32位的USER32.DLL實(shí)作)指使用者接口,實(shí)作所有窗口運作機制。GDI(由16位的GDI.EXE和32位的GDI32.DLL實(shí)作)是一個(gè)圖形設備接口,允許程序在屏幕和打印機上顯示文字和圖形。
Windows 98支持應用程序可使用的上千種函數呼叫。每個(gè)函數都有一個(gè)描述名稱(chēng),例如CreateWindow。該函數(如您所猜想的)為程序建立新窗口。所有應用程序可以使用的Windows函數都在表頭文件里預先聲明過(guò)。
在Windows程序中,使用Windows函數的方式通常與使用如strlen等C語(yǔ)言鏈接庫函數的方式相同。主要的區別在于C語(yǔ)言鏈接庫函數的機械碼連結到您的程序代碼中,而Windows函數的程序代碼在您程序執行文件外的DLL中。
當您執行Windows程序時(shí),它通過(guò)一個(gè)稱(chēng)作「動(dòng)態(tài)鏈接」的過(guò)程與Windows相接。一個(gè)Windows的.EXE文件中有使用到的不同動(dòng)態(tài)鏈接庫的參考數據,所使用的函數即在那些動(dòng)態(tài)鏈接庫中。當Windows程序被加載到內存中時(shí),程序中的呼叫被指向DLL函數的入口。如果該DLL不在內存中,就把它加載到內存中。
當您連結Windows程序以產(chǎn)生一個(gè)可執行文件時(shí),您必須連結程序開(kāi)發(fā)環(huán)境提供的特定「引用鏈接庫(import library)」。這些引用鏈接庫包含了動(dòng)態(tài)鏈接庫名稱(chēng)和所有Windows函數呼叫的引用信息。連結程序使用該信息在.EXE文件中建立一個(gè)表格,在加載程序時(shí),Windows使用它將呼叫轉換為Windows函數。
為說(shuō)明Windows程序設計的多種技術(shù),本書(shū)提供了許多范例程序。這些程序使用C語(yǔ)言撰寫(xiě)并原原本本的使用Windows API來(lái)開(kāi)發(fā)程序。我將這種方法稱(chēng)作「古典」Windows程序設計。這是我們在1985年為Windows 1.0寫(xiě)程序的方法,它今天仍是寫(xiě)作Windows程序的有效方法。
API和內存模式
對于程序寫(xiě)作者來(lái)說(shuō),操作系統是由本身的API定義的。API包含了所有應用程序能夠使用的操作系統函數呼叫,同時(shí)包含了相關(guān)的數據型態(tài)和結構。在Windows中,API還意味著(zhù)一個(gè)特殊的程序架構,我們將在每章的開(kāi)頭進(jìn)行研究。
一般而言,Windows API自Windows 1.0以來(lái)一直保持一致,沒(méi)什么重大改變。具有Windows 98程序寫(xiě)作經(jīng)驗的Windows程序寫(xiě)作者會(huì )對Windows 1.0程序的原始碼感覺(jué)非常熟悉。API改變的一種方式是進(jìn)行增強。Windows 1.0支持不到450個(gè)函數呼叫,現在已有了上千種函數呼叫。
Windows API和它的語(yǔ)法的最大變化來(lái)自于從16位架構向32位架構轉化的過(guò)程中。Windows從版本1.0到版本3.1使用16位Intel 8086、8088、和286微處理器上所謂的分段內存模式,由于兼容性的原因,從386開(kāi)始的32位Intel微處理器也支持該模式。在這種模式下,微處理器緩存器的大小為16位,因此C的int數據型態(tài)也是16位寬。在分段內存模式下,內存地址由兩個(gè)部分組成-一個(gè)16位段(segment)指針和一個(gè)16位偏移量(offset)指標。從程序寫(xiě)作者的角度看,這非常凌亂并帶來(lái)了long或far指針(包括段地址和偏移量地址)和short或near指標(包括帶有假定段地址的偏移量地址)的區別。
從Windows NT和Windows 95開(kāi)始,Windows支持使用Intel 386、486和Pentium處理器32位模式下的32位平坦尋址內存模式。C語(yǔ)言的int數據型態(tài)也擴展為32位的值。為32位版本W(wǎng)indows編寫(xiě)的程序使用簡(jiǎn)單的平坦線(xiàn)性空間尋址的32位指針值。
用于16位版本W(wǎng)indows的API(Windows 1.0到Windows 3.1)現在稱(chēng)作Win16。用于32位版本W(wǎng)indows的API(Windows 95、Windows 98和所有版本的Windows NT)現在稱(chēng)作Win32。許多函數呼叫在從Win16到Win32的轉變中保持相同,但有些需要增強。例如,圖像坐標點(diǎn)由Win16中的16位值變?yōu)閃in32中的32位值。此外,某些Win16函數呼叫返回一個(gè)包含在32位整數值中的二維坐標點(diǎn)。這在Win32中不可能,因此增加的新函數呼叫以不同方式運作。
所有32位版本的Windows都支持Win16 API(以確保和舊有應用程序兼容)和Win32 API(以運行新應用程序)。非常有趣的是,Windows NT與Windows 95及Windows 98的工作方式不同。在Windows NT中,Win16函數呼叫通過(guò)一個(gè)轉換層被轉化為Win32函數呼叫,然后被操作系統處理。在Windows 95和Windows 98中,該操作正相反:Win32函數呼叫通過(guò)轉換層轉換為Win16函數呼叫,再由操作系統處理。
在同一時(shí)刻有兩個(gè)不同的Windows API集(至少名稱(chēng)不同)。Win32s (「s」代表「subset(子集)」)是一個(gè)API,允許程序寫(xiě)作者編寫(xiě)在Windows 3.1上執行的32位應用程序。該API僅支持已被Win16支持的32位函數版本。此外,Windows 95 API一度被稱(chēng)作Win32c(「c」代表「compatibility(兼容性)」),但該術(shù)語(yǔ)已被拋棄了。
現在,Windows NT和Windows 98都被認為能夠支持Win32 API。然而,每個(gè)操作系統依然都支持某些不被別的操作系統支持的某些功能特性。因為它們的相同之處是相當可觀(guān)的,所以有可能編寫(xiě)在兩個(gè)操作系統下都可執行的程序。而且,人們普遍認為這兩個(gè)產(chǎn)品最終會(huì )合而為一。
語(yǔ)言選項
使用C語(yǔ)言和原始的API不是編寫(xiě)Windows 98程序的唯一方法。然而,這種方法卻提供給您最佳的性能、最強大的功能和在發(fā)掘Windows特性方面最大的靈活性??蓤绦形募鄬^小且運行時(shí)不要求外部鏈接庫(自然,Windows DLL自身除外)。最重要的是,不管您最終以什么方式開(kāi)發(fā)Windows應用程序,熟悉API會(huì )使您對Windows內部有更深入的了解。
雖然我認為學(xué)習古典的Windows程序設計對任何Windows程序寫(xiě)作者都是重要的,我沒(méi)有必要建議使用C和API編寫(xiě)每個(gè)Windows應用程序。許多程序寫(xiě)作者,特別是那些為公司內部開(kāi)發(fā)程序或在家編寫(xiě)娛樂(lè )程序的程序寫(xiě)作者喜歡輕松的開(kāi)發(fā)環(huán)境,例如Microsoft Visual Basic或者Borland Delphi(它結合了對象導向的Pascal版本)。這些環(huán)境使程序寫(xiě)作者將精力集中于應用程序的使用者接口和相關(guān)使用者接口對象的程序代碼上。要學(xué)習Visual Basic,您也許需要參考Microsoft Press的一些其它圖書(shū),例如Michael Halvorson1996年著(zhù)的《Learn Visual Basic Now》。
在專(zhuān)業(yè)程序寫(xiě)作者中-特別是那些開(kāi)發(fā)商業(yè)應用程序的程序寫(xiě)作者-Microsoft Visual C++和Microsoft Foundation Class Library(MFC)是近年來(lái)流行的選擇。MFC在一組C++對象類(lèi)別中封裝了許多Windows程序設計中的瑣碎細節。Jeff Prosise的《Programming Windows with MFC,第二版》(Microsoft Press,1999年)提供了MFC程序的寫(xiě)作指南。
最近,Internet和World Wide Web的流行大力推廣著(zhù)Sun Microsystems的Java,這是一個(gè)受C++啟發(fā)卻與微處理器無(wú)關(guān)的程序設計語(yǔ)言,而且結合了可在幾個(gè)操作系統平臺上執行的圖形應用程序開(kāi)發(fā)工具組。Microsoft Press有一本關(guān)于Microsoft J++(Microsoft的Java)開(kāi)發(fā)工具的好書(shū),《Programming Visual J++ 6.0》(1998年),由Stephen R. Davis著(zhù)。
顯然,很難說(shuō)哪種方法更有利于開(kāi)發(fā)Windows應用程序。更主要的是,也許是應用程序自身的特性決定了所使用的工具。不管您最后實(shí)際上使用什么工具寫(xiě)作程序,學(xué)習Windows API將使您更深入地了解Windows工作的方式。Windows是一個(gè)復雜的系統,在A(yíng)PI上增加一個(gè)程序寫(xiě)作層并未減少它的復雜性,僅僅是掩蓋了它,早晚您會(huì )碰到它。了解API會(huì )給您更好的補救機會(huì )。
在原始的Windows API之上的任何軟件層都必定將您限制在全部功能的一個(gè)子集內。您也許發(fā)現,例如,使用Visual Basic編寫(xiě)應用程序非常理想,然而它不允許您做一個(gè)或兩個(gè)很簡(jiǎn)單的基本工作。在這種情況下,您將不得不使用原始的API呼叫。API定義了作為Windows程序寫(xiě)作者所需的一切。沒(méi)有什么方法比直接使用API更萬(wàn)能的了。
MFC尤其問(wèn)題百出。雖然它大幅簡(jiǎn)化了某些工作(例如OLE),我卻經(jīng)常發(fā)現要讓它們按我所想的去工作時(shí),會(huì )在其它特性(例如Document/View架構)上碰壁。MFC還不是Windows程序設計者所追求的靈丹妙藥,很少有人認為它是一個(gè)好的對象導向設計的模型。MFC程序寫(xiě)作者從他們使用的對象類(lèi)別定義如何工作中受益頗深,并會(huì )發(fā)現他們經(jīng)常參考MFC原始碼,搞懂這些原始碼是學(xué)習Windows API的好處之一。
程序開(kāi)發(fā)環(huán)境
在本書(shū)中,假定您正使用Microsoft Visual C++ 6.0,標準版、專(zhuān)業(yè)版和企業(yè)版都可以。經(jīng)濟的標準版足以應付本書(shū)中的程序設計需求。Visual C++ 還是Visual Studio 6.0中的一部分。
Microsoft Visual C++ 軟件包中包括C編譯器和其它編譯及連結Windows程序所需的文件和工具等。它還包括Visual C++ Developer Studio,一個(gè)可編輯原始碼、以交談方式建立資源(如圖標和對話(huà)框)以及編輯、編譯、執行和測試程序的環(huán)境。
如果您正使用Visual C++ 5.0,則需要為Windows 98和Windows NT 5.0更新表頭文件和引用鏈接庫,這些東西可從Microsoft的網(wǎng)站上得到。在 http://www.microsoft.com/msdn/,選擇「Downloads」,然后選擇「 Platform SDK」(軟件開(kāi)發(fā)套件),您就能在選擇的目錄中下載和安裝更新文件。要讓Microsoft Developer Studio瀏覽這些目錄,可以從「Tool」菜單項選擇「 Options」然后按下「Directories」標簽。
Microsoft網(wǎng)站上的msdn部分代表「Microsoft Developer Network(Microsoft軟件開(kāi)發(fā)者網(wǎng)絡(luò ))」。這是一個(gè)向程序寫(xiě)作者提供了經(jīng)常更新的CD-ROM的計劃,這些CD-ROM中包含了程序寫(xiě)作者在Windows開(kāi)發(fā)中所需的最新東西。您也可以訂閱MSDN,這樣就避免經(jīng)常得從Microsoft的網(wǎng)站下載文件。
API文件
本書(shū)不是Windows API權威的正式文件的替代品。那組文件不再以印刷形式出版,它僅能從CD-ROM或Internet上取得。
當您安裝Visual C++ 6.0時(shí),您將得到一個(gè)包括API文件的在線(xiàn)求助系統。您可通過(guò)訂閱MSDN或使用Microsoft網(wǎng)站上的在線(xiàn)求助系統更新該文件。連接到 http://www.microsoft.com/msdn/,并選擇「MSDN Library Online」。
在Visual C++ 6.0中,從「Help」菜單項選擇「Contents」項目開(kāi)啟MSDN窗口。API文件按樹(shù)形結構組織,尋找標有「 Platform SDK」的部分,所有在本書(shū)中引用的文件都來(lái)自于該部分。我將向您介紹如何從「 Platform SDK」開(kāi)始尋找以斜線(xiàn)分層分門(mén)別類(lèi)的文件的位置。(我知道「Platform SDK」是整個(gè)MSDN知識庫中較為晦澀的部分,但我敢保證那是Windows程序設計的基本核心。)例如,對于如何在Windows程序中使用鼠標的文件,您可參考/ Platform SDK / User Interface Services / User Input / Mouse Input。
我在前面提到Windows大致分為Kernel、User和GDI子系統。kernel接口在/ Platform SDK / Windows Base Services中,User界面函數在 / Platform SDK / User Interface Services中,GDI位于 / Platform SDK / Graphics and Multimedia Services / GDI中。
現在是開(kāi)始寫(xiě)些程序的時(shí)候了。為了便于對比,讓我們以一個(gè)非常短的Windows程序和一個(gè)簡(jiǎn)短的文字模式程序開(kāi)始。這會(huì )幫助我們找到使用開(kāi)發(fā)環(huán)境并感受建立和編譯程序機制的正確方向。
文字模式(Character-Mode)模型
程序寫(xiě)作者們喜愛(ài)的一本書(shū)是《The C Programming Language》(Prentice Hall,1978年和1988年),由Brian W. Kernighan和Dennis M. Ritchie(親切地稱(chēng)為K&R)編著(zhù)。該書(shū)的第一章以一個(gè)顯示「hello, world」的C語(yǔ)言程序開(kāi)始。
這里是在《The C Programming Language》第一版第6頁(yè)中出現的程序:
main (){ printf ("hello, world\n") ;}以前C程序寫(xiě)作者在使用printf等C執行期鏈接庫函數時(shí),無(wú)需先聲明它們。但這是90年代,我們愿意給編譯器一個(gè)在我們的程序中標出錯誤的機會(huì )。這里是在K&R第二版中修正的程序:
#includemain (){ printf ("hello, world\n") ;}
該程序仍然是那么短。但它可通過(guò)編譯并執行得很好,但當今許多程序寫(xiě)作者更愿意清楚地說(shuō)明main函數的返回值,在這種情況下ANSI C規定該函數必須返回一個(gè)值:
#includeint main (){ printf ("hello, world\n") ; return 0 ;}
我們還可以包括main的參數,把程序弄得更長(cháng)一些,但讓我們暫且這樣就好了-包括一個(gè)include聲明、程序的進(jìn)入點(diǎn)、一個(gè)對執行期鏈接庫函數的呼叫和一個(gè)return語(yǔ)句。
同樣效果的Windows程序
Windows關(guān)于「hello, world」程序的等價(jià)程序有和文字模式版本完全相同的組件。它有一個(gè)include聲明、一個(gè)程序進(jìn)入點(diǎn)、一個(gè)函數呼叫和一個(gè)return語(yǔ)句。下面便是該程序:
/*------------------------------------------------------------------HelloMsg.c -- Displays "Hello, Windows 98!" in a message box (c) Charles Petzold, 1998--------------------------------------------------------------------*/#includeint WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){MessageBox (NULL, TEXT ("Hello, Windows 98!"), TEXT ("HelloMsg"), 0);return 0 ;}
在剖析該程序之前,讓我們看一下在Visual C++ Developer Studio中建立新程序的方式。
首先,從File菜單中選New。在 New對話(huà)框中,單擊Projects頁(yè)面標簽,選擇 Win32 Application。在Location欄中,選擇一個(gè)子目錄,在 Project Name欄中,輸入該項目的名稱(chēng),此時(shí)該名稱(chēng)是HelloMsg,這便是在 Location欄中顯示的目錄的子目錄。Create New Workspace復選框應該勾起來(lái),Platforms部分應該顯示 Win32,選擇OK。
將會(huì )出現一個(gè)標題為Win32 Application - Step 1 Of 1的對話(huà)框,指出要建立一個(gè)Empty Project,并按下Finish按鈕。
從File菜單中再次選擇New。在 New對話(huà)框中,選擇Files頁(yè)面標簽,選擇 C++ Source File。Add To Project復選框應被選中,并應顯示HelloMsg。在 File Name欄中輸入HelloMsg.c,選中OK。
現在您可輸入上面所示的HELLOMSG.C文件,您也可以選擇Insert菜單和 File As Text選項從本書(shū)附帶的CD-ROM上復制HELLOMSG.C的內容。
從結構上說(shuō),HELLOMSG.C與K&R的「hello,world」程序是相同的。表頭文件STDIO.H已被WINDOWS.H所代替,進(jìn)入點(diǎn)main被WinMain所代替,而且C語(yǔ)言執行時(shí)期鏈接庫函數printf被Windows API函數MessageBox所代替。然而,在程序中有許多新東西,包括幾個(gè)陌生的大寫(xiě)標識符。
讓我們從頭開(kāi)始。
表頭文件
HELLOMSG.C以一個(gè)前置處理器指示命令開(kāi)始,實(shí)際上在每個(gè)用C編寫(xiě)的Windows程序的開(kāi)頭都可看到:
#include
WINDOWS.H是主要的含入文件,它包含了其它Windows表頭文件,這些表頭文件的某些也包含了其它表頭文件。這些表頭文件中最重要的和最基本的是:
這些表頭文件定義了Windows的所有數據型態(tài)、函數呼叫、數據結構和常數標識符,它們是Windows文件中的一個(gè)重要部分。使用Visual C++ Developer Studio的Edit菜單中的Find in Files搜索這些表頭文件非常方便。您還可以在Developer Studio中打開(kāi)這些表頭文件并直接閱讀它們。
程序進(jìn)入點(diǎn)
正如在C程序中的進(jìn)入點(diǎn)是函數main一樣,Windows程序的進(jìn)入點(diǎn)是WinMain,總是像這樣出現:
int WINAPI WinMain ( HINSTANCE hInstance,HINSTANCE hPrevInstance, PSTR szCmdLine,int iCmdShow)
該進(jìn)入點(diǎn)在/ Platform SDK / User Interface Services / Windowing / Windows / Window Reference / Window Functions中有說(shuō)明。它在WINBASE.H中聲明如下:
intWINAPIWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd );
您會(huì )注意到我在HELLOMSG.C中做了許多小改動(dòng)。第三個(gè)參數在WINBASE.H中定義為L(cháng)PSTR,我將它改為PSTR。這兩種數據型態(tài)都定義在WINNT.H中,作為指向字符串的指針。LP前綴代表「長(cháng)指針」,這是16位Windows下的產(chǎn)物。
我還在WinMain聲明中改變了兩個(gè)參數的名稱(chēng)。許多Windows程序中的變量名使用一種稱(chēng)作「匈牙利表示法」的命名系統,該系統在變量名稱(chēng)前面增加了表示變量數據型態(tài)的短前綴,我將在第三章更詳細地討論這個(gè)概念?,F在僅需記住前綴i表示int、sz表示「以零結束的字符串」。
WinMain函數聲明為返回一個(gè)int值。WINAPI標識符在WINDEF.H定義,語(yǔ)句如下:
#define WINAPI __stdcall
該語(yǔ)句指定了一個(gè)呼叫約定,包括如何生產(chǎn)機械碼以在堆棧中放置函數呼叫的參數。許多Windows函數呼叫聲明為WINAPI。
WinMain的第一個(gè)參數被稱(chēng)作「執行實(shí)體句柄」。在Windows程序設計中,句柄僅是一個(gè)應用程序用來(lái)識別某些東西的數字。在這種情況下,該句柄唯一地標識該程序,還需要它在其它Windows函數呼叫中作為參數。在Windows的早期版本中,當同時(shí)運行同一程序多次時(shí),您便創(chuàng )建了該程序的「多個(gè)執行實(shí)體(multiple instances)」。同一應用程序的所有執行實(shí)體共享程序和只讀的內存(通常是例如菜單和對話(huà)框模板的資源)。程序通過(guò)檢查hPrevInstance參數就能夠確定自身的其它執行實(shí)體是否正在運行。然后它可以略過(guò)一些繁雜的工作并從前面的執行實(shí)體將某些數據移到自己的數據區域。
在32位Windows版本中,該概念已被拋棄。傳給WinMain的第二個(gè)參數總是NULL(定義為0)。
WinMain的第三個(gè)參數是用于執行程序的命令列。某些Windows應用程序利用它在程序啟動(dòng)時(shí)將文件加載內存。WinMain的第四個(gè)參數指出程序最初顯示的方式,可以是正常的或者是最大化地充滿(mǎn)整個(gè)畫(huà)面,或者是最小化顯示在工作列中。我們將在 第三章中介紹使用該參數的方法。
MessageBox函數
MessageBox函數用于顯示短信息。雖然,MessageBox顯示的小窗口不具有什么功能,實(shí)際上它被認為是一個(gè)對話(huà)框。
MessageBox的第一個(gè)參數通常是窗口句柄,我們將在第三章介紹其含義。第二個(gè)參數是在消息框主體中顯示的字符串,第三個(gè)參數是出現在消息框標題列上的字符串。在HELLMSG.C中,這些文字字符串的每一個(gè)都被封裝在一個(gè)TEXT宏中。通常您不必將所有字符串都封裝在TEXT宏中,但如果想將您的程序轉換為Unicode字符集,這確是一個(gè)好主意。我將在第二章詳細討論該問(wèn)題。
MessageBox的第四個(gè)參數可以是在WINUSER.H中定義的一組以前綴MB_開(kāi)始的常數的組合。您可從第一組中選擇一個(gè)常數指出希望在對話(huà)框中顯示的按鈕:
#define MB_OK 0x00000000L#define MB_OKCANCEL 0x00000001L#define MB_ABORTRETRYIGNORE 0x00000002L#define MB_YESNOCANCEL 0x00000003L#define MB_YESNO 0x00000004L#define MB_RETRYCANCEL 0x00000005L
如果在HELLOMSG中將第四個(gè)參數設置為0,則僅顯示「OK」按鈕??梢允褂肅語(yǔ)言的OR(|)操作符號將上面顯示的一個(gè)常數與代表內定按鈕的常數組合:
#define MB_DEFBUTTON1 0x00000000L#define MB_DEFBUTTON2 0x00000100L#define MB_DEFBUTTON3 0x00000200L#define MB_DEFBUTTON4 0x00000300L
還可以使用一個(gè)常數指出消息框中圖示的外觀(guān):
#define MB_ICONHAND 0x00000010L#define MB_ICONQUESTION 0x00000020L#define MB_ICONEXCLAMATION 0x00000030L#define MB_ICONASTERISK 0x00000040L
這些圖示中的某些有替代名稱(chēng):
#define MB_ICONWARNING MB_ICONEXCLAMATION#define MB_ICONERROR MB_ICONHAND#define MB_ICONINFORMATION MB_ICONASTERISK#define MB_ICONSTOP MB_ICONHAND
雖然只有少數其它MB_常數,但您可以自己參考表頭文件或 / Platform SDK / User Interface Services / Windowing / Dialog Boxes / Dialog Box Reference / Dialog Box Functions里的文件。
在本程序中,MessageBox返回數值1,但更嚴格地說(shuō)它返回IDOK,IDOK在WINUSER.H中定義,等于1。根據在消息框中顯示的其它按鈕,MessageBox函數還可返回IDYES、IDNO、IDCANCEL、IDABORT、 IDRETRY或IDIGNORE。
這個(gè)小的Windows程序真的與K&R的「hello, world」程序有著(zhù)同等效果嗎?您也許認為不是,因為MessageBox函數并沒(méi)有「hello, world」中printf函數所具有的潛在格式化文字能力。但我們將在下一章中看到編寫(xiě)類(lèi)似printf的MessageBox版本的方法。
編譯、連結和執行
當您準備編譯HELLOMSG時(shí),您可從「Build」菜單中選擇「 Build Hellomsg.exe」,或者按F7,或者在「 Build」工具列中選擇「Build」圖示。(該圖示的外觀(guān)顯示在「 Build」菜單中。如果當前沒(méi)有顯示「Build」工具列,您可從「 Tools」菜單中選擇「Customize」并選擇「 Toolbars」頁(yè)面標簽,選中「Build」或者「 Build MiniBar」。)
另一種方法,您可從「Build」菜單中選擇「Execute Hellomsg.exe」,或者按「Ctrl+F5」,或者在「 Build」工具列單擊「Execute Program」圖標(該圖標看上去像一個(gè)紅的感嘆號),就會(huì )彈出一個(gè)消息框詢(xún)問(wèn)是否編譯該程序。
正常情況下,在編譯階段,編譯器從C原始碼文件產(chǎn)生一個(gè).OBJ(目標)文件。在連結階段,連結程序結合.OBJ文件和.LIB(庫)文件以建立.EXE(可執行)文件。通過(guò)在「 Project」頁(yè)面標簽上選擇「Settings」并單擊「 Link」頁(yè)面標簽可以查看這些庫文件的列表。特別地,您會(huì )注意到KERNEL32.LIB、USER32.LIB和GDI32.LIB。這些是三個(gè)主要Windows子系統的「引用鏈接庫」。它們包含了動(dòng)態(tài)鏈接庫的名稱(chēng)以及放進(jìn).EXE文件的引用信息。Windows使用該信息處理程序對KERNEL32.DLL、USER32.DLL、GDI32.DLL動(dòng)態(tài)鏈接庫中函數的呼叫。
在Visual C++ Developer Studio中,您可用不同的設定編譯和連結程序。內定情況下,它們是「Debug」和「Release」??蓤绦形募淮娣旁谝赃@些名稱(chēng)命名的子目錄下。在Debug設定下,信息被附加到 .EXE文件中,這些信息有助于測試程序和追蹤原始碼。
如果您喜歡在命令列下工作,附上的CD-ROM包含所有范例程序的.MAK(make)文件。(可通過(guò)「 Tools」菜單選擇「Options」,再選擇「 Build」頁(yè)面標簽,來(lái)告訴Developer Studio生成make文件。這里有一個(gè)復選框需要勾選)。您需要執行在Developer Studio的BIN子目錄下的VCVARS32.BAT來(lái)設置環(huán)境變量。要從命令列執行make文件,可以轉到HELLOMSG目錄并執行:
NMAKE /f HelloMsg.mak CFG="HelloMsg - Win32 Debug"
或者
NMAKE /f HelloMsg.mak CFG="HelloMsg - Win32 Release"
然后您可通過(guò)輸入:
DEBUG\HELLOMSG
或者
RELEASE\HELLOMSG
從命令列執行.EXE文件。
我已經(jīng)在本書(shū)附上的CD-ROM中對項目文件中的內定Debug設定做了一個(gè)改動(dòng)。在「 Project Settings」對話(huà)框中,選擇「C/C++」頁(yè)面標簽后,在「 Preprocessor Definitions」欄中,我已定義了標識符UNICODE。我將在下一章中對此有更多的解釋。
聯(lián)系客服