久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Windows程序中的字符編碼

 xiaochong 2007-04-30

我們?cè)谑褂煤桶惭bWindows程序時(shí),,有時(shí)會(huì)看到以“2052”,、“1033”這些數(shù)字為名的文件夾(如Office),這些數(shù)字似乎和字符集有關(guān),,但它們究竟是什么意思呢,?研究這個(gè)問(wèn)題的同時(shí),又會(huì)遇到其它問(wèn)題,。我們會(huì)談到Windows的內(nèi)部架構(gòu),、Win32 API的A/W函數(shù)、Locale,、ANSI代碼頁(yè),、與字符編碼有關(guān)的編譯參數(shù)、MBCS和Unicode程序,、資源和亂碼等,,一起經(jīng)歷這段瑣碎細(xì)節(jié)為主,間或樂(lè)趣點(diǎn)綴的旅程。

0 Where is Win32 API

Windows程序有用戶(hù)態(tài)和核心態(tài)的說(shuō)法,。在32位地址空間中,,0×80000000以下屬于用戶(hù)態(tài)(0×0~0×7FFFFFFF=2GB,0×0~0×10000是保留的),0×80000000以上屬于核心態(tài),。所有硬件管理都在核心態(tài),。用戶(hù)態(tài)程序的不能直接使用核心態(tài)的任何代碼。所謂核心態(tài)其實(shí)只是CPU的一種保護(hù)模式,。在x86 CPU上,,用戶(hù)態(tài)處于ring 3,核心態(tài)處于ring 0,。

從用戶(hù)態(tài)進(jìn)入核心態(tài)的最常用的方法是在寄存器eax填一個(gè)功能碼,,然后執(zhí)行int 2e。這有點(diǎn)像DOS時(shí)代的DOS和BIOS系統(tǒng)調(diào)用,。在NT架構(gòu)中這種機(jī)制被稱(chēng)作system service,。

在核心態(tài)提供system service的有兩個(gè)家伙:ntoskrnl.exe和win32k.sys。ntoskrnl.exe是Windows的大腦,,它的上層被稱(chēng)為Executive,,下層被稱(chēng)作Kernel。Win32k.sys提供與顯示有關(guān)的system service,。

在用戶(hù)態(tài)一側(cè),,有一個(gè)重要的角色叫作ntdll.dll,大多數(shù)system service都是它調(diào)用的,。它封裝這些system service,然后提供一個(gè)API接口,。這個(gè)接口被稱(chēng)作native API,。 native API的用戶(hù)是各個(gè)子系統(tǒng)(subsystem),包括Win32子系統(tǒng),、OS/2子系統(tǒng),、POSIX子系統(tǒng)。各個(gè)子系統(tǒng)為Win32,、OS2,、POSIX程序提供了運(yùn)行平臺(tái)。

ntdll.dll由于提供了平臺(tái)無(wú)關(guān)的API接口,,所以被看作是NT系統(tǒng)的原生接口,,由之得到了“native API”的匪號(hào)。其實(shí)它的主要工作是將調(diào)用傳遞到核心態(tài),。

Win32,、OS/2、POSIX,聽(tīng)起來(lái)很龐大,。其實(shí)真正做好的只有Win32子系統(tǒng),。OS2、POSIX都是Console UI,,即只有字符界面,。提供OS/2子系統(tǒng),只因?yàn)樵?988年,,NT的主要設(shè)計(jì)目標(biāo)就是與OS/2兼容,,后來(lái)由于Windows 3.0賣(mài)得很好,所以設(shè)計(jì)目標(biāo)被變更為與Windows兼容,。提供POSIX子系統(tǒng),,是為了應(yīng)付美國(guó)政府的一個(gè)編號(hào)為FIPS 151-2的標(biāo)準(zhǔn)。

Win32子系統(tǒng)的管理員是一個(gè)叫作csrss.exe的弟兄,,它的全名是:Client/Server Run-Time Subsystem,。它剛上任時(shí),本來(lái)要分管所有的子系統(tǒng),,但后來(lái)POSIX和OS/2都被分別處理了,,所以只管了一個(gè)Win32。即使這樣也很了不起,,所有的Win32程序的進(jìn)程,、線程們都要向它登記。

不過(guò)Win32程序用得最多的還是Win32子系統(tǒng)的DLL們,,最核心的DLL包括:kernel32.dll,、User32.dll、Gdi32.dll,、Advapi32.dll(高級(jí)Win32應(yīng)用程序接口),。這些DLL包裝了ntdll.dll的native API。其中Gdi32.dll比較特殊,,它與核心態(tài)的win32k.sys直接保持聯(lián)系,,以提高NT系統(tǒng)的圖形處理能力。Win32子系統(tǒng)的DLL們提供的接口函數(shù)在MSDN文檔中被詳細(xì)介紹,,它們就是Win32 API,。

附錄0 Windows的啟動(dòng)

計(jì)算機(jī)上電后,從BIOS的ROM開(kāi)始運(yùn)行,。BIOS在做一些初始化后會(huì)將硬盤(pán)的第一個(gè)扇區(qū)的數(shù)據(jù)讀入內(nèi)存,,然后將控制權(quán)交給它,這段數(shù)據(jù)被稱(chēng)作Master Boot Record(MBR),。

MBR包含一段啟動(dòng)代碼和硬盤(pán)的主分區(qū)表,。這段啟動(dòng)代碼掃描主分區(qū)表,,找到第一個(gè)可以啟動(dòng)的分區(qū),然后將這個(gè)分區(qū)的第一個(gè)扇區(qū)讀入內(nèi)存并運(yùn)行,。這個(gè)扇區(qū)被稱(chēng)作引導(dǎo)扇區(qū)(boot sector),。

