Unicode與Windows編程(3) 2.8 如何編寫UNICODE源代碼Microsoft公司為UNICODE設(shè)計了Windows API,這樣,可以盡量減少對你的代碼的影響。實際上,你可以編寫單個源代碼文件,以便使用或者不使用UNICODE來對它進行編譯。只需要定義兩個宏(UNICODE和_ UNICODE),就可以修改然后重新編譯該源文件。 2.8.1 C運行期庫對UNICODE的支持為了利用UNICODE字符串,定義了一些數(shù)據(jù)類型。標(biāo)準的C頭文件String,h已經(jīng)作了修改,以便定義一個名字為wchart的數(shù)據(jù)類型,它是一個UNICODE字符的數(shù)據(jù)類型: 例如,如果想要創(chuàng)建一個緩存,用于存放最多為99個字符的UNICODE字符串和一個結(jié)尾為 零的字符,可以使用下面這個語句: 該語句創(chuàng)建了一個由100個16位值組成的數(shù)組。當(dāng)然,標(biāo)準的C運行期字符串函數(shù),如 strcpy、strchr和strcat等,只能對ANSI字符串進行操作,不能正確地處理UNICODE字符串。因此,ANSI C也擁有一組補充函數(shù)。清單2 – 1顯示了一些標(biāo)準的ANSI C字符串函數(shù),后面是它們的等價UNICODE函數(shù)。 清單2-1 標(biāo)準的ANSI C字符串函數(shù)和它們的等價UNICODE函數(shù) 請注意,所有的UNICODE函數(shù)均以wcs開頭,wcs是寬字符串的英文縮寫。若要調(diào)用UNICODE函數(shù),只需用前綴wcs來取代ANSI字符串函數(shù)的前綴str即可。
注意 大多數(shù)軟件開發(fā)人員可能已經(jīng)不記得這樣一個非常重要的問題了,那就是 Microsoft公司提供的C運行期庫與ANSI的標(biāo)準C運行期庫是一致的。ANSI C規(guī)定,C運行期庫支持UNICODE字符和字符串。這意味著始終都可以調(diào)用C運行期函數(shù),以便對UNICODE字符和字符串進行操作,即使是在Windows 98上運行,也可以調(diào)用這些函數(shù)。換句話說, wcscat、wcslen和wcstok等函數(shù)都能夠在Windows 98上很好地運行,這些都是必須關(guān)心的操作系統(tǒng)函數(shù)。 對于包含了對str函數(shù)或wcs函數(shù)進行顯式調(diào)用的代碼來說,無法非常容易地同時為ANSI和UNICODE對這些代碼進行編譯。本章前面說過,可以創(chuàng)建同時為ANSI和UNICODE進行編譯的單個源代碼文件。若要建立雙重功能,必須包含TChar.h文件,而不是包含String,h文件。 TChar.h文件的唯一作用是幫助創(chuàng)建ANSI / UNICODE通用源代碼文件。它包含你應(yīng)該用在源代碼中的一組宏,而不應(yīng)該直接調(diào)用str函數(shù)或者wcs函數(shù)。如果在編譯源代碼文件時定義了_ UNICODE,這些宏就會引用wcs這組函數(shù)。如果沒有定義_ UNICODE,那么這些宏將引用str這組宏。 例如,在TChar.h中有一個宏稱為_tcscpy。如果在包含該頭文件時沒有定義_ UNICODE ,那么_tcscpy就會擴展為ANSI的strcpy函數(shù)。但是如果定義了_UNICODE, _tcscpy將擴展為UNICODE的wcscpy函數(shù)。擁有字符串參數(shù)的所有C運行期函數(shù)都在TChar.h文件中定義了一個通用宏。如果使用通用宏,而不是ANSI / UNICODE的特定函數(shù)名,就能夠順利地創(chuàng)建可以為ANSI或UNICODE進行編譯的源代碼。 但是,除了使用這些宏之外,還有一些操作是必須進行的。TChar.h文件包含了另外一些宏。若要定義一個ANSI / UNICODE通用的字符串?dāng)?shù)組,請使用下面的TCHAR數(shù)據(jù)類型。如果定義了_ UNICODE,TCHAR將聲明為下面的形式: 使用該數(shù)據(jù)類型,可以像下面這樣分配一個字符串: 也可以創(chuàng)建對字符串的指針: 不過上面這行代碼存在一個問題。按照默認設(shè)置, Microsoft公司的C + +編譯器能夠編譯所有的字符串,就像它們是ANSI字符串,而不是UNICODE字符串。因此,如果沒有定義_ UNICODE,該編譯器將能正確地編譯這一行代碼。但是,如果定義了_ UNICODE,就會產(chǎn)生一個錯誤。若要生成一個UNICODE字符串而不是ANSI字符串,必須將該代碼行改寫為下面的樣子: 字符串(literal string)前面的大寫字母L,用于告訴編譯器該字符串應(yīng)該作為UNICODE字符串來編譯。當(dāng)編譯器將字符串置于程序的數(shù)據(jù)部分中時,它在每個字符之間分散插入零字節(jié)。這種變更帶來的問題是,現(xiàn)在只有當(dāng)定義了_ UNICODE時,程序才能成功地進行編譯。我們需要另一個宏,以便有選擇地在字符串的前面加上大寫字母L。這項工作由_TEXT宏來完成,_TEXT宏也在TChar.h文件中做了定義。如果定義了_ UNICODE,那么_TEXT定義為下面的形式: 如果沒有定義_ UNICODE,_TEXT將定義為 使用該宏,可以改寫上面這行代碼,這樣,無論是否定義了_ UNICODE宏,它都能夠正確地進行編譯。如下所示: _TEXT宏也可以用于字符串。例如,若要檢查一個字符串的第一個字符是否是大寫字母J,只需編寫下面的代碼即可: 2.8.2 Windows定義的UNICODE數(shù)據(jù)類型Windows頭文件定義了表2 – 3列出的數(shù)據(jù)類型。 表2-3 Uincode 數(shù)據(jù)類型 這些數(shù)據(jù)類型是指UNICODE字符和字符串。Windows頭文件也定義了ANSI / UNICODE通用數(shù)據(jù)類型P T STR和P C T STR。這些數(shù)據(jù)類型既可以指ANSI字符串,也可以指UNICODE字符串,這取決于當(dāng)編譯程序模塊時是否定義了UNICODE宏。請注意,這里的UNICODE宏沒有前置的下劃線。_ UNICODE宏用于C運行期頭文件,而UNICODE宏則用于Windows頭文件。當(dāng)編譯源代碼模塊時,通常必須同時定義這兩個宏。 2.8.3 Windows中的UNICODE函數(shù)和ANSI函數(shù)前面已經(jīng)講過,有兩個函數(shù)稱為CreateWindowEx,一個CreateWindowEx接受UNICODE字符串,另一個CreateWindowEx接受ANSI字符串。情況確實如此,不過,這兩個函數(shù)的原型實際上是下面的樣子: CreateWindowEx W是接受UNICODE字符串的函數(shù)版本。函數(shù)名結(jié)尾處的大寫字母W是英文wide(寬)的縮寫。每個UNICODE字符的長度是1 6位,因此,它們常常稱為寬字符。CreateWindowEx A的結(jié)尾處的大寫字母A表示該函數(shù)可以接受ANSI字符串。 但是,在我們的代碼中,通常只包含了對CreateWindowEx的調(diào)用,而不是直接調(diào)用CreateWindowEx W或者CreateWindowEx A。在Wi n U s e r. h文件中,CreateWindowEx實際上是定義為下面這種形式的一個宏: 當(dāng)編譯源代碼模塊時, UNICODE 是否已經(jīng)作了定義,將決定你調(diào)用的是哪CreateWindowEx版本。當(dāng)轉(zhuǎn)用一個1 6位的Windows應(yīng)用程序時,你在編譯期間可能沒有定義UNICODE。對CreateWindowEx函數(shù)的任何調(diào)用都會將該宏擴展為對CreateWindowEx A的調(diào)用,即對CreateWindowEx的ANSI版本的調(diào)用。由于1 6位Windows只提供了CreateWindowEx的ANSI版本,因此可以比較容易地轉(zhuǎn)用它的應(yīng)用程序。 在Windows 2000下,Microsoft的CreateWindowEx A源代碼只不過是一個形實替換程序?qū)踊?/SPAN> 翻譯層,用于分配內(nèi)存,以便將ANSI字符串轉(zhuǎn)換成UNICODE字符串。該代碼然后調(diào)用CreateWindowEx,并傳遞轉(zhuǎn)換后的字符串。當(dāng)CreateWindowEx W返回時,CreateWindowEx A便釋放它的內(nèi)存緩存,并將窗口句柄返回給你。 如果要創(chuàng)建其他軟件開發(fā)人員將要使用的動態(tài)鏈接庫(DLL),請考慮使用下面的方法。在D L L中提供兩個輸出函數(shù)。一個是ANSI版本,另一個是UNICODE版本。在ANSI版本中,只需要分配內(nèi)存,執(zhí)行必要的字符串轉(zhuǎn)換,并調(diào)用該函數(shù)的UNICODE版本(本章后面部分介紹這個進程)。 在Windows 98下,Microsoft的CreateWindowEx A源代碼是執(zhí)行操作的函數(shù)。Windows 98 提供了接受UNICODE參數(shù)的所有Windows函數(shù)的進入點,但是這些函數(shù)并不將UNICODE字符串轉(zhuǎn)換成ANSI字符串,它們只返回運行失敗的消息。調(diào)用GetLastError將返ERROR_CALLNOTIMPLEMENTED。這些函數(shù)中只有ANSI版本的函數(shù)才能正確地運行。如果編譯的代碼調(diào)用了任何寬字符函數(shù),應(yīng)用程序?qū)o法在Windows 98下運行。 Windows API中的某些函數(shù),比如WinExec和OpenFile等,只是為了實現(xiàn)與1 6位Windows程序的向后兼容而存在,因此,應(yīng)該避免使用。應(yīng)該使用對CreateProcess和CreateFile函數(shù)的調(diào)用來取代對WinExec和OpenFile函數(shù)的調(diào)用。從系統(tǒng)內(nèi)部來講,老的函數(shù)完全可以調(diào)用新的函數(shù)。老的函數(shù)存在的一個大問題是,它們不接受UNICODE字符串。當(dāng)調(diào)用這些函數(shù)時,必須傳遞ANSI字符串。另一方面,所有新的和未過時的函數(shù)在Windows 2000中都同時擁有ANSI和UNICODE兩個版本。 2.8.4 Windows字符串函數(shù)Windows還提供了一組范圍很廣的字符串操作函數(shù)。這些函數(shù)與C運行期字符串函數(shù)(如strcpy和wcscpy)很相似。但是該操作系統(tǒng)函數(shù)是操作系統(tǒng)的一個組成部分,操作系統(tǒng)的許多組件都使用這些函數(shù),而不使用C運行期庫。建議最好使用操作系統(tǒng)函數(shù),而不要使用C運行期字符串函數(shù)。這將有助于稍稍提高你的應(yīng)用程序的運行性能,因為操作系統(tǒng)字符串函數(shù)常常被大型應(yīng)用程序比如操作系統(tǒng)的外殼進程Explorer.exe所使用。由于這些函數(shù)使用得很多,因此,在你的應(yīng)用程序運行時,它們可能已經(jīng)被裝入RAM。 若要使用這些函數(shù),系統(tǒng)必須運行Windows 2000或Windows 98。如果安裝了InternetExplorer 4.0或更新的版本,也可以在較早的Windows版本中獲得這些函數(shù)。在經(jīng)典的操作系統(tǒng)函數(shù)樣式中,操作系統(tǒng)字符串函數(shù)名既包含大寫字母,也包含小寫字母,它的形式類似這個樣子: Strcat、Strchr、StrCmp和Strcpy等。若要使用這些函數(shù),必須加上S h l WA p i . h頭文件。另外,如前所述,這些字符串函數(shù)既有ANSI版本,也有UNICODE版本,例如Strcat A和Strcat W。由于這些函數(shù)屬于操作系統(tǒng)函數(shù),因此,當(dāng)創(chuàng)建應(yīng)用程序時,如果定義了UNICODE(不帶前置下劃線),那么它們的符號將擴展為寬字符版本。 2.9 成為符合ANSI和UNICODE的應(yīng)用程序即使你不打算立即使用UNICODE,最好也應(yīng)該著手將你的應(yīng)用程序轉(zhuǎn)換成符合UNICODE的應(yīng) 用程序。下面是應(yīng)該遵循的一些基本原則: 將文本串視為字符數(shù)組,而不是c h a r s數(shù)組或字節(jié)數(shù)組。 將通用數(shù)據(jù)類型(如TCHAR和P T STR)用于文本字符和字符串。 將顯式數(shù)據(jù)類型(如B Y T E和P B Y T E)用于字節(jié)、字節(jié)指針和數(shù)據(jù)緩存。 將T E X T宏用于原義字符和字符串。 執(zhí)行全局性替換(例如用P T STR替換P STR)。 修改字符串運算問題。例如函數(shù)通常希望你在字符中傳遞一個緩存的大小,而不是字節(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 ( TCHAR )。另外, 如果需要為字符串分配一個內(nèi)存塊,并且擁有該字符串中的字符數(shù)目,那么請記住要按字節(jié)來分配內(nèi)存。這就是說,應(yīng)該調(diào)用malloc(nCharacters *sizeof(TCHAR)), 而不是調(diào)用malloc(nChar acters )。在上面所說的所有原則中,這是最難記住的一條原則,如果操作錯誤,編譯器 將不發(fā)出任何警告。 當(dāng)我為本書的第一版編寫示例程序時,我編寫的原始程序只能編譯為ANSI程序。后來,當(dāng)我開始撰寫本章的內(nèi)容時,我想我應(yīng)該鼓勵使用UNICODE,并且打算創(chuàng)建一些示例程序,以便展示你可以非常容易地編寫既可以用UNICODE也可以用ANSI來編譯的程序。這時我發(fā)現(xiàn)最好的辦法是將本書的所有示例程序進行轉(zhuǎn)換,使它們都能夠用UNICODE和ANSI進行編譯。我用了大約4個小時將所有程序進行了轉(zhuǎn)換。考慮到我以前從來沒有這方面的轉(zhuǎn)換經(jīng)驗,這個速度是相當(dāng)不錯了。 2.9.1 Windows字符串函數(shù)Win d o w s也提供了一組用于對UNICODE字符串進行操作的函數(shù),表2 – 4對它們進行了描述。 這些函數(shù)是作為宏來實現(xiàn)的,這些宏既可以調(diào)用函數(shù)的UNICODE版本,也可以調(diào)用函數(shù)的ANSI版本,這要根據(jù)編譯源代碼模塊時是否已經(jīng)定義了UNICODE而定。例如,如果沒有定義UNICODE,l strcat函數(shù)將擴展為l strcat A。如果定義了UNICODE,l strcat將擴展為l strcat W。有兩個字符串函數(shù),即l StrCmp和l StrCmp i,它們的行為特性與等價的C運行期函數(shù)是不同的。C運行期函數(shù)StrCmp、StrCmpi、wcscmp和wcscmpi只是對字符串中的代碼點的值進行比較這 就是說,這些函數(shù)將忽略實際字符的含義,只是將第一個字符串中的每個字符的數(shù)值與第二個 字符串中的字符的數(shù)值進行比較。而Windows函數(shù)l StrCmp和l StrCmpi是作為對Windows函數(shù)CompareString的調(diào)用來實現(xiàn)的。 該函數(shù)對兩個UNICODE字符串進行比較。CompareString的第一個參數(shù)用于設(shè)定語言ID(LCID),這是個3 2位值,用于標(biāo)識一種特定的語言。CompareString使用這個LCID來比較這兩個字符串,方法是對照一種特定的語言來查看它們的字符的含義。這種操作方法比C運行期函數(shù)簡單地進行數(shù)值比較更有意義。 當(dāng)l StrCmp函數(shù)系列中的任何一個函數(shù)調(diào)用CompareString時,該函數(shù)便將調(diào)用Windows的 GetTheadString函數(shù)的結(jié)果作為第一個參數(shù)來傳遞: 每次創(chuàng)建一個線程時,它就被賦予一種語言。函數(shù)將返回該線程的當(dāng)前語言設(shè)置。CompareString的第二個參數(shù)用于標(biāo)識一些標(biāo)志,這些標(biāo)志用來修改該函數(shù)比較兩個字符串時所用的方法。表2 – 5顯示了可以使用的標(biāo)志。 當(dāng)l StrCmp調(diào)用CompareString時,它傳遞0作為fdwStyle的參數(shù)。但是,當(dāng)lStrCmp i調(diào)用CompareString時,它就傳遞NORMIGNORECASE。CompareString的其余4個參數(shù)用于設(shè)定兩個字符串和它們各自的長度。如果為cch1參數(shù)傳遞- 1,那么該函數(shù)將認為pString1字符串是以0結(jié)尾,并計算該字符串的長度。對于pString2字符串來說,參數(shù)cch2的作用也是一樣。其他C運行期函數(shù)沒有為UNICODE字符串的操作提供很好的支持。例如,tolower和toupper函數(shù)無法正確地轉(zhuǎn)換帶有重音符號的字符。為了彌補C運行期庫中的這些不足,必須調(diào)用下面這些Windows函數(shù),以便轉(zhuǎn)換UNICODE字符串的大小寫字母。這些函數(shù)也可以正確地用于ANSI字符串。頭兩個函數(shù): 既可以轉(zhuǎn)換單個字符,也可以轉(zhuǎn)換以0結(jié)尾的整個字符串。若要轉(zhuǎn)換整個字符串,只需要傳遞字符串的地址即可。若要轉(zhuǎn)換單個字符,必須像下面這樣傳遞各個字符: 6位包含了該字符,較高的1 6位包含0。當(dāng)該函數(shù)看到較高位是0時,該函數(shù)就知道你想要轉(zhuǎn)換單個字符,而不是整個字符串。返回的值是個3 2位值,較低的1 6位中是已經(jīng)轉(zhuǎn)換的字符。下面兩個函數(shù)與前面兩個函數(shù)很相似,差別在于它們用于轉(zhuǎn)換緩存中包含的字符(該緩存不必以0結(jié)尾): 其他的C運行期函數(shù),如isalpha、islower和isupper,返回一個值,指明某個字符是字母字符、小寫字母還是大寫字母。Windows API 提供了一些函數(shù),也能返回這些信息,但是Windows函數(shù)也要考慮用戶在控制面板中指定的語言: printf函數(shù)家族是要介紹的最后一組C運行期函數(shù)。如果在定義了_ UNICODE的情況下編譯你的源代碼模塊,那么printf函數(shù)家族便希望所有字符和字符串參數(shù)代表UNICODE字符和字符串。但是,如果在沒有定義_ UNICODE的情況下編譯你的源代碼模塊, printf函數(shù)家族便希望傳遞 給它的所有字符和字符串都是ANSI字符和字符串。Microsoft公司已經(jīng)給C運行期的printf函數(shù)家族增加了一些特殊的域類型。其中有些域類型尚未被ANSIC采用。新類型使你能夠很容易地對ANSI和UNICODE字符和字符串進行混合和匹配。 操作系統(tǒng)的w s printf函數(shù)也得到了增強。下面是一些例子(請注意大寫S和小寫s的使用): 2.9.2 資源當(dāng)資源編譯器對你的所有資源進行編譯時,輸出文件是資源的二進制文件。資源(字符串表、對話框模板和菜單等)中的字符串值總是寫作UNICODE字符串。在Windows 98和Windows2000下,如果應(yīng)用程序沒有定義UNICODE宏,那么系統(tǒng)就會進行內(nèi)部轉(zhuǎn)換。 例如,如果在編譯源代碼模塊時沒有定義UNICODE,調(diào)用LoadString實際上就是調(diào)用LoadString A函數(shù)。這時LoadString A就從你的資源中讀取字符串,并將該字符串轉(zhuǎn)換成ANSI字符串。ANSI形式的字符串將從該函數(shù)返回給你的應(yīng)用程序。 2.9.3 確定文本是ANSI文本還是UNICODE文本到現(xiàn)在為止,UNICODE文本文件仍然非常少。實際上, Microsoft公司自己的大多數(shù)產(chǎn)品并沒有配備任何UNICODE文本文件。但是預(yù)計將來這種情況是會改變的(盡管這需要一個很長的過程)。當(dāng)然,Windows 2000的Notepad (記事本)應(yīng)用程序允許你既能打開UNICODE文件,也能打開ANSI文件,并且可以創(chuàng)建這些文件。圖2 – 1顯示了Notepad的Save As(文件另存為)對話框。請注意可以用不同的方法來保存文本文件。 對于許多用來打開文本文件和處理這些文件的應(yīng)用程序(如編譯器)來說,打開一個文件后,應(yīng)用程序就能方便地確定該文本文件是包含ANSI字符還是UNICODE字符。IsTest UNICODE函數(shù)能夠幫助進行這種區(qū)分: 文本文件存在的問題是,它們的內(nèi)容沒有嚴格和明確的規(guī)則,因此很難確定該文件是包含ANSI字符還是UNICODE字符。IsTest UNICODE使用一系列統(tǒng)計方法和定性方法,以便猜測緩存的內(nèi)容。由于這不是一種確切的科學(xué)方法,因此IsTest UNICODE有可能返回不正確的結(jié)果。第一個參數(shù)pvBuffer用于標(biāo)識要測試的緩存的地址。該數(shù)據(jù)是個無效指針,因為你不知道你擁有的是ANSI字符數(shù)組還是UNICODE字符數(shù)組。 第二個參數(shù)c b用于設(shè)定pvBuffer指向的字節(jié)數(shù)。同樣,由于你不知道緩存中放的是什么,因此c b是個字節(jié)數(shù),而不是字符數(shù)。請注意,不必設(shè)定緩存的整個長度。當(dāng)然, IsTest UNICODE能夠測試的字節(jié)越多,得到的結(jié)果越準確。 第三個參數(shù)pResult是個整數(shù)的地址,必須在調(diào)用IsTest UNICODE之前對它進行初始化。對該整數(shù)進行初始化后,就可以指明你要IsTest UNICODE執(zhí)行哪些測試。也可以為該參數(shù)傳遞NULL,在這種情況下,IsTest UNICODE將執(zhí)行它能夠進行的所有測試(詳細說明請參見Platform SDK文檔)。 如果IsTest UNICODE認為緩存包含UNICODE文本,便返回TRUE,否則返回FALSE。確實是這樣,盡管Microsoft將該函數(shù)的原型規(guī)定為返回D W O R D,但是它實際上返回一個布爾值。如果在pResult參數(shù)指向的整數(shù)中必須進行特定的測試,該函數(shù)就會在返回之前設(shè)定整數(shù)中的信息位,以反映每個測試的結(jié)果。 Windows98 在Windows 98下,IsTest UNICODE函數(shù)沒有有用的實現(xiàn)代碼,它只是返回FALSE。調(diào)用GetLastError函數(shù)將返回E R R O R C A L L N O T I M P L E M E N T D。
2.9.4 在UNICODE與ANSI之間轉(zhuǎn)換字符串Windows函數(shù)MultiByteToWideChar用于將多字節(jié)字符串轉(zhuǎn)換成寬字符串。下面顯示了MultiByteToWideChar函數(shù)。 uCodePage參數(shù)用于標(biāo)識一個與多字節(jié)字符串相關(guān)的代碼頁號。dwFlags參數(shù)用于設(shè)定另一個控件,它可以用重音符號之類的區(qū)分標(biāo)記來影響字符。這些標(biāo)志通常并不使用,在dwFlags參數(shù)中傳遞0。pMultiByteStr參數(shù)用于設(shè)定要轉(zhuǎn)換的字符串, cchMultiByte參數(shù)用于指明該字符串的長度(按字節(jié)計算)。如果為cchMultiByte參數(shù)傳遞- 1,那么該函數(shù)用于確定源字符串的長 度。 轉(zhuǎn)換后產(chǎn)生的UNICODE版本字符串將被寫入內(nèi)存中的緩存,其地址由pWideCharStr參數(shù)指定。必須在cchWideChar參數(shù)中設(shè)定該緩存的最大值(以字符為計量單位)。如果調(diào)用MultiByteToWideChar,給cchWideChar參數(shù)傳遞0,那么該參數(shù)將不執(zhí)行字符串的轉(zhuǎn)換,而是返回為使轉(zhuǎn)換取得成功所需要的緩存的值。一般來說,可以通過下列步驟將多字節(jié)字符串轉(zhuǎn)換 成UNICODE等價字符串: 1) 調(diào)用MultiByteToWideChar函數(shù),為pWideCharStr參數(shù)傳遞NULL,為cchWideChar參數(shù) 傳遞0。 2) 分配足夠的內(nèi)存塊,用于存放轉(zhuǎn)換后的UNICODE字符串。該內(nèi)存塊的大小由前面對M u l t B y t e To Wi d e C h a r的調(diào)用返回。 3) 再次調(diào)用MultiByteToWideChar,這次將緩存的地址作為pWideCharStr參數(shù)來傳遞,并傳遞第一次調(diào)用MultiByteToWideChar時返回的緩存大小,作為cchWideChar參數(shù)。 4. 使用轉(zhuǎn)換后的字符串。 5) 釋放UNICODE字符串占用的內(nèi)存塊。函數(shù)WideCharToMultiByte將寬字符串轉(zhuǎn)換成等價的多字節(jié)字符串,如下所示: 該函數(shù)與MultiByteToWideChar函數(shù)相似。同樣,uCodePage參數(shù)用于標(biāo)識與新轉(zhuǎn)換的字符串相關(guān)的代碼頁。dwFlags則設(shè)定用于轉(zhuǎn)換的其他控件。這些標(biāo)志能夠作用于帶有區(qū)分符號的字符和系統(tǒng)不能轉(zhuǎn)換的字符。通常不需要為字符串的轉(zhuǎn)換而擁有這種程度的控制手段,你將為dwFlags參數(shù)傳遞0。pWideCharStr參數(shù)用于設(shè)定要轉(zhuǎn)換的字符串的內(nèi)存地址,cchWideChar參數(shù)用于指明該字符串的長度(用字符數(shù)來計量)。如果你為cchWideChar參數(shù)傳遞- 1,那么該函數(shù)用于確定源字符串的長度。 轉(zhuǎn)換產(chǎn)生的多字節(jié)版本的字符串被寫入由pMultiByteStr參數(shù)指明的緩存。必須在cchMultiByte參數(shù)中設(shè)定該緩存的最大值(用字節(jié)來計量)。如果傳遞0作為WideCharToMultiByte函數(shù)的cchMultiByte參數(shù),那么該函數(shù)將返回目標(biāo)緩存需要的大小值。通常可以使用將多字節(jié)字符串轉(zhuǎn)換成寬字節(jié)字符串時介紹的一系列類似的事件,將寬字節(jié)字符串轉(zhuǎn)換成多字節(jié)字符串。你會發(fā)現(xiàn),WideCharToMultiByte函數(shù)接受的參數(shù)比MultiByteToWideChar函數(shù)要多2個,即pDefaulTCHAR和pfUsedDefaulTCHAR。只有當(dāng)WideCharToMultiByte函數(shù)遇到一個寬字節(jié)字符,而該字符在uCodePage參數(shù)標(biāo)識的代碼頁中并沒有它的表示法時,WideCharToMultiByte函數(shù)才使用這兩個參數(shù)。如果寬字節(jié)字符不能被轉(zhuǎn)換,該函數(shù)便使用pDefaulTCHAR參數(shù)指向的字符。如果該參數(shù)是NULL(這是大多數(shù)情況下的參數(shù)值),那么該函數(shù)使用系統(tǒng)的默認字符。該默認字符通常是個問號。這對于文件名來說是危險的,因為問號是個通配符。pfUsedDefaulTCHAR參數(shù)指向一個布爾變量,如果寬字符串中至少有一個字符不能轉(zhuǎn)換成等價多字節(jié)字符,那么函數(shù)就將該變量置為TRUE。如果所有字符均被成功地轉(zhuǎn)換,那么該函數(shù)就將該變量置為FALSE。當(dāng)函數(shù)返回以便檢查寬字節(jié)字符串是否被成功地轉(zhuǎn)換后,可以測試該變量。同樣,通常為該測試傳遞NULL。 關(guān)于如何使用這些函數(shù)的詳細說明,請參見Platform SDK文檔。 如果使用這兩個函數(shù),就可以很容易創(chuàng)建這些函數(shù)的UNICODE版本和ANSI版本。例如,你 可能有一個動態(tài)鏈接庫,它包含一個函數(shù),能夠轉(zhuǎn)換字符串中的所有字符??梢韵裣旅孢@樣編寫該函數(shù)的UNICODE版本: 你可以編寫該函數(shù)的ANSI版本以便該函數(shù)根本不執(zhí)行轉(zhuǎn)換字符串的實際操作。你也可以編寫該函數(shù)的ANSI版本,以便該函數(shù)它將ANSI字符串轉(zhuǎn)換成UNICODE字符串,將UNICODE字符串傳遞給StringReverseW函數(shù),然后將轉(zhuǎn)換后的字符串重新轉(zhuǎn)換成ANSI字符串。該函數(shù)類似下面的樣子: 最后,在用動態(tài)鏈接庫分配的頭文件中,可以像下面這樣建立這兩個函數(shù)的原型: |
|