windows核心编程-第二章 Unicode
第2章U n i c o d e
? ? 隨著M i c r o s o f t公司的Wi n d o w s操作系統(tǒng)在全世界日益廣泛的流行,對(duì)于軟件開(kāi)發(fā)人員來(lái)說(shuō),將目標(biāo)瞄準(zhǔn)國(guó)際上的各個(gè)不同市場(chǎng),已經(jīng)成為一個(gè)越來(lái)越重要的問(wèn)題。美國(guó)的軟件版本比國(guó)際版本提前6個(gè)月推向市場(chǎng),這曾經(jīng)是個(gè)司空見(jiàn)慣的現(xiàn)象。但是,由于各國(guó)對(duì)Wi n d o w s操作系統(tǒng)提供了越來(lái)越多的支持,因此就更加容易為國(guó)際市場(chǎng)生產(chǎn)各種應(yīng)用軟件,從而縮短了軟件的美國(guó)版本與國(guó)際版本推出的時(shí)間間隔。
? ? Wi n d o w s操作系統(tǒng)始終不逾地提供各種支持,以幫助軟件開(kāi)發(fā)人員進(jìn)行應(yīng)用程序的本地化工作。應(yīng)用軟件可以從各種不同的函數(shù)中獲得特定國(guó)家的信息,并可觀察控制面板的設(shè)置,以確定用戶的首選項(xiàng)。Wi n d o w s甚至支持不同的字體,以適應(yīng)應(yīng)用的需要。
? ? 之所以將這一章放在本書的開(kāi)頭,是因?yàn)榭紤]到 U n i c o d e是開(kāi)發(fā)任何應(yīng)用程序時(shí)要采用的基本步驟。本書的每一章中幾乎都要講到關(guān)于U n i c o d e的問(wèn)題,而且書中給出的所有示例應(yīng)用程序都是“用U n i c o d e實(shí)現(xiàn)的”。如果你為Microsoft Windows 2000或Microsoft Windows CE開(kāi)發(fā)應(yīng)用程序,你應(yīng)該使用U n i c o d e進(jìn)行開(kāi)發(fā)。如果你為Microsoft Windows 98開(kāi)發(fā)應(yīng)用程序,你必須對(duì)某些問(wèn)題作出決定。本章也要講述Windows 98的有關(guān)問(wèn)題。
2.1 字符集
? ? 軟件的本地化要解決的真正問(wèn)題,實(shí)際上就是如何來(lái)處理不同的字符集。多年來(lái),許多人一直將文本串作為一系列單字節(jié)字符來(lái)進(jìn)行編碼,并在結(jié)尾處放上一個(gè)零。對(duì)于我們來(lái)說(shuō),這已經(jīng)成了習(xí)慣。當(dāng)調(diào)用s t r l e n函數(shù)時(shí),它在以0結(jié)尾的單字節(jié)字符數(shù)組中返回字符的數(shù)目。
? ? 問(wèn)題是,有些文字和書寫規(guī)則(比如日文中的漢字就是個(gè)典型的例子)的字符集中的符號(hào)太多了,因此單字節(jié)(它提供的符號(hào)最多不能超過(guò)2 5 6個(gè))是根本不敷使用的。為此出現(xiàn)了雙字節(jié)字符集(D B C S) ,以支持這些文字和書寫規(guī)則。
2.1.1 單字節(jié)與雙字節(jié)字符集
? ? 在雙字節(jié)字符集中,字符串中的每個(gè)字符可以包含一個(gè)字節(jié)或包含兩個(gè)字節(jié)。例如,日文中的漢字,如果第一個(gè)字符在0 x 8 1與0 x 9 F之間,或者在0 x E 0與0 x F C之間,那么就必須觀察下一個(gè)字節(jié),才能確定字符串中的這個(gè)完整的字符。使用雙字節(jié)字符集,對(duì)于程序員來(lái)說(shuō)簡(jiǎn)直是個(gè)很大的難題,因?yàn)橛行┳址挥幸粋€(gè)字節(jié)寬,而有些字符則是兩個(gè)字節(jié)寬。
? ? 如果只是調(diào)用s t r l e n函數(shù),那么你無(wú)法真正了解字符串中究竟有多少字符,它只能告訴你到達(dá)結(jié)尾的0之前有多少個(gè)字節(jié)。A N S I的C運(yùn)行期庫(kù)中沒(méi)有配備相應(yīng)的函數(shù),使你能夠?qū)﹄p字節(jié)字符集進(jìn)行操作。但是,Microsoft Visual C++的運(yùn)行期庫(kù)卻包含許多函數(shù),如_ m b s l e n ,它可以用來(lái)操作多字節(jié)(既包括單字節(jié)也包括雙字節(jié))字符串。
? ? 為了幫助你對(duì)D B C S字符串進(jìn)行操作,Wi n d o w s提供了下面的一組幫助函數(shù)(見(jiàn)表2 - 1 )。
? ? 前兩個(gè)函數(shù)CharNext和Char Prev允許前向或逆向遍歷DBCS字符串,方法是每次一個(gè)字符。第三個(gè)函數(shù) IsDBCSLeadByte, 在字節(jié)返回到一個(gè)兩字字節(jié)符的第一個(gè)字節(jié)時(shí)將返回T R U E。
? ? 盡管這些函數(shù)使得我們對(duì) D B C S的操作更容易,但還需要,一個(gè)更好的方法讓我們來(lái)看看U n i c o d e。
2.1.2 Unicode:寬字節(jié)字符集
? ? U n i c o d e是A p p l e和X e r o x公司于1 9 8 8年建立的一個(gè)技術(shù)標(biāo)準(zhǔn)。 1 9 9 1年,成立了一個(gè)集團(tuán)機(jī)構(gòu)負(fù)責(zé)U n i c o d e的開(kāi)發(fā)和推廣應(yīng)用。該集團(tuán)由A p p l e、C o m p a q、H P、I B M、M i c r o s o f t、O r a c l e、Silicon Graphics, Inc.、S y b a s e、U n i s y s和X e r o x等公司組成(若要了解該集團(tuán)的全部成員,請(qǐng)通過(guò)網(wǎng)址w w w. U n i c o d e . o rg查找) 。該集團(tuán)負(fù)責(zé)維護(hù)U n i c o d e標(biāo)準(zhǔn)。U n i c o d e的完整描述可以參閱A d d i s o n We s l e y出版的《Unicode Standard》一書(該書可以通過(guò)網(wǎng)址w w w. U n i c o d e . o rg訂購(gòu)) 。
? ? U n i c o d e提供了一種簡(jiǎn)單而又一致的表示字符串的方法。U n i c o d e字符串中的所有字符都是1 6位的(兩個(gè)字節(jié)) 。它沒(méi)有專門的字節(jié)來(lái)指明下一個(gè)字節(jié)是屬于同一個(gè)字符的組成部分,還是一個(gè)新字符。這意味著你只需要對(duì)指針進(jìn)行遞增或遞減,就可以遍歷字符串中的各個(gè)字符,不再需要調(diào)用C h a r N e x t、C h a r P r e v和I s D B C S L e a d B y t e之類的函數(shù)。由于U n i c o d e用一個(gè)1 6位的值來(lái)表示每個(gè)字符,因此總共可以得到65 000個(gè)字符,這樣,它就能夠?qū)κ澜绺鲊?guó)的書面文字中的所有字符進(jìn)行編碼,遠(yuǎn)遠(yuǎn)超過(guò)了單字節(jié)字符集的2 5 6個(gè)字符的數(shù)目。
目前,已經(jīng)為阿拉伯文、中文拼音、西里爾字母(俄文) 、希臘文、西伯萊文、日文、韓文和拉丁文(英文)字母定義了U n i c o d e代碼點(diǎn) 。這些字符集中還包含了大量的標(biāo)點(diǎn)符號(hào)、數(shù)學(xué)符號(hào)、技術(shù)符號(hào)、箭頭、裝飾標(biāo)志、區(qū)分標(biāo)志和其他許多字符。如果將所有這些字母和符號(hào)加在一起,總計(jì)約達(dá)3 5 0 0 0個(gè)不同的代碼點(diǎn),這樣,總計(jì)65 000多個(gè)代碼點(diǎn)中,大約還有一半可供將來(lái)擴(kuò)充時(shí)使用。
? ? 這65 536個(gè)字符可以分成不同的區(qū)域。表2-2 顯示了這樣的區(qū)域的一部分以及分配給這些區(qū)域的字符。
? ? 目前尚未分配的代碼點(diǎn)大約還有29 000個(gè),不過(guò)它們是保留供將來(lái)使用的。另外,大約有6 0 0 0個(gè)代碼點(diǎn)是保留供個(gè)人使用的。
2.2 為什么使用U n i c o d e
????當(dāng)開(kāi)發(fā)應(yīng)用程序時(shí),當(dāng)然應(yīng)該考慮利用 U n i c o d e的優(yōu)點(diǎn)。即使現(xiàn)在你不打算對(duì)應(yīng)用程序進(jìn)行本地化,開(kāi)發(fā)時(shí)將U n i c o d e放在心上,肯定可以簡(jiǎn)化將來(lái)的代碼轉(zhuǎn)換工作。此外,U n i c o d e還
具備下列功能:
? 可以很容易地在不同語(yǔ)言之間進(jìn)行數(shù)據(jù)交換。
? 使你能夠分配支持所有語(yǔ)言的單個(gè)二進(jìn)制. e x e文件或D L L文件。
? 提高應(yīng)用程序的運(yùn)行效率(本章后面還要詳細(xì)介紹) 。
2.3 Windows 2000與U n i c o d e
????Windows 2000是使用U n i c o d e從頭進(jìn)行開(kāi)發(fā)的,用于創(chuàng)建窗口、顯示文本、進(jìn)行字符串操作等的所有核心函數(shù)都需要U n i c o d e字符串。如果調(diào)用任何一個(gè)Wi n d o w s函數(shù)并給它傳遞一個(gè)A N S I字符串,那么系統(tǒng)首先要將字符串轉(zhuǎn)換成U n i c o d e,然后將U n i c o d e字符串傳遞給操作系統(tǒng)。如果希望函數(shù)返回A N S I字符串,系統(tǒng)就會(huì)首先將U n i c o d e字符串轉(zhuǎn)換成A N S I字符串,然后將結(jié)果返回給你的應(yīng)用程序。所有這些轉(zhuǎn)換操作都是在你看不見(jiàn)的情況下發(fā)生的。當(dāng)然,進(jìn)行這些字符串的轉(zhuǎn)換需要占用系統(tǒng)的時(shí)間和內(nèi)存。
????例如,如果調(diào)用C r e a t e Wi n d o w E x函數(shù),并傳遞類名字和窗口標(biāo)題文本的非U n i c o d e字符串,那么C r e a t e Wi n d o w E x必須分配內(nèi)存塊(在你的進(jìn)程的默認(rèn)堆中) ,將非U n i c o d e字符串轉(zhuǎn)換成U n i c o d e字符串,并將結(jié)果存儲(chǔ)在分配到的內(nèi)存塊中,然后調(diào)用U n i c o d e版本的C r e a t e Wi n d o w E x函數(shù)。
對(duì)于用字符串填入緩存的函數(shù)來(lái)說(shuō),系統(tǒng)必須首先將 U n i c o d e字符串轉(zhuǎn)換成非U n i c o d e字符串,然后你的應(yīng)用程序才能處理該字符串。由于系統(tǒng)必須執(zhí)行所有這些轉(zhuǎn)換操作,因此你的應(yīng)用程序需要更多的內(nèi)存,并且運(yùn)行的速度比較慢。通過(guò)從頭開(kāi)始用U n i c o d e來(lái)開(kāi)發(fā)應(yīng)用程序,就能夠使你的應(yīng)用程序更加有效地運(yùn)行。
2.4 Windows 98與U n i c o d e
? ? Windows 98不是一種全新的操作系統(tǒng)。它繼承了1 6位Wi n d o w s操作系統(tǒng)的特性,它不是用來(lái)處理U n i c o d e的。如果要增加對(duì)U n i c o d e的支持,其工作量非常大,因此在該產(chǎn)品的特性列表中沒(méi)有包括這個(gè)支持項(xiàng)目。由于這個(gè)原因,Windows 98像它的前任產(chǎn)品一樣,幾乎都是使用A N S I字符串來(lái)進(jìn)行所有的內(nèi)部操作的。
? ? 仍然可以編寫用于處理U n i c o d e字符和字符串的Wi n d o w s應(yīng)用程序,不過(guò),使用Wi n d o w s函數(shù)要難得多。例如,如果想要調(diào)用 C r e a t e Wi n d o w E x函數(shù)并將A N S I字符串傳遞給它,這個(gè)調(diào)用的速度非常快,不需要從你進(jìn)程的默認(rèn)堆棧中分配緩存,也不需要進(jìn)行字符串轉(zhuǎn)換。但是,如果想要調(diào)用C r e a t e Wi n d o w E x函數(shù)并將U n i c o d e字符串傳遞給它,就必須明確分配緩存,并調(diào)用函數(shù),以便執(zhí)行從U n i c o d e到A N S I字符串的轉(zhuǎn)換操作。然后可以調(diào)用 C r e a t e Wi n d o w E x,傳遞A N S I字符串。當(dāng)C r e a t e Wi n d o w E x函數(shù)返回時(shí),就能釋放臨時(shí)緩存。這比使用Windows 2000上的U n i c o d e要麻煩得多。本章的后面要介紹如何在Windows 98下進(jìn)行這些轉(zhuǎn)換。
? ? 雖然大多數(shù)U n i c o d e函數(shù)在Windows 98中不起任何作用,但是仍有少數(shù)U n i c o d e函數(shù)確實(shí)非常有用。這些函數(shù)是:
?
?
? ? 可惜的是,這些函數(shù)中有許多函數(shù)在Windows 98中會(huì)出現(xiàn)各種各樣的錯(cuò)誤。有些函數(shù)無(wú)法使用某些字體,有些函數(shù)會(huì)破壞內(nèi)存堆棧,有些函數(shù)會(huì)使打印機(jī)驅(qū)動(dòng)程序崩潰,等等。如果要使用這些函數(shù),必須對(duì)它們進(jìn)行大量的測(cè)試。即使這樣,可能仍然無(wú)法解決問(wèn)題。因此必須向用戶說(shuō)明這些情況。
2.5 Windows CE與U n i c o d e
? ? Windows CE操作系統(tǒng)是為小型設(shè)備開(kāi)發(fā)的,這些設(shè)備的內(nèi)存很小,并且不帶磁盤存儲(chǔ)器。你可能認(rèn)為,由于M i c r o s o f t公司的主要目標(biāo)是建立一種盡可能小的操作系統(tǒng),因此它會(huì)使用A N S I作為自己的字符集。但是M i c r o s o f t公司并非鼠目寸光,他們懂得,Windows CE的設(shè)備要在世界各地銷售,他們希望降低軟件開(kāi)發(fā)成本,這樣就能更加容易地開(kāi)發(fā)應(yīng)用程序。為此,Windows CE本身就是使用U n i c o d e的一種操作系統(tǒng)。
? ? 但是,為了使Windows CE盡量做得小一些,M i c r o s o f t公司決定完全不支持ANSI Wi n d o w s函數(shù)。因此,如果要為Windows CE開(kāi)發(fā)應(yīng)用程序,必須懂得U n i c o d e,并且在整個(gè)應(yīng)用程序中使用U n i c o d e。
2.6 需要注意的問(wèn)題
? ? 下面讓我們進(jìn)一步明確一下“M i c r o s o f t公司對(duì)U n i c o d e支持的情況”:
? ? ? Windows 2000既支持U n i c o d e,也支持A N S I,因此可以為任意一種開(kāi)發(fā)應(yīng)用程序。
? ? ? Windows 98只支持A N S I,只能為A N S I開(kāi)發(fā)應(yīng)用程序。
? ? ? Windows CE只支持U n i c o d e,只能為U n i c o d e開(kāi)發(fā)應(yīng)用程序。
? ? 雖然M i c r o s o f t公司試圖讓軟件開(kāi)發(fā)人員能夠非常容易地開(kāi)發(fā)在這3種平臺(tái)上運(yùn)行的軟件,但是U n i c o d e與A N S I之間的差異使得事情變得困難起來(lái),并且這種差異通常是我遇到的最大的問(wèn)題之一。請(qǐng)不要誤解,M i c r o s o f t公司堅(jiān)定地支持U n i c o d e,并且我也堅(jiān)決鼓勵(lì)你使用它。不過(guò)你應(yīng)該懂得,你可能遇到一些問(wèn)題,需要一定的時(shí)間來(lái)解決這些問(wèn)題。建議你盡可能使用U n i c o d e。如果運(yùn)行Windows 98,那么只有在必要時(shí)才需轉(zhuǎn)換到A N S I。不過(guò),還有另一個(gè)小問(wèn)題你應(yīng)該了解,那就是C O M。
2.7 對(duì)C O M的簡(jiǎn)單說(shuō)明
? ? 當(dāng)M i c r o s o f t公司將C O M從1 6位Wi n d o w s轉(zhuǎn)換成Wi n 3 2時(shí),公司作出了一個(gè)決定,即需要字符串的所有C O M接口方法都只能接受U n i c o d e字符串。這是個(gè)了不起的決定,因?yàn)?/span>C O M通常用于使不同的組件能夠互相進(jìn)行通信,而U n i c o d e則是傳遞字符串的最佳手段。如果你為Windows 2000或Windows CE開(kāi)發(fā)應(yīng)用程序,并且也使用C O M,那么你將會(huì)如虎添翼。在你的整個(gè)源代碼中使用U n i c o d e,將使與操作系統(tǒng)進(jìn)行通信和與C O M對(duì)象進(jìn)行通信的操作變成一件輕而易舉的事情。
? ? 如果你為Windows 98開(kāi)發(fā)應(yīng)用程序,并且也使用C O M,那么將會(huì)遇到一些問(wèn)題。C O M要求使用U n i c o d e字符串,而操作系統(tǒng)的大多數(shù)函數(shù)要求使用A N S I字符串。那是多么難辦的事情啊!我曾經(jīng)從事過(guò)若干個(gè)項(xiàng)目的開(kāi)發(fā),在這些項(xiàng)目中,我編寫了許多代碼,僅僅是為了來(lái)回進(jìn)行字符串的轉(zhuǎn)換。
2.8 如何編寫U n i c o d e源代碼
? ? M i c r o s o f t公司為U n i c o d e設(shè)計(jì)了Windows API,這樣,可以盡量減少對(duì)你的代碼的影響。實(shí)際上,你可以編寫單個(gè)源代碼文件,以便使用或者不使用U n i c o d e來(lái)對(duì)它進(jìn)行編譯。只需要定義兩個(gè)宏(U N I C O D E和_ U N I C O D E) ,就可以修改然后重新編譯該源文件。
2.8.1 C運(yùn)行期庫(kù)對(duì)U n i c o d e的支持
? ? 為了利用U n i c o d e字符串,定義了一些數(shù)據(jù)類型。標(biāo)準(zhǔn)的C頭文件S t r i n g . h已經(jīng)作了修改,以便定義一個(gè)名字為w c h a r _ t的數(shù)據(jù)類型,它是一個(gè)U n i c o d e字符的數(shù)據(jù)類型:
? ? typedef unsigned short wchart_t;
? ? 例如,如果想要?jiǎng)?chuàng)建一個(gè)緩存,用于存放最多為 9 9個(gè)字符的U n i c o d e字符串和一個(gè)結(jié)尾為零的字符,可以使用下面這個(gè)語(yǔ)句:
? ? wchar_t szBuffer[100];
? ? 該語(yǔ)句創(chuàng)建了一個(gè)由 1 0 0個(gè)1 6位值組成的數(shù)組。當(dāng)然,標(biāo)準(zhǔn)的C運(yùn)行期字符串函數(shù),如s t r c p y、s t r c h r和s t r c a t等,只能對(duì)A N S I字符串進(jìn)行操作,不能正確地處理U n i c o d e字符串。因此,ANSI C也擁有一組補(bǔ)充函數(shù)。清單2 - 1顯示了一些標(biāo)準(zhǔn)的ANSI C字符串函數(shù),后面是它們的等價(jià)U n i c o d e函數(shù)。
?
? ? 請(qǐng)注意,所有的U n i c o d e函數(shù)均以w c s開(kāi)頭,w c s是寬字符串的英文縮寫。若要調(diào)用U n i c o d e函數(shù),只需用前綴w c s來(lái)取代A N S I字符串函數(shù)的前綴s t r即可。
? ? 注意 大多數(shù)軟件開(kāi)發(fā)人員可能已經(jīng)不記得這樣一個(gè)非常重要的問(wèn)題了,那就是
? ? M i c r o s o f t公司提供的C運(yùn)行期庫(kù)與A N S I的標(biāo)準(zhǔn)C運(yùn)行期庫(kù)是一致的。ANSI C規(guī)定,C運(yùn)行期庫(kù)支持U n i c o d e字符和字符串。這意味著始終都可以調(diào)用C運(yùn)行期函數(shù),以便對(duì)U n i c o d e字符和字符串進(jìn)行操作,即使是在Windows 98上運(yùn)行,也可以調(diào)用這些函數(shù)。換句話說(shuō),w c s c a t、w c s l e n和w c s t o k等函數(shù)都能夠在Windows 98上很好地運(yùn)行,這些都是必須關(guān)心的操作系統(tǒng)函數(shù)。
? ? 對(duì)于包含了對(duì)s t r函數(shù)或w c s函數(shù)進(jìn)行顯式調(diào)用的代碼來(lái)說(shuō),無(wú)法非常容易地同時(shí)為A N S I和U n i c o d e對(duì)這些代碼進(jìn)行編譯。本章前面說(shuō)過(guò),可以創(chuàng)建同時(shí)為A N S I和U n i c o d e進(jìn)行編譯的單個(gè)源代碼文件。若要建立雙重功能,必須包含T C h a r. h文件,而不是包含S t r i n g . h文件。T C h a r. h文件的唯一作用是幫助創(chuàng)建A N S I / U n i c o d e通用源代碼文件。它包含你應(yīng)該用在源代碼中的一組宏,而不應(yīng)該直接調(diào)用s t r函數(shù)或者w c s函數(shù)。如果在編譯源代碼文件時(shí)定義了_ U N I C O D E,這些宏就會(huì)引用w c s這組函數(shù)。如果沒(méi)有定義_ U N I C O D E,那么這些宏將引用s t r這組宏。
? ? 例如,在T C h a r. h中有一個(gè)宏稱為_ t c s c p y。如果在包含該頭文件時(shí)沒(méi)有定義_ U N I C O D E ,那么_ t c s c p y就會(huì)擴(kuò)展為A N S I的s t r c p y函數(shù)。但是如果定義了_UNICODE, _tcscpy將擴(kuò)展為U n i c o d e的w c s c p y函數(shù)。擁有字符串參數(shù)的所有C運(yùn)行期函數(shù)都在T C h a r. h文件中定義了一個(gè)通用宏。如果使用通用宏,而不是A N S I / U n i c o d e的特定函數(shù)名,就能夠順利地創(chuàng)建可以為 A N S I或U n i c o d e進(jìn)行編譯的源代碼。
? ? 但是,除了使用這些宏之外,還有一些操作是必須進(jìn)行的。 T C h a r. h文件包含了另外一些宏。若要定義一個(gè)A N S I / U n i c o d e通用的字符串?dāng)?shù)組,請(qǐng)使用下面的T C H A R數(shù)據(jù)類型。如果定義了_ U N I C O D E,T C H A R將聲明為下面的形式:
? ? typedef wchar_t TCHAR;
? ? 如果沒(méi)有定義_ U N I C O D E,則T C H A R將聲明為下面的形式:
? ? typedef char TCHAR;
? ? 使用該數(shù)據(jù)類型,可以像下面這樣分配一個(gè)字符串:
? ? TCHAR szString[100];
? ? 也可以創(chuàng)建對(duì)字符串的指針:
? ? TCHAR *szError = “Error”;
? ? 不過(guò)上面這行代碼存在一個(gè)問(wèn)題。按照默認(rèn)設(shè)置, M i c r o s o f t公司的C + +編譯器能夠編譯所有的字符串,就像它們是A N S I字符串,而不是U n i c o d e字符串。因此,如果沒(méi)有定義_ U N I C O D E,該編譯器將能正確地編譯這一行代碼。但是,如果定義了_ U N I C O D E,就會(huì)產(chǎn)生一個(gè)錯(cuò)誤。若要生成一個(gè)U n i c o d e字符串而不是A N S I字符串,必須將該代碼行改寫為下面的樣子:
? ? TCHAR *szError = L”Error”;
? ? 字符串(literal string)前面的大寫字母L,用于告訴編譯器該字符串應(yīng)該作為U n i c o d e字符串來(lái)編譯。當(dāng)編譯器將字符串置于程序的數(shù)據(jù)部分中時(shí),它在每個(gè)字符之間分散插入零字節(jié)。這種變更帶來(lái)的問(wèn)題是,現(xiàn)在只有當(dāng)定義了_ U N I C O D E時(shí),程序才能成功地進(jìn)行編譯。我們需要另一個(gè)宏,以便有選擇地在字符串的前面加上大寫字母L。這項(xiàng)工作由_ T E X T宏來(lái)完成,_ T E X T宏也在T C h a r. h文件中做了定義。如果定義了_ U N I C O D E,那么_ T E X T定義為下面的形式:
? ? #define_TEXT(x) L ## x
? ? 如果沒(méi)有定義_ U N I C O D E,_ T E X T將定義為
? ? #define _TEXT(x) x
? ? 使用該宏,可以改寫上面這行代碼,這樣,無(wú)論是否定義了 _ U N I C O D E宏,它都能夠正確地進(jìn)行編譯。如下所示:
? ? TCHAR *szError = _TEXT(“Error”);
? ? _ T E X T宏也可以用于字符。例如,若要檢查一個(gè)字符串的第一個(gè)字符是否是大寫字母J,只需編寫下面的代碼即可:
?
2.8.2 Wi n d o w s定義的U n i c o d e數(shù)據(jù)類型
? ? Wi n d o w s頭文件定義了表2 - 3列出的數(shù)據(jù)類型。
? ? ?
? ? 這些數(shù)據(jù)類型是指U n i c o d e字符和字符串。Wi n d o w s頭文件也定義了A N S I / U n i c o d e通用數(shù)據(jù)類型P T S T R和P C T S T R。這些數(shù)據(jù)類型既可以指A N S I字符串,也可以指U n i c o d e字符串,這取決于當(dāng)編譯程序模塊時(shí)是否定義了U N I C O D E宏。
? ? 請(qǐng)注意,這里的U N I C O D E宏沒(méi)有前置的下劃線。_ U N I C O D E宏用于C運(yùn)行期頭文件,而U N I C O D E宏則用于Wi n d o w s頭文件。當(dāng)編譯源代碼模塊時(shí),通常必須同時(shí)定義這兩個(gè)宏。
2.8.3 Wi n d o w s中的U n i c o d e函數(shù)和A N S I函數(shù)
? ? 前面已經(jīng)講過(guò),有兩個(gè)函數(shù)稱為C r e a t e Wi n d o w E x,一個(gè)C r e a t e Wi n d o w E x接受U n i c o d e字符串,另一個(gè)C r e a t e Wi n d o w E x接受A N S I字符串。情況確實(shí)如此,不過(guò),這兩個(gè)函數(shù)的原型實(shí)際上是下面的樣子:
?
? ? C r e a t e Wi n d o w E x W是接受U n i c o d e字符串的函數(shù)版本。函數(shù)名結(jié)尾處的大寫字母W是英文w i d e(寬)的縮寫。每個(gè)U n i c o d e字符的長(zhǎng)度是1 6位,因此,它們常常稱為寬字符。C r e a t e Wi n d o w E x A的結(jié)尾處的大寫字母A表示該函數(shù)可以接受A N S I字符串。但是,在我們的代碼中,通常只包含了對(duì) C r e a t e Wi n d o w E x的調(diào)用,而不是直接調(diào)用C r e a t e Wi n d o w E x W或者C r e a t e Wi n d o w E x A。在Wi n U s e r. h文件中,C r e a t e Wi n d o w E x實(shí)際上是定義為下面這種形式的一個(gè)宏:
?
? ? 當(dāng)編譯源代碼模塊時(shí), U N I C O D E 是否已經(jīng)作了定義,將決定你調(diào)用的是哪個(gè)C r e a t e Wi n d o w E x版本。當(dāng)轉(zhuǎn)用一個(gè)1 6位的Wi n d o w s應(yīng)用程序時(shí),你在編譯期間可能沒(méi)有定義U N I C O D E。對(duì)C r e a t e Wi n d o w E x函數(shù)的任何調(diào)用都會(huì)將該宏擴(kuò)展為對(duì)C r e a t e Wi n d o w E x A的調(diào)用,即對(duì)C r e a t e Wi n d o w E x的A N S I版本的調(diào)用。由于1 6位Wi n d o w s只提供了C r e a t e Wi n d o w s E x的A N S I版本,因此可以比較容易地轉(zhuǎn)用它的應(yīng)用程序。在Windows 2000下,M i c r o s o f t的C r e a t e Wi n d o w E x A源代碼只不過(guò)是一個(gè)形實(shí)替換程序?qū)踊蚍g層,用于分配內(nèi)存,以便將A N S I字符串轉(zhuǎn)換成U n i c o d e字符串。該代碼然后調(diào)用C r e a t eWi n d o w E x W,并傳遞轉(zhuǎn)換后的字符串。當(dāng)C r e a t e Wi n d o w E x W返回時(shí),C r e a t e Wi n d o w E x A便釋放它的內(nèi)存緩存,并將窗口句柄返回給你。
? ? 如果要?jiǎng)?chuàng)建其他軟件開(kāi)發(fā)人員將要使用的動(dòng)態(tài)鏈接庫(kù)( D L L) ,請(qǐng)考慮使用下面的方法。在D L L中提供兩個(gè)輸出函數(shù)。一個(gè)是A N S I版本,另一個(gè)是U n i c o d e版本。在A N S I版本中,只需要分配內(nèi)存,執(zhí)行必要的字符串轉(zhuǎn)換,并調(diào)用該函數(shù)的U n i c o d e版本(本章后面部分介紹這個(gè)進(jìn)程) 。
? ? 在Windows 98下,M i c r o s o f t的C r e a t e Wi n d o w E x A源代碼是執(zhí)行操作的函數(shù)。Windows 98提供了接受U n i c o d e參數(shù)的所有Wi n d o w s函數(shù)的進(jìn)入點(diǎn),但是這些函數(shù)并不將 U n i c o d e字符串轉(zhuǎn)換成A N S I字符串,它們只返回運(yùn)行失敗的消息。調(diào)用G e t L a s t E r r o r將返回E R R O R _C A L L _ N O T _ I M P L E M E N T E D。這些函數(shù)中只有A N S I版本的函數(shù)才能正確地運(yùn)行。如果編譯的代碼調(diào)用了任何寬字符函數(shù),應(yīng)用程序?qū)o(wú)法在Windows 98下運(yùn)行。Windows API中的某些函數(shù),比如Wi n E x e c和O p e n F i l e等,只是為了實(shí)現(xiàn)與1 6位Wi n d o w s程序的向后兼容而存在,因此,應(yīng)該避免使用。應(yīng)該使用對(duì)C r e a t e P r o c e s s和C r e a t e F i l e函數(shù)的調(diào)用來(lái)取代對(duì)Wi n E x e c和O p e n F i l e函數(shù)的調(diào)用。從系統(tǒng)內(nèi)部來(lái)講,老的函數(shù)完全可以調(diào)用新的函數(shù)。
? ? ?老的函數(shù)存在的一個(gè)大問(wèn)題是,它們不接受 U n i c o d e字符串。當(dāng)調(diào)用這些函數(shù)時(shí),必須傳遞A N S I字符串。另一方面,所有新的和未過(guò)時(shí)的函數(shù)在Windows 2000中都同時(shí)擁有A N S I和U n i c o d e兩個(gè)版本。
2.8.4 Wi n d o w s字符串函數(shù)
????Wi n d o w s還提供了一組范圍很廣的字符串操作函數(shù)。這些函數(shù)與C運(yùn)行期字符串函數(shù)(如s t r c p y和w c s c p y)很相似。但是該操作系統(tǒng)函數(shù)是操作系統(tǒng)的一個(gè)組成部分,操作系統(tǒng)的許多組件都使用這些函數(shù),而不使用C運(yùn)行期庫(kù)。建議最好使用操作系統(tǒng)函數(shù),而不要使用C運(yùn)行期字符串函數(shù)。這將有助于稍稍提高你的應(yīng)用程序的運(yùn)行性能,因?yàn)椴僮飨到y(tǒng)字符串函數(shù)常常被大型應(yīng)用程序比如操作系統(tǒng)的外殼進(jìn)程E x p l o r e r. e x e所使用。由于這些函數(shù)使用得很多,因此,在你的應(yīng)用程序運(yùn)行時(shí),它們可能已經(jīng)被裝入R A M。
????若要使用這些函數(shù),系統(tǒng)必須運(yùn)行 Windows 2000或Windows 98。如果安裝了I n t e r n e t Explorer 4.0或更新的版本,也可以在較早的Wi n d o w s版本中獲得這些函數(shù)。
在經(jīng)典的操作系統(tǒng)函數(shù)樣式中,操作系統(tǒng)字符串函數(shù)名既包含大寫字母,也包含小寫字母,它的形式類似這個(gè)樣子:S t r C a t、S t r C h r、S t r C m p和S t r C p y等。若要使用這些函數(shù),必須加上S h l WA p i . h頭文件。另外,如前所述,這些字符串函數(shù)既有A N S I版本,也有U n i c o d e版本,例如S t r C a t A和S t r C a t W。由于這些函數(shù)屬于操作系統(tǒng)函數(shù),因此,當(dāng)創(chuàng)建應(yīng)用程序時(shí),如果定義了U N I C O D E(不帶前置下劃線) ,那么它們的符號(hào)將擴(kuò)展為寬字符版本。
2.9 成為符合A N S I和U n i c o d e的應(yīng)用程序
? ? 即使你不打算立即使用U n i c o d e,最好也應(yīng)該著手將你的應(yīng)用程序轉(zhuǎn)換成符合U n i c o d e的應(yīng)用程序。下面是應(yīng)該遵循的一些基本原則:
? ? ? 將文本串視為字符數(shù)組,而不是c h a r s數(shù)組或字節(jié)數(shù)組。
? ? ? 將通用數(shù)據(jù)類型(如T C H A R和P T S T R)用于文本字符和字符串。
? ? ? 將顯式數(shù)據(jù)類型(如B Y T E和P B Y T E)用于字節(jié)、字節(jié)指針和數(shù)據(jù)緩存。
? ? ? 將T E X T宏用于原義字符和字符串。
? ? ? 執(zhí)行全局性替換(例如用P T S T R替換P S T R) 。
? ? ? 修改字符串運(yùn)算問(wèn)題。例如函數(shù)通常希望你在字符中傳遞一個(gè)緩存的大小,而不是字節(jié)。
? ? 這意味著你不應(yīng)該傳遞 s i z e o f ( s z B u ff e r ) ,而應(yīng)該傳遞(s i z e o f ( s z B u ff e r ) / s i z e o f ( T C H A R )。另外,如果需要為字符串分配一個(gè)內(nèi)存塊,并且擁有該字符串中的字符數(shù)目,那么請(qǐng)記住要按字節(jié)來(lái)分配內(nèi)存。這就是說(shuō),應(yīng)該調(diào)用malloc(nCharacters *sizeof(TCHAR)),而不是調(diào)用m a l l o c( n C h a r a c t e r s )。在上面所說(shuō)的所有原則中,這是最難記住的一條原則,如果操作錯(cuò)誤,編譯器將不發(fā)出任何警告。
? ? 當(dāng)我為本書的第一版編寫示例程序時(shí),我編寫的原始程序只能編譯為 A N S I程序。后來(lái),當(dāng)我開(kāi)始撰寫本章的內(nèi)容時(shí),我想我應(yīng)該鼓勵(lì)使用 U n i c o d e,并且打算創(chuàng)建一些示例程序,以便展示你可以非常容易地編寫既可以用U n i c o d e也可以用A N S I來(lái)編譯的程序。這時(shí)我發(fā)現(xiàn)最好的辦法是將本書的所有示例程序進(jìn)行轉(zhuǎn)換,使它們都能夠用U n i c o d e和A N S I進(jìn)行編譯。我用了大約4個(gè)小時(shí)將所有程序進(jìn)行了轉(zhuǎn)換。考慮到我以前從來(lái)沒(méi)有這方面的轉(zhuǎn)換經(jīng)驗(yàn),這個(gè)速度是相當(dāng)不錯(cuò)了。
2.9.1 Wi n d o w s字符串函數(shù)
? ? Wi n d o w s也提供了一組用于對(duì)U n i c o d e字符串進(jìn)行操作的函數(shù),表2 - 4對(duì)它們進(jìn)行了描述。
?
? ? 這些函數(shù)是作為宏來(lái)實(shí)現(xiàn)的,這些宏既可以調(diào)用函數(shù)的 U n i c o d e版本,也可以調(diào)用函數(shù)的A N S I版本,這要根據(jù)編譯源代碼模塊時(shí)是否已經(jīng)定義了U N I C O D E而定。例如,如果沒(méi)有定義U N I C O D E,l s t r c a t函數(shù)將擴(kuò)展為l s t r c a t A。如果定義了U N I C O D E,l s t r c a t將擴(kuò)展為l s t r c a t W。
? ? 有兩個(gè)字符串函數(shù),即l s t r c m p和l s t r c m p i,它們的行為特性與等價(jià)的C運(yùn)行期函數(shù)是不同的。C運(yùn)行期函數(shù)s t r c m p、s t r c m p i、w c s c m p和w c s c m p i只是對(duì)字符串中的代碼點(diǎn)的值進(jìn)行比較,這就是說(shuō),這些函數(shù)將忽略實(shí)際字符的含義,只是將第一個(gè)字符串中的每個(gè)字符的數(shù)值與第二個(gè)字符串中的字符的數(shù)值進(jìn)行比較。而Wi n d o w s函數(shù)l s t r c m p和l s t r c m p i是作為對(duì)Wi n d o w s函數(shù)C o m p a r e S t r i n g的調(diào)用來(lái)實(shí)現(xiàn)的。
?
? ? 該函數(shù)對(duì)兩個(gè) U n i c o d e字符串進(jìn)行比較。C o m p a r e S t r i n g的第一個(gè)參數(shù)用于設(shè)定語(yǔ)言I D(L C I D) ,這是個(gè)3 2位值,用于標(biāo)識(shí)一種特定的語(yǔ)言。C o m p a r e S t r i n g使用這個(gè)L C I D來(lái)比較這兩個(gè)字符串,方法是對(duì)照一種特定的語(yǔ)言來(lái)查看它們的字符的含義。這種操作方法比C運(yùn)行期函數(shù)簡(jiǎn)單地進(jìn)行數(shù)值比較更有意義。
? ? 當(dāng)l s t r c m p函數(shù)系列中的任何一個(gè)函數(shù)調(diào)用C o m p a r e S t r i n g時(shí),該函數(shù)便將調(diào)用Wi n d o w s的G e t T h r e a d S t r i n g函數(shù)的結(jié)果作為第一個(gè)參數(shù)來(lái)傳遞:
? ? LCID GetThreadLocale();
? ? 每次創(chuàng)建一個(gè)線程時(shí),它就被賦予一種語(yǔ)言。函數(shù)將返回該線程的當(dāng)前語(yǔ)言設(shè)置。
? ? C o m p a r e S t r i n g的第二個(gè)參數(shù)用于標(biāo)識(shí)一些標(biāo)志,這些標(biāo)志用來(lái)修改該函數(shù)比較兩個(gè)字符串時(shí)所用的方法。表2 - 5顯示了可以使用的標(biāo)志。
?
? ? 當(dāng)l s t r c m p調(diào)用C o m p a r e S t r i n g時(shí),它傳遞0作為f d w S t y l e的參數(shù)。但是,當(dāng)l s t r c m p i調(diào)用C o m p a r e S t r i n g時(shí),它就傳遞N O R M _ I G N O R E C A S E。C o m p a r e S t r i n g的其余4個(gè)參數(shù)用于設(shè)定兩個(gè)字符串和它們各自的長(zhǎng)度。如果為c c h 1參數(shù)傳遞- 1,那么該函數(shù)將認(rèn)為p S t r i n g 1字符串是以0結(jié)尾,并計(jì)算該字符串的長(zhǎng)度。對(duì)于p S t r i n g 2字符串來(lái)說(shuō),參數(shù)c c h 2的作用也是一樣。
? ? 其他C運(yùn)行期函數(shù)沒(méi)有為U n i c o d e字符串的操作提供很好的支持。例如,t o l o w e r和t o u p p e r函數(shù)無(wú)法正確地轉(zhuǎn)換帶有重音符號(hào)的字符。為了彌補(bǔ)C運(yùn)行期庫(kù)中的這些不足,必須調(diào)用下面這些Wi n d o w s函數(shù),以便轉(zhuǎn)換U n i c o d e字符串的大小寫字母。這些函數(shù)也可以正確地用于A N S I字符串。
? ? 頭兩個(gè)函數(shù):
? ? PTSTR CharLower(PTSTR pzsString);
和
? ? PTSTR CharUpper(PTSTR pszString);
? ? 既可以轉(zhuǎn)換單個(gè)字符,也可以轉(zhuǎn)換以 0結(jié)尾的整個(gè)字符串。若要轉(zhuǎn)換整個(gè)字符串,只需要傳遞字符串的地址即可。若要轉(zhuǎn)換單個(gè)字符,必須像下面這樣傳遞各個(gè)字符:
? ? TCHAR cLowerCaseChar = CharLower((PTSTR) szString[0]);
? ? 將單個(gè)字符轉(zhuǎn)換成一個(gè)P T S T R,便可調(diào)用該函數(shù),將一個(gè)值傳遞給它,在這個(gè)值中,較低的1 6位包含了該字符,較高的1 6位包含0。當(dāng)該函數(shù)看到較高位是0時(shí),該函數(shù)就知道你想要轉(zhuǎn)換單個(gè)字符,而不是整個(gè)字符串。返回的值是個(gè)3 2位值,較低的1 6位中是已經(jīng)轉(zhuǎn)換的字符。下面兩個(gè)函數(shù)與前面兩個(gè)函數(shù)很相似,差別在于它們用于轉(zhuǎn)換緩存中包含的字符(該緩存不必以0結(jié)尾) :
?
? ? 其他的C運(yùn)行期函數(shù),如i s a l p h a、i s l o w e r和i s u p p e r,返回一個(gè)值,指明某個(gè)字符是字母字符、小寫字母還是大寫字母。Windows API提供了一些函數(shù),也能返回這些信息,但是Wi n d o w s函數(shù)也要考慮用戶在控制面板中指定的語(yǔ)言:
?
????p r i n t f函數(shù)家族是要介紹的最后一組C運(yùn)行期函數(shù)。如果在定義了_ U N I C O D E的情況下編譯你的源代碼模塊,那么p r i n t f函數(shù)家族便希望所有字符和字符串參數(shù)代表U n i c o d e字符和字符串。但是,如果在沒(méi)有定義_ U N I C O D E的情況下編譯你的源代碼模塊,p r i n t f函數(shù)家族便希望傳遞給它的所有字符和字符串都是A N S I字符和字符串。
? ? M i c r o s o f t公司已經(jīng)給C運(yùn)行期的p r i n t f函數(shù)家族增加了一些特殊的域類型。其中有些域類型尚未被ANSI C采用。 新類型使你能夠很容易地對(duì)A N S I和U n i c o d e字符和字符串進(jìn)行混合和匹配。操作系統(tǒng)的w s p r i n t f函數(shù)也得到了增強(qiáng)。下面是一些例子(請(qǐng)注意大寫S和小寫s的使用) :
?
2.9.2 資源
? ? 當(dāng)資源編譯器對(duì)你的所有資源進(jìn)行編譯時(shí),輸出文件是資源的二進(jìn)制文件。資源(字符串表、對(duì)話框模板和菜單等)中的字符串值總是寫作U n i c o d e字符串。在Windows 98和Wi n d o w s2 0 0 0下,如果應(yīng)用程序沒(méi)有定義U N I C O D E宏,那么系統(tǒng)就會(huì)進(jìn)行內(nèi)部轉(zhuǎn)換。
? ? 例如,如果在編譯源代碼模塊時(shí)沒(méi)有定義 U N I C O D E,調(diào)用L o a d S t r i n g實(shí)際上就是調(diào)用L o a d S t r i n g A函數(shù)。這時(shí)L o a d S t r i n g A就從你的資源中讀取字符串,并將該字符串轉(zhuǎn)換成A N S I字符串。A N S I形式的字符串將從該函數(shù)返回給你的應(yīng)用程序。
2.9.3 確定文本是A N S I文本還是U n i c o d e文本
? ? 到現(xiàn)在為止,U n i c o d e文本文件仍然非常少。實(shí)際上,M i c r o s o f t公司自己的大多數(shù)產(chǎn)品并沒(méi)有配備任何U n i c o d e文本文件。但是預(yù)計(jì)將來(lái)這種情況是會(huì)改變的(盡管這需要一個(gè)很長(zhǎng)的過(guò)程) 。當(dāng)然,Windows 2000的N o t e p a d (記事本)應(yīng)用程序允許你既能打開(kāi)U n i c o d e文件,也能打開(kāi)A N S I文件,并且可以創(chuàng)建這些文件。圖2 - 1顯示了N o t e p a d的Save As(文件另存為)對(duì)話框。請(qǐng)注意可以用不同的方法來(lái)保存文本文件。
?
? ? 對(duì)于許多用來(lái)打開(kāi)文本文件和處理這些文件的應(yīng)用程序(如編譯器)來(lái)說(shuō),打開(kāi)一個(gè)文件后,應(yīng)用程序就能方便地確定該文本文件是包含A N S I字符還是U n i c o d e字符。I s Te x t U n i c o d e函數(shù)能夠幫助進(jìn)行這種區(qū)分:
? ? DWORD IsTextUnicode(CONST PVOID pvBuffer ,int cb ,PINT pResult);
? ? 文本文件存在的問(wèn)題是,它們的內(nèi)容沒(méi)有嚴(yán)格和明確的規(guī)則,因此很難確定該文件是包含A N S I字符還是U n i c o d e字符。I s Te x t U n i c o d e使用一系列統(tǒng)計(jì)方法和定性方法,以便猜測(cè)緩存的
? ? 內(nèi)容。由于這不是一種確切的科學(xué)方法,因此I s Te x t U n i c o d e有可能返回不正確的結(jié)果。
? ? 第一個(gè)參數(shù)p v B u ff e r用于標(biāo)識(shí)要測(cè)試的緩存的地址。該數(shù)據(jù)是個(gè)無(wú)效指針,因?yàn)槟悴恢?/span>你擁有的是A N S I字符數(shù)組還是U n i c o d e字符數(shù)組。
? ? 第二個(gè)參數(shù)c b用于設(shè)定p v B u ff e r指向的字節(jié)數(shù)。同樣,由于你不知道緩存中放的是什么,因此c b是個(gè)字節(jié)數(shù),而不是字符數(shù)。請(qǐng)注意,不必設(shè)定緩存的整個(gè)長(zhǎng)度。當(dāng)然,I s Te x t U n i c o d e
能夠測(cè)試的字節(jié)越多,得到的結(jié)果越準(zhǔn)確。
? ? 第三個(gè)參數(shù)p R e s u l t是個(gè)整數(shù)的地址,必須在調(diào)用I s Te x t U n i c o d e之前對(duì)它進(jìn)行初始化。對(duì)該
整數(shù)進(jìn)行初始化后,就可以指明你要I s Te x t U n i c o d e執(zhí)行哪些測(cè)試。也可以為該參數(shù)傳遞N U L L,
在這種情況下,I s Te x t U n i c o d e將執(zhí)行它能夠進(jìn)行的所有測(cè)試(詳細(xì)說(shuō)明請(qǐng)參見(jiàn)Platform SDK文檔) 。
? ? 如果I s Te x t U n i c o d e認(rèn)為緩存包含U n i c o d e文本,便返回T R U E,否則返回FA L S E。確實(shí)是這樣,盡管M i c r o s o f t將該函數(shù)的原型規(guī)定為返回D W O R D,但是它實(shí)際上返回一個(gè)布爾值。如果在p R e s u l t參數(shù)指向的整數(shù)中必須進(jìn)行特定的測(cè)試,該函數(shù)就會(huì)在返回之前設(shè)定整數(shù)中的信息位,以反映每個(gè)測(cè)試的結(jié)果。
? ? Wi n d o w s 9 8 在Windows 98下,I s Te x t U n i c o d e函數(shù)沒(méi)有有用的實(shí)現(xiàn)代碼,它只是返回FA L S E。調(diào)用G e t L a s t E r r o r函數(shù)將返回E R R O R _ C A L L _ N O T _ I M P L E M E N T D。
第1 7章中的Flie Rev示例應(yīng)用程序演示了I s TextUnicode函數(shù)的使用。
2.9.4 在U n i c o d e與A N S I之間轉(zhuǎn)換字符串
? ? Wi n d o w s函數(shù)M u l t i B y t e To Wi d e C h a r用于將多字節(jié)字符串轉(zhuǎn)換成寬字符串。下面顯示了M u l t i B y t e To Wi d e C h a r函數(shù)。
?
? ? u C o d e P a g e參數(shù)用于標(biāo)識(shí)一個(gè)與多字節(jié)字符串相關(guān)的代碼頁(yè)號(hào)。d w F l a g s參數(shù)用于設(shè)定另一個(gè)控件,它可以用重音符號(hào)之類的區(qū)分標(biāo)記來(lái)影響字符。這些標(biāo)志通常并不使用,在d w F l a g s參數(shù)中傳遞0。p M u l t i B y t e S t r參數(shù)用于設(shè)定要轉(zhuǎn)換的字符串,c c h M u l t i B y t e參數(shù)用于指明該字符串的長(zhǎng)度(按字節(jié)計(jì)算) 。如果為c c h M u l t i B y t e參數(shù)傳遞- 1,那么該函數(shù)用于確定源字符串的長(zhǎng)度。
? ? 轉(zhuǎn)換后產(chǎn)生的U n i c o d e版本字符串將被寫入內(nèi)存中的緩存,其地址由p Wi d e C h a r S t r參數(shù)指定。必須在c c h Wi d e C h a r參數(shù)中設(shè)定該緩存的最大值(以字符為計(jì)量單位) 。如果調(diào)用M u l t i B y t e To Wi d e C h a r,給c c h Wi d e C h a r參數(shù)傳遞0,那么該參數(shù)將不執(zhí)行字符串的轉(zhuǎn)換,而是返回為使轉(zhuǎn)換取得成功所需要的緩存的值。一般來(lái)說(shuō),可以通過(guò)下列步驟將多字節(jié)字符串轉(zhuǎn)換成U n i c o d e等價(jià)字符串:
? ? 1) 調(diào)用M u l t i B y t e To Wi d e C h a r函數(shù),為p Wi d e C h a r S t r參數(shù)傳遞N U L L,為c c h Wi d e C h a r參數(shù)傳遞0。
? ? 2) 分配足夠的內(nèi)存塊,用于存放轉(zhuǎn)換后的U n i c o d e字符串。該內(nèi)存塊的大小由前面對(duì)M u l t B y t e To Wi d e C h a r的調(diào)用返回。
? ? 3) 再次調(diào)用M u l t i B y t e To Wi d e C h a r,這次將緩存的地址作為p Wi d e C h a r S t r參數(shù)來(lái)傳遞,并傳遞第一次調(diào)用M u l t i B y t e To Wi d e C h a r時(shí)返回的緩存大小,作為c c h Wi d e c h a r參數(shù)。
? ? 4)?使用轉(zhuǎn)換后的字符串。
? ? 5) 釋放U n i c o d e字符串占用的內(nèi)存塊。函數(shù)Wi d e C h a r To M u l t i B y t e將寬字符串轉(zhuǎn)換成等價(jià)的多字節(jié)字符串,如下所示:
?
? ? 該函數(shù)與M u l t i B i t e To Wi d e C h a r函數(shù)相似。同樣,u C o d e P a g e參數(shù)用于標(biāo)識(shí)與新轉(zhuǎn)換的字符串相關(guān)的代碼頁(yè)。d w F l a g s則設(shè)定用于轉(zhuǎn)換的其他控件。這些標(biāo)志能夠作用于帶有區(qū)分符號(hào)的字符和系統(tǒng)不能轉(zhuǎn)換的字符。通常不需要為字符串的轉(zhuǎn)換而擁有這種程度的控制手段,你將為d w F l a g s參數(shù)傳遞0。
? ? p Wi d e C h a r S t r參數(shù)用于設(shè)定要轉(zhuǎn)換的字符串的內(nèi)存地址,c c h Wi d e C h a r參數(shù)用于指明該字符串的長(zhǎng)度(用字符數(shù)來(lái)計(jì)量) 。如果你為c c h Wi d e C h a r參數(shù)傳遞- 1,那么該函數(shù)用于確定源字符串的長(zhǎng)度。
? ? 轉(zhuǎn)換產(chǎn)生的多字節(jié)版本的字符串被寫入由p M u l t i B y t e S t r參數(shù)指明的緩存。必須在c c h M u l t i B y t e參數(shù)中設(shè)定該緩存的最大值(用字節(jié)來(lái)計(jì)量) 。如果傳遞0作為Wi d e C h a r To M u l t i B y t e函數(shù)的c c h M u l t i B y t e參數(shù),那么該函數(shù)將返回目標(biāo)緩存需要的大小值。通常可以使用將多字節(jié)字符串轉(zhuǎn)換成寬字節(jié)字符串時(shí)介紹的一系列類似的事件,將寬字節(jié)字符串轉(zhuǎn)換成多字節(jié)字符串。
? ? 你會(huì)發(fā)現(xiàn),Wi d e C h a r To M u l t i B y t e函數(shù)接受的參數(shù)比M u l t i B y t e To Wi d e C h a r函數(shù)要多2個(gè),即p D e f a u l t C h a r和p f U s e d D e f a u l t C h a r。只有當(dāng)Wi d e C h a r To M u l t i B y t e函數(shù)遇到一個(gè)寬字節(jié)字符,而該字符在u C o d e P a g e參數(shù)標(biāo)識(shí)的代碼頁(yè)中并沒(méi)有它的表示法時(shí),Wi d e C h a r To M u l t i B y t e函數(shù)才使用這兩個(gè)參數(shù)。如果寬字節(jié)字符不能被轉(zhuǎn)換,該函數(shù)便使用p D e f a u l t C h a r參數(shù)指向的字符。如果該參數(shù)是N U L L(這是大多數(shù)情況下的參數(shù)值) ,那么該函數(shù)使用系統(tǒng)的默認(rèn)字符。該默認(rèn)字符通常是個(gè)問(wèn)號(hào)。這對(duì)于文件名來(lái)說(shuō)是危險(xiǎn)的,因?yàn)閱?wèn)號(hào)是個(gè)通配符。
? ? p f U s e d D e f a u l t C h a r參數(shù)指向一個(gè)布爾變量,如果寬字符串中至少有一個(gè)字符不能轉(zhuǎn)換成等價(jià)多字節(jié)字符,那么函數(shù)就將該變量置為T R U E。如果所有字符均被成功地轉(zhuǎn)換,那么該函數(shù)就將該變量置為FA L S E。當(dāng)函數(shù)返回以便檢查寬字節(jié)字符串是否被成功地轉(zhuǎn)換后,可以測(cè)試該變量。同樣,通常為該測(cè)試傳遞N U L L。
? ? 關(guān)于如何使用這些函數(shù)的詳細(xì)說(shuō)明,請(qǐng)參見(jiàn)Platform SDK文檔。
? ? 如果使用這兩個(gè)函數(shù),就可以很容易創(chuàng)建這些函數(shù)的 U n i c o d e版本和A N S I版本。例如,你可能有一個(gè)動(dòng)態(tài)鏈接庫(kù),它包含一個(gè)函數(shù),能夠轉(zhuǎn)換字符串中的所有字符。可以像下面這樣編寫該函數(shù)的U n i c o d e版本:
?
ASCII版本
?
然后是提供一個(gè)對(duì)外接口:
?
總結(jié)
以上是生活随笔為你收集整理的windows核心编程-第二章 Unicode的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Intel汇编语言程序设计学习-第五章
- 下一篇: Intel汇编语言程序设计学习-第五章