引導(dǎo)扇區(qū)的代碼具備讀文件系統(tǒng)根目錄的能力,顯然不同的文件系統(tǒng)需要不同的代碼,。引導(dǎo)扇區(qū)會(huì)從根目錄中讀出一個(gè)叫作ntldr的文件,。顧名思義,這個(gè)文件是load NT的主要角色,。它的業(yè)績(jī)主要包括將CPU從實(shí)模式轉(zhuǎn)入保護(hù)模式,,啟動(dòng)分頁(yè)機(jī)制,處理boot.ini等,。

如果boot.ini中有一句:

C:\bootsect.rh=”Red Hat Linux”

bootsect.rh的內(nèi)容是Linux引導(dǎo)扇區(qū),,用戶(hù)又選擇了“Red Hat Linux”,ntldr就會(huì)將執(zhí)行Linux的引導(dǎo)扇區(qū),,開(kāi)始Linux的引導(dǎo),。如果用戶(hù)選擇繼續(xù)使用Windows,ntldr會(huì)裝載并運(yùn)行我們前面提到的ntoskrnl.exe,。

ntoskrnl.exe會(huì)啟動(dòng)會(huì)話管理器smss.exe,。smss.exe啟動(dòng)csrss.exe和winlogon.exe。smss.exe會(huì)永遠(yuǎn)等待csrss.exe和winlogon.exe返回,。如果兩者之一異常中止,,就會(huì)導(dǎo)致系統(tǒng)崩潰。所以病毒們經(jīng)常以打擊csrss.exe為樂(lè),。

winlogon.exe負(fù)責(zé)用戶(hù)登錄,,在完成登錄后,它會(huì)啟動(dòng)注冊(cè)表HKLM\SOFTWARE\Microsoft\Windows NT\Current Version\Winlogon項(xiàng)下Userinit值指定的程序,。該值的缺省數(shù)據(jù)是userinit.exe,。userinit.exe會(huì)裝載個(gè)人設(shè)置,讓硬盤(pán)響個(gè)不停,,并考驗(yàn)我們的耐性,最后啟動(dòng)注冊(cè)表同一項(xiàng)下Shell值指定的程序,。該值的缺省數(shù)據(jù)是Explorer.exe,。Explorer.exe運(yùn)行后,我們就會(huì)看到熟悉的開(kāi)始菜單和桌面,。

1 Win32 API的A/W函數(shù)

要了解Win32子系統(tǒng)的DLL們提供了哪些API,,最直接的方法就是用Win32dsm直接查看DLL們的導(dǎo)出表。這時(shí)我們會(huì)發(fā)現(xiàn)Win32 API中帶字符串的API一般都有兩個(gè)版本,,例如CreateFileA和CreateFileW,。當(dāng)然也有例外,,例如GetProcAddress函數(shù)。

A代表ANSI代碼頁(yè),,W是寬字符,,即Unicode字符。Windows中的Unicode字符一般指UCS2的UTF16-LE編碼,。讓我們通過(guò)幾個(gè)實(shí)例觀察A/W版本間的關(guān)系,。

例1:用WIn32dsm查看gdi32.dll的匯編代碼,可以看到TextOutA調(diào)用GdiGetCodePage獲取當(dāng)前代碼頁(yè),,再調(diào)用MultiByteToWideChar轉(zhuǎn)換輸入的字符串,,然后調(diào)用一個(gè)內(nèi)部函數(shù)。而TextOutW直接調(diào)用這個(gè)內(nèi)部函數(shù),。

例2:用調(diào)試器跟蹤一個(gè)使用了CreateFileA的程序,,可以看到:CreateFileA在將輸入字符串轉(zhuǎn)換為Unicode后,會(huì)調(diào)用CreateFileW,。假設(shè)輸入文件名是“測(cè)試.txt”,,對(duì)應(yīng)的數(shù)據(jù)就是:“B2 E2 CA D4 2E 74 78 74 00”。
在調(diào)試器中可以看到傳給CreateFileW的文件名數(shù)據(jù)是:“4B 6D D5 8B 2E 00 74 00 78 00 74 00 00 00”,。 這是”測(cè)試.txt”對(duì)應(yīng)的Unicdoe字符串,。CreateFileW會(huì)接著調(diào)用ntdll.dll中的NtCreateFile。順便看看NtCreateFile的代碼:
mov eax, 00000020
lea edx, dword ptr [esp+04]
int 2E
ret 002C
可見(jiàn)這個(gè)native API只是簡(jiǎn)單地調(diào)用了核心態(tài)提供的0×20號(hào)system service,。

例3:gdi32.dll中的GetGlyphOutline函數(shù)可以獲取指定字符的字模,。GetGlyphOutlineA和GetGlyphOutlineW函數(shù)都會(huì)調(diào)用同一個(gè)內(nèi)部函數(shù)(記作F)。函數(shù)F在返回前將通過(guò)int 2E調(diào)用0×10B1號(hào)system service,。
GetGlyphOutlineW直接調(diào)用函數(shù)F,。GetGlyphOutlineA在調(diào)用函數(shù)F前,要依次調(diào)用GdiGetCodePage,、IsDBCSLeadByteEx和MultiByteToWideChar,,將當(dāng)前代碼頁(yè)的字符編碼轉(zhuǎn)換成Unicode編碼。
如果我們調(diào)用GetGlyphOutlineA時(shí)傳入“baba”,,這是“漢”字的GBK編碼,,用調(diào)試器可以看到傳給函數(shù)F的字符編碼是“6c49”,這是“漢”字的Unicode編碼,。

從以上例子可見(jiàn),,A版本總會(huì)在某處將輸入的字符串轉(zhuǎn)換為Unicode字符串,然后和W版本執(zhí)行相同的代碼,。在由A/W版本API引出MBCS程序和Unicode程序前,,讓我們先解釋一下Locale和ANSI代碼頁(yè)。

2 Locale和ANSI代碼頁(yè)

2.1 Locale和LCID

Locale是指特定于某個(gè)國(guó)家或地區(qū)的一組設(shè)定,,包括字符集,,數(shù)字,、貨幣、時(shí)間和日期的格式等,。在Windows中,,每個(gè)Locale可以用一個(gè)32位數(shù)字表示,記作LCID,。在winnt.h中可以看到LCID的組成,。它的高16位表示字符的排序方法,一般為0,。在它的低16位中,,低10位是primary language的ID,高4位指定sublanguage,。sublanguage被用來(lái)區(qū)分同一種語(yǔ)言的不同編碼,。下面是部分primary language和sublanguage的常數(shù)定義:

#define LANG_CHINESE 0×04
#define LANG_ENGLISH 0×09
#define LANG_FRENCH 0×0c
#define LANG_GERMAN 0×07

#define SUBLANG_CHINESE_TRADITIONAL 0×01 // Chinese (Taiwan Region)
#define SUBLANG_CHINESE_SIMPLIFIED 0×02 // Chinese (PR China)
#define SUBLANG_ENGLISH_US 0×01 // English (USA)
#define SUBLANG_ENGLISH_UK 0×02 // English (UK)

好,現(xiàn)在我們可以計(jì)算簡(jiǎn)體中文的LCID了,,將sublanguage的常數(shù)左移10位,,即乘上1024,再加上primary language的常數(shù):2*1024+4=2052,,16進(jìn)制是0804,。美國(guó)英語(yǔ)是:1*1024+9=1033,16進(jìn)制是0409,。,。繁體中文是1*1024+4=1028,16進(jìn)制是0404,。

2.2 代碼頁(yè)

每個(gè)Locale都聯(lián)系著很多信息,,可以通過(guò)GetLocalInfo函數(shù)讀取。其中最重要的信息就是字符集了,,即Locale對(duì)應(yīng)的語(yǔ)言文字的編碼,。Windows將字符集稱(chēng)作代碼頁(yè)。

每個(gè)Locale可以對(duì)應(yīng)一個(gè)ANSI代碼頁(yè)和一個(gè)OEM代碼頁(yè),。Win32 API使用ANSI代碼頁(yè),,底層設(shè)備使用OEM代碼頁(yè),兩者可以相互映射,。

例如English (US)的ANSI和OEM代碼頁(yè)分別為“1252 (ANSI - Latin I)”和“437 (OEM - United States)”,。 Chinese (PRC)的ANSI和OEM代碼頁(yè)都是“936 (ANSI/OEM - Simplified Chinese GBK)”。  Chinese (TW)的ANSI和OEM代碼頁(yè)都是“950 (ANSI/OEM - Traditional Chinese Big5)”,。

附錄1中有一張很長(zhǎng)的表。列出了我正在使用的Windows所支持的135個(gè)Locale的部分信息,,包括 LCID,、國(guó)家/地區(qū)名稱(chēng),、語(yǔ)言名稱(chēng)、語(yǔ)言縮寫(xiě)和對(duì)應(yīng)的ANSI代碼頁(yè),。

2.3 系統(tǒng)Locale,、用戶(hù)Locale,再談ANSI代碼頁(yè)

在Windows中,,通過(guò)控制面板可以為系統(tǒng)和用戶(hù)分別設(shè)置Locale,。系統(tǒng)Locale決定代碼頁(yè),用戶(hù)Locale決定數(shù)字,、貨幣,、時(shí)間和日期的格式。這不是一個(gè)好的設(shè)計(jì),,后面會(huì)談到它帶來(lái)的問(wèn)題,。

使用GetSystemDefaultLCID函數(shù)和GetUserDefaultLCID函數(shù)分別得到系統(tǒng)和用戶(hù)的LCID。有很多材料將這兩個(gè)函數(shù)和另外兩個(gè)函數(shù)混淆:GetSystemDefaultUILanguage和GetUserDefaultUILanguage,。

GetSystemDefaultUILanguage和GetUserDefaultUILanguage得到的是您當(dāng)前使用的Windows版本所帶的UI資源的語(yǔ)言,。

用戶(hù)程序缺省使用的代碼頁(yè)是當(dāng)前系統(tǒng)Locale的ANSI代碼頁(yè),可以稱(chēng)作ANSI編碼,,也就是A版本的Win32 API默認(rèn)的字符編碼,。對(duì)于一個(gè)未指定編碼方式的文本文件,Windows會(huì)按照ANSI編碼解釋,。

2.4 AppLocale

如果一個(gè)文本文件采用BIG5編碼,,系統(tǒng)當(dāng)前的ANSI代碼頁(yè)是GBK。打開(kāi)這個(gè)文件,,就會(huì)顯示亂碼,。例如“中文”在BIG5中的編碼是A4A4、A4E5,,這兩個(gè)編碼在GBK中對(duì)應(yīng)的字符是“いゅ”,。這是日文的兩個(gè)平假名。

在Windows XP平臺(tái)有一個(gè)AppLocale程序,,可以以指定的語(yǔ)言運(yùn)行非Unicode程序,。用Win32dsm打開(kāi)看一看,其實(shí)它只是在運(yùn)行程序前設(shè)置了兩個(gè)環(huán)境變量,。我們可以用個(gè)批處理文件模仿一下:

@ECHO OFF
SET __COMPAT_LAYER=#ApplicationLocale
SET ApplocaleID=0404
start notepad.exe

在簡(jiǎn)體中文平臺(tái),,用這個(gè)批處理文件啟動(dòng)的記事本可以正確顯示BIG5編碼的文本文件。用它打開(kāi)GBK編碼的文本文件會(huì)怎么樣,?“中文”會(huì)被顯示為“笢恅”,。設(shè)置這兩個(gè)環(huán)境變量會(huì)作用于當(dāng)前進(jìn)程和其子進(jìn)程。Windows 2000平臺(tái)不支持這個(gè)方法,。

3 MBCS程序和Unicode程序

3.1 與字符編碼有關(guān)的編譯參數(shù)

讓我們回到Win32 API,。我們?cè)诔绦蛑惺褂玫腤in32 API沒(méi)有A/W后綴,,Windows的頭文件會(huì)根據(jù)編譯參數(shù)UNICODE將沒(méi)有后綴的函數(shù)名替換為A版本或W版本,例如:

#ifdef UNICODE
#define CreateFile CreateFileW
#else
#define CreateFile CreateFileA
#endif

C RunTime庫(kù)(CRT)使用_UNICODE和_MBCS來(lái)區(qū)分三套字符串處理函數(shù),,分別用于SBCS,、MBCS和Unicdoe字符串。SBCS和MBCS分別指單字節(jié)字符串和多字節(jié)字符串,。例如_tcsclen的3個(gè)版本分別為strlen,、_mbslen和wcslen ,猜猜以下函數(shù)返回幾,?

strlen(”VOIP網(wǎng)關(guān)”);
_mbslen((unsigned char *)”VOIP網(wǎng)關(guān)”);
wcslen(L”VOIP網(wǎng)關(guān)”);

答案是8,、6、6,。L”ANSI字符串”通知編譯器將ANSI字符串轉(zhuǎn)換為Unicode字符串,,這是VC++編譯器提供的一個(gè)小甜點(diǎn)。不過(guò)我們應(yīng)該用宏:_T(”ANSI字符串”),。_T宏只在我們定義了_UNICODE時(shí)才轉(zhuǎn)換,。這樣同一套代碼既可以編譯MBCS版本,也可以編譯Unicode版本,。

MFC用_UNICODE參數(shù)區(qū)分Unicode版本特有的代碼,,決定使用什么版本的導(dǎo)入庫(kù)或靜態(tài)庫(kù)。

3.2 Unicode程序,、MBCS程序和多語(yǔ)言支持

Unicode程序直接使用Unicode版本的CRT和Win32 API。Unicode程序的運(yùn)行與當(dāng)前的ANSI代碼頁(yè)沒(méi)有關(guān)系,。MBCS程序的運(yùn)行依賴(lài)于ANSI代碼頁(yè),。如果設(shè)計(jì)者和使用者使用不同的代碼頁(yè),就可能出現(xiàn)亂碼,。微軟開(kāi)發(fā)的程序大都是Unicode程序,,不管我們?cè)鯓幼儞Q系統(tǒng)Locale,,它們總能正常運(yùn)行。

使用VCL類(lèi)庫(kù)的Delphi程序都是MBCS程序,。VCL框架在程序啟動(dòng)會(huì)調(diào)用GetThreadLocale獲取當(dāng)前用戶(hù)的LCID,,然后在當(dāng)前目錄查找對(duì)應(yīng)的資源文件,,命名規(guī)則是:程序名+’.‘+語(yǔ)言縮寫(xiě),,語(yǔ)言縮寫(xiě)可以參見(jiàn)附錄1,。在找不到時(shí)才會(huì)使用EXE文件中的資源,。不過(guò)如果系統(tǒng)LCID是English(United States),用戶(hù)LCID是Chinese(PRC),,由VCL產(chǎn)生的程序就會(huì)出現(xiàn)亂碼,。讀者可以自己分析原因。

為VCL程序做多語(yǔ)言版本,。只要用Delphi自帶的Resource DLL Wizard再做一個(gè)特定語(yǔ)言的資源DLL,,原來(lái)的程序都不用改。不過(guò)很多程序員用其它組件做多語(yǔ)言版本,,例如TsiLang ,。

MBCS程序雖然也可以做成多語(yǔ)言版本,但它無(wú)法在同時(shí)顯示不同代碼頁(yè)特有的字符,,這時(shí)就必須使用Unicode程序了,。

VS.NET文檔中有個(gè)多語(yǔ)言資源的例子:SatDLL。它只用Win32 API的例子,,卻用了VC7項(xiàng)目,。我在學(xué)習(xí)時(shí)將它改成了VC6項(xiàng)目,,并糾正了它的兩個(gè)問(wèn)題:
1、用GetUserDefaultUILanguage讀到的是Windows資源版本,,不是當(dāng)前用戶(hù)設(shè)置的代碼頁(yè),。
2,、啟動(dòng)時(shí)沒(méi)有使用資源DLL里的菜單,。

在我的個(gè)人主頁(yè)(http://fmddlmyy.home4u.china.com)上可以下載修改過(guò)的SatDLL,。這個(gè)程序說(shuō)明了支持多語(yǔ)言資源的基本思路:將不同語(yǔ)言資源放到不同的DLL中,在程序啟動(dòng)時(shí)根據(jù)當(dāng)前Locale裝載對(duì)應(yīng)的資源DLL,。必要時(shí)動(dòng)態(tài)切換資源。為了標(biāo)記不同語(yǔ)言的資源,可以將它們放到不同的目錄中,以LCID作為目錄名,,例如“2052”,、“1033”。當(dāng)然我們也可以用其它方法聯(lián)系LCID和資源DLL,。

MFC程序可以在App類(lèi)的InitInstance函數(shù)中用AfxSetResourceHandle函數(shù)設(shè)置資源DLL。在Delphi中動(dòng)態(tài)切換資源可以參考Delphi Demo目錄RichEdit項(xiàng)目的ReInit.pas,。在讀取當(dāng)前設(shè)定時(shí),,建議用GetSystemDefaultLCID函數(shù),因?yàn)橄到y(tǒng)Locale決定ANSI代碼頁(yè),。

3.4 資源和亂碼

通過(guò)檢查可執(zhí)行文件,,我們可以確定VC和Delphi的資源編譯器都以Unicode保存字符資源。在VC環(huán)境編輯資源時(shí),,我們會(huì)指定資源的代碼頁(yè),。編譯器根據(jù)資源的代碼頁(yè),將其轉(zhuǎn)換到Unicode,。

Unicode程序直接使用以Unicode編碼保存的資源,。MBCS程序需要將Unicode資源先轉(zhuǎn)換回當(dāng)前ANSI代碼頁(yè),然后再使用,。如果資源中的Unicode字符串不能映射到當(dāng)前代碼頁(yè)中的字符,,就會(huì)出現(xiàn)??。

例如Windows的標(biāo)準(zhǔn)對(duì)話框也會(huì)出現(xiàn)亂碼,。假設(shè)我們使用簡(jiǎn)體中文Windows,,當(dāng)前Locale是Chinese (TW),我們的程序是MBCS的,,使用標(biāo)準(zhǔn)的打開(kāi)文件對(duì)話框,。因?yàn)樵贐IG5中沒(méi)有“開(kāi)”這個(gè)字,所以“打開(kāi)”會(huì)被顯示成“打?”,。將程序編譯成Unicode版本,,就可以避免這個(gè)問(wèn)題。

如果字符不是保存在資源中,,而是硬編碼在程序中,。然后開(kāi)發(fā)者和用戶(hù)使用不同的代碼頁(yè),,就會(huì)導(dǎo)致亂碼。假設(shè)開(kāi)發(fā)者的Locale是Chinese (PRC),,用戶(hù)的Locale是English (US),,程序中硬編碼了字符串“文件”。 Chinese (PRC)的ANSI代碼頁(yè)是GBK,,“文件”的編碼“CE C4 BC FE”,。English (US)的ANSI代碼頁(yè)是Latin I,用戶(hù)按照Latin I編碼去解釋“CE C4 BC FE”,,就會(huì)看到“???t”,。

回答我前面提過(guò)的一個(gè)問(wèn)題:Delphi程序根據(jù)用戶(hù)LCID轉(zhuǎn)換資源中的字符串。如果用戶(hù)LCID是Chinese (PRC),,系統(tǒng)LCID是English (US),。那么資源中的Unicode字符串會(huì)被轉(zhuǎn)換為GBK編碼,然后按照Latin I顯示,,這時(shí)我們看到的就是類(lèi)似“???t”的東東,,不是??。

既然資源是以Unicode保存的,,MBCS程序如果不將其轉(zhuǎn)換到ANSI代碼頁(yè),而用W版本的函數(shù)直接顯示,,就不會(huì)產(chǎn)生亂碼,。例如MFC程序菜單里的中文,在English (US)的Locale也可以正常顯示,。不過(guò)這取決于各部分代碼的具體實(shí)現(xiàn),,menu bar控件里的中文在English (US)的Locale會(huì)全部顯示成??。

進(jìn)一步的參考資料

本文的第0節(jié)和附錄0主要參考了《Inside Windows 2000 Third Edition》,,國(guó)內(nèi)出過(guò)該書(shū)的影印版,。DDK文檔中有大量Windows內(nèi)核的信息。用Win32dsm和各種調(diào)試器查看Windows系統(tǒng)文件可以獲得更直接的信息,。

關(guān)于Window程序的字符編碼,,最好的參考資料是winnt.h等SDK的包含文件、VCL,、MFC,、CRT的源文件。我們不需要閱讀它們,,只要找到自己感興趣的信息就可以了,,用Source Insight可能方便一些。

本文所談的不是什么萬(wàn)古不遷的道理,,只是別的程序員的一些設(shè)定,,我們因?yàn)樾枰褂盟麄兊某绦?,所以有必要了解一些?xì)節(jié)。研究問(wèn)題的方法和興趣永遠(yuǎn)比問(wèn)題本身重要,,如一句拉丁俗語(yǔ)所說(shuō):res, non verba,,實(shí)質(zhì)勝于文字。

尾聲

“明月雖有圓缺,,但畢竟永恒不滅,,人生卻如過(guò)眼煙云,一去不回,,真不知計(jì)較為何,?”

“蛙聲雖是短促,但卻是萬(wàn)籟中一個(gè)活潑的禪機(jī),,也可以說(shuō)萬(wàn)古如斯,,永恒不遷,無(wú)奈感受到的,,能有幾人,?”

這是一本武俠書(shū)中的對(duì)話。在時(shí)間的長(zhǎng)河中,,人生和蛙聲一樣易逝,。說(shuō)到蛙聲,我的20個(gè)月的小寶寶在喝湯后,,略加醞釀,,就會(huì)緊閉著嘴巴,發(fā)出很像蛙鳴的聲音,。我們會(huì)逗他說(shuō):“小青蛙又來(lái)了”,。小家伙益發(fā)得意,不管我的抗議,,將連湯帶油的小下巴親熱地貼在我的身上,。

 

附錄1 一些關(guān)于LCID的信息

使用EnumSystemLocales函數(shù)可以枚舉系統(tǒng)支持的LCID。用GetLocaleInfo可以得到ANSI代碼頁(yè)的ID,,再通過(guò)GetCPInfoEx可以獲得代碼頁(yè)的全稱(chēng),。以下是我在中文Windows XP上讀到的內(nèi)容。

LCID 國(guó)家或地區(qū) 語(yǔ)言 語(yǔ)言縮寫(xiě) ANSI代碼頁(yè)
1025 沙特阿拉伯 阿拉伯語(yǔ)(沙特阿拉伯) ARA 1256  (ANSI - 阿拉伯文)
1026 保加利亞 保加利亞語(yǔ) BGR 1251  (ANSI - 西里爾文)
1027 西班牙 加泰隆語(yǔ) CAT 1252  (ANSI - 拉丁文 I)
1028 臺(tái)灣 中文(臺(tái)灣) CHT 950   (ANSI/OEM - 繁體中文 Big5)
1029 捷克共和國(guó) 捷克語(yǔ) CSY 1250  (ANSI - 中歐)
1030 丹麥 丹麥語(yǔ) DAN 1252  (ANSI - 拉丁文 I)
1031 德國(guó) 德語(yǔ)(德國(guó)) DEU 1252  (ANSI - 拉丁文 I)
1032 希臘 希臘語(yǔ) ELL 1253  (ANSI - 希臘文)
1033 美國(guó) 英語(yǔ)(美國(guó)) ENU 1252  (ANSI - 拉丁文 I)
1034 西班牙 西班牙語(yǔ)(傳統(tǒng)) ESP 1252  (ANSI - 拉丁文 I)
1035 芬蘭 芬蘭語(yǔ) FIN 1252  (ANSI - 拉丁文 I)
1036 法國(guó) 法語(yǔ)(法國(guó)) FRA 1252  (ANSI - 拉丁文 I)
1037 以色列 希伯來(lái)語(yǔ) HEB 1255  (ANSI - 希伯來(lái)文)
1038 匈牙利 匈牙利語(yǔ) HUN 1250  (ANSI - 中歐)
1039 冰島 冰島語(yǔ) ISL 1252  (ANSI - 拉丁文 I)
1040 意大利 意大利語(yǔ)(意大利) ITA 1252  (ANSI - 拉丁文 I)
1041 日本 日語(yǔ) JPN 932   (ANSI/OEM - 日文 Shift-JIS)
1042 朝鮮 朝鮮語(yǔ) KOR 949   (ANSI/OEM - 韓文)
1043 荷蘭 荷蘭語(yǔ)(荷蘭) NLD 1252  (ANSI - 拉丁文 I)
1044 挪威 挪威語(yǔ)(伯克梅爾) NOR 1252  (ANSI - 拉丁文 I)
1045 波蘭 波蘭語(yǔ) PLK 1250  (ANSI - 中歐)
1046 巴西 葡萄牙語(yǔ)(巴西) PTB 1252  (ANSI - 拉丁文 I)
1048 羅馬尼亞 羅馬尼亞語(yǔ) ROM 1250  (ANSI - 中歐)
1049 俄羅斯 俄語(yǔ) RUS 1251  (ANSI - 西里爾文)
1050 克羅地亞 克羅地亞語(yǔ) HRV 1250  (ANSI - 中歐)
1051 斯洛伐克語(yǔ) 斯洛伐克語(yǔ) SKY 1250  (ANSI - 中歐)
1052 阿爾巴尼亞 阿爾巴尼亞語(yǔ) SQI 1250  (ANSI - 中歐)
1053 瑞典 瑞典語(yǔ) SVE 1252  (ANSI - 拉丁文 I)
1054 泰國(guó) 泰語(yǔ) THA 874   (ANSI/OEM - 泰文)
1055 土耳其 土耳其語(yǔ) TRK 1254  (ANSI - 土耳其文)
1056 巴基斯坦伊斯蘭共和國(guó) 烏都語(yǔ) URD 1256  (ANSI - 阿拉伯文)
1057 印度尼西亞 印度尼西亞語(yǔ) IND 1252  (ANSI - 拉丁文 I)
1058 烏克蘭 烏克蘭語(yǔ) UKR 1251  (ANSI - 西里爾文)
1059 比利時(shí) 比利時(shí)語(yǔ) BEL 1251  (ANSI - 西里爾文)
1060 斯洛文尼亞 斯洛文尼亞語(yǔ) SLV 1250  (ANSI - 中歐)
1061 愛(ài)沙尼亞 愛(ài)沙尼亞語(yǔ) ETI 1257  (ANSI - 波羅的海文)
1062 拉脫維亞 拉脫維亞語(yǔ) LVI 1257  (ANSI - 波羅的海文)
1063 立陶宛 立陶宛語(yǔ) LTH 1257  (ANSI - 波羅的海文)
1065 伊朗 法斯語(yǔ) FAR 1256  (ANSI - 阿拉伯文)
1066 越南 越南語(yǔ) VIT 1258  (ANSI/OEM - 越南)
1067 亞美尼亞 亞美尼亞語(yǔ) HYE 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1068 阿塞拜疆 阿塞拜疆語(yǔ)(拉丁文) AZE 1254  (ANSI - 土耳其文)
1069 西班牙 巴士克語(yǔ) EUQ 1252  (ANSI - 拉丁文 I)
1071 前南斯拉夫馬其頓共和國(guó) 馬其頓語(yǔ)(FYROM) MKI 1251  (ANSI - 西里爾文)
1078 南非 南非語(yǔ) AFK 1252  (ANSI - 拉丁文 I)
1079 格魯吉亞 格魯吉亞語(yǔ) KAT 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1080 法羅群島 法羅語(yǔ) FOS 1252  (ANSI - 拉丁文 I)
1081 印度 印地語(yǔ) HIN 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1086 馬來(lái)西亞 馬來(lái)語(yǔ)(馬來(lái)西亞) MSL 1252  (ANSI - 拉丁文 I)
1087 吉爾吉斯坦 哈薩克語(yǔ) KKZ 1251  (ANSI - 西里爾文)
1088 吉爾吉斯斯坦 吉爾吉斯語(yǔ) (西里爾文) KYR 1251  (ANSI - 西里爾文)
1089 肯尼亞 斯瓦希里語(yǔ) SWK 1252  (ANSI - 拉丁文 I)
1091 烏茲別克斯坦 烏茲別克語(yǔ)(拉丁文) UZB 1254  (ANSI - 土耳其文)
1092 韃靼斯坦 韃靼語(yǔ) TTT 1251  (ANSI - 西里爾文)
1094 印度 旁遮普語(yǔ) PAN 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1095 印度 古吉拉特語(yǔ) GUJ 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1097 印度 泰米爾語(yǔ) TAM 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1098 印度 泰盧固語(yǔ) TEL 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1099 印度 卡納拉語(yǔ) KAN 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1102 印度 馬拉地語(yǔ) MAR 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1103 印度 梵文 SAN 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1104 蒙古 蒙古語(yǔ)(西里爾文) MON 1251  (ANSI - 西里爾文)
1110 西班牙 加里西亞語(yǔ) GLC 1252  (ANSI - 拉丁文 I)
1111 印度 孔卡尼語(yǔ) KNK 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1114 敘利亞 敘利亞語(yǔ) SYR 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
1125 馬爾代夫 第維埃語(yǔ) DIV 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
2049 伊拉克 阿拉伯語(yǔ)(伊拉克) ARI 1256  (ANSI - 阿拉伯文)
2052 中華人民共和國(guó) 中文(中國(guó)) CHS 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
2055 瑞士 德語(yǔ)(瑞士) DES 1252  (ANSI - 拉丁文 I)
2057 英國(guó) 英語(yǔ)(英國(guó)) ENG 1252  (ANSI - 拉丁文 I)
2058 墨西哥 西班牙語(yǔ)(墨西哥) ESM 1252  (ANSI - 拉丁文 I)
2060 比利時(shí) 法語(yǔ)(比利時(shí)) FRB 1252  (ANSI - 拉丁文 I)
2064 瑞士 意大利語(yǔ)(瑞士) ITS 1252  (ANSI - 拉丁文 I)
2067 比利時(shí) 荷蘭語(yǔ)(比利時(shí)) NLB 1252  (ANSI - 拉丁文 I)
2068 挪威 挪威語(yǔ)(尼諾斯克) NON 1252  (ANSI - 拉丁文 I)
2070 葡萄牙 葡萄牙語(yǔ)(葡萄牙) PTG 1252  (ANSI - 拉丁文 I)
2074 塞爾維亞 塞爾維亞語(yǔ)(拉丁文) SRL 1250  (ANSI - 中歐)
2077 芬蘭 瑞典語(yǔ)(芬蘭) SVF 1252  (ANSI - 拉丁文 I)
2092 阿塞拜疆 阿塞拜疆語(yǔ)(西里爾文) AZE 1251  (ANSI - 西里爾文)
2110 文萊達(dá)魯薩蘭 馬來(lái)語(yǔ)(文萊達(dá)魯薩蘭) MSB 1252  (ANSI - 拉丁文 I)
2115 烏茲別克斯坦 烏茲別克語(yǔ)(西里爾文) UZB 1251  (ANSI - 西里爾文)
3073 埃及 阿拉伯語(yǔ)(埃及) ARE 1256  (ANSI - 阿拉伯文)
3076 香港特別行政區(qū) 中文(香港特別行政區(qū)) ZHH 950   (ANSI/OEM - 繁體中文 Big5)
3079 奧地利 德語(yǔ)(奧地利) DEA 1252  (ANSI - 拉丁文 I)
3081 澳大利亞 英語(yǔ)(澳大利亞) ENA 1252  (ANSI - 拉丁文 I)
3082 西班牙 西班牙語(yǔ)(國(guó)際) ESN 1252  (ANSI - 拉丁文 I)
3084 加拿大 法語(yǔ)(加拿大) FRC 1252  (ANSI - 拉丁文 I)
3098 塞爾維亞 塞爾維亞語(yǔ)(西里爾文) SRB 1251  (ANSI - 西里爾文)
4097 利比亞 阿拉伯語(yǔ)(利比亞) ARL 1256  (ANSI - 阿拉伯文)
4100 新加坡 中文(新加坡) ZHI 936   (ANSI/OEM - 簡(jiǎn)體中文 GBK)
4103 盧森堡 德語(yǔ)(盧森堡) DEL 1252  (ANSI - 拉丁文 I)
4105 加拿大 英語(yǔ)(加拿大) ENC 1252  (ANSI - 拉丁文 I)
4106 危地馬拉 西班牙語(yǔ)(危地馬拉) ESG 1252  (ANSI - 拉丁文 I)
4108 瑞士 法語(yǔ)(瑞士) FRS 1252  (ANSI - 拉丁文 I)
5121 阿爾及利亞 阿拉伯語(yǔ)(阿爾及利亞) ARG 1256  (ANSI - 阿拉伯文)
5124 澳門(mén)特別行政區(qū) 中文(澳門(mén)特別行政區(qū)) ZHM 950   (ANSI/OEM - 繁體中文 Big5)
5127 列支敦士登 德語(yǔ)(列支敦士登) DEC 1252  (ANSI - 拉丁文 I)
5129 新西蘭 英語(yǔ)(新西蘭) ENZ 1252  (ANSI - 拉丁文 I)
5130 哥斯達(dá)黎加 西班牙語(yǔ)(哥斯達(dá)黎加) ESC 1252  (ANSI - 拉丁文 I)
5132 盧森堡 法語(yǔ)(盧森堡) FRL 1252  (ANSI - 拉丁文 I)
6145 摩洛哥 阿拉伯語(yǔ)(摩洛哥) ARM 1256  (ANSI - 阿拉伯文)
6153 愛(ài)爾蘭 英語(yǔ)(愛(ài)爾蘭) ENI 1252  (ANSI - 拉丁文 I)
6154 巴拿馬 西班牙語(yǔ)(巴拿馬) ESA 1252  (ANSI - 拉丁文 I)
6156 摩納哥公國(guó) 法語(yǔ)(摩納哥) FRM 1252  (ANSI - 拉丁文 I)
7169 突尼斯 阿拉伯語(yǔ)(突尼斯) ART 1256  (ANSI - 阿拉伯文)
7177 南非 英語(yǔ)(南非) ENS 1252  (ANSI - 拉丁文 I)
7178 多米尼加共和國(guó) 西班牙語(yǔ)(多米尼加共和國(guó)) ESD 1252  (ANSI - 拉丁文 I)
8193 阿曼 阿拉伯語(yǔ)(阿曼) ARO 1256  (ANSI - 阿拉伯文)
8201 牙買(mǎi)加 英語(yǔ)(牙買(mǎi)加) ENJ 1252  (ANSI - 拉丁文 I)
8202 委內(nèi)瑞拉 西班牙語(yǔ)(委內(nèi)瑞拉) ESV 1252  (ANSI - 拉丁文 I)
9217 也門(mén) 阿拉伯語(yǔ)(也門(mén)) ARY 1256  (ANSI - 阿拉伯文)
9225 加勒比海 英語(yǔ)(加勒比海) ENB 1252  (ANSI - 拉丁文 I)
9226 哥倫比亞 西班牙語(yǔ)(哥倫比亞) ESO 1252  (ANSI - 拉丁文 I)
10241 敘利亞 阿拉伯語(yǔ)(敘利亞) ARS 1256  (ANSI - 阿拉伯文)
10249 伯利茲 英語(yǔ)(伯利茲) ENL 1252  (ANSI - 拉丁文 I)
10250 秘魯 西班牙語(yǔ)(秘魯) ESR 1252  (ANSI - 拉丁文 I)
11265 約旦 阿拉伯語(yǔ)(約旦) ARJ 1256  (ANSI - 阿拉伯文)
11273 特立尼達(dá)和多巴哥 英語(yǔ)(特立尼達(dá)) ENT 1252  (ANSI - 拉丁文 I)
11274 阿根廷 西班牙語(yǔ)(阿根廷) ESS 1252  (ANSI - 拉丁文 I)
12289 黎巴嫩 阿拉伯語(yǔ)(黎巴嫩) ARB 1256  (ANSI - 阿拉伯文)
12297 津巴布韋 英語(yǔ)(津巴布韋) ENW 1252  (ANSI - 拉丁文 I)
12298 厄瓜多爾 西班牙語(yǔ)(厄瓜多爾) ESF 1252  (ANSI - 拉丁文 I)
13313 科威特 阿拉伯語(yǔ)(科威特) ARK 1256  (ANSI - 阿拉伯文)
13321 菲律賓共和國(guó) 英語(yǔ)(菲律賓) ENP 1252  (ANSI - 拉丁文 I)
13322 智利 西班牙語(yǔ)(智利) ESL 1252  (ANSI - 拉丁文 I)
14337 阿聯(lián)酋 阿拉伯語(yǔ)(阿聯(lián)酋) ARU 1256  (ANSI - 阿拉伯文)
14346 烏拉圭 西班牙語(yǔ)(烏拉圭) ESY 1252  (ANSI - 拉丁文 I)
15361 巴林 阿拉伯語(yǔ)(巴林) ARH 1256  (ANSI - 阿拉伯文)
15370 巴拉圭 西班牙語(yǔ)(巴拉圭) ESZ 1252  (ANSI - 拉丁文 I)
16385 卡塔爾 阿拉伯語(yǔ)(卡塔爾) ARQ 1256  (ANSI - 阿拉伯文)
16394 玻利維亞 西班牙語(yǔ)(玻利維亞) ESB 1252  (ANSI - 拉丁文 I)
17418 薩爾瓦多 西班牙語(yǔ)(薩爾瓦多) ESE 1252  (ANSI - 拉丁文 I)
18442 洪都拉斯 西班牙語(yǔ)(洪都拉斯) ESH 1252  (ANSI - 拉丁文 I)
19466 尼加拉瓜 西班牙語(yǔ)(尼加拉瓜) ESI 1252  (ANSI - 拉丁文 I)
20490 波多黎各(美) 西班牙語(yǔ)(波多黎各(美)) ESU 1252  (ANSI - 拉丁文 I)

LCID取決于語(yǔ)言,,在表中列出國(guó)家名只是為了增加趣味性,。例如可以看到以色列還在使用古老的希伯來(lái)語(yǔ)?!跋2畞?lái)語(yǔ)”的法文是hébreu,,這個(gè)單詞還有一個(gè)意思,就是“不能理解的東西”,。

 

 

在winnt.h中,,對(duì)subsystem的定義如下:
#define IMAGE_SUBSYSTEM_UNKNOWN 0 // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE 1 // Image doesn’t require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI 5 // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI 7 // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 // Image runs in the Windows CE subsystem.
CUI就是Console UI了,。我們使用的subsystem主要是3和2。

在用戶(hù)態(tài)看起來(lái)很底層的東西,,例如Win32 subsystem的核心:kernel32.dll,、user32.dll、gdi32.dll,,基本上只是ntdll.dll的一個(gè)包裝,,而ntdll.dll包裝了從用戶(hù)態(tài)到核心態(tài)的system call,也稱(chēng)作“Native System Service”,。
用戶(hù)態(tài)不能訪問(wèn)核心態(tài)的任何函數(shù)和變量,,所以system call不同于一般的API調(diào)用。system call可以被看作:將要調(diào)用的功能ID放到eax,,然后執(zhí)行INT 2e,。
ntdll.dll通過(guò)system call使用核心態(tài)的ntoskrnl.exe和win32k.sys提供的功能。ntoskrnl.exe被尊稱(chēng)為“Executive”,,可以看作是NT的大腦級(jí)模塊,。win32k.sys提供NT圖形庫(kù)接口的API。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶(hù)發(fā)布,,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買(mǎi)等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多