我們在使用和安裝Windows程序時(shí),有時(shí)會(huì )看到以“2052”、“1033”這些數字為名的文件夾(如Office),這些數字似乎和字符集有關(guān),但它們究竟是什么意思呢?研究這個(gè)問(wèn)題的同時(shí),又會(huì )遇到其它問(wèn)題。我們會(huì )談到Windows的內部架構、Win32 API的A/W函數、Locale、ANSI代碼頁(yè)、與字符編碼有關(guān)的編譯參數、MBCS和Unicode程序、資源和亂碼等,一起經(jīng)歷這段瑣碎細節為主,間或樂(lè )趣點(diǎn)綴的旅程。
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的一種保護模式。在x86 CPU上,用戶(hù)態(tài)處于ring 3,核心態(tài)處于ring 0。
從用戶(hù)態(tài)進(jìn)入核心態(tài)的最常用的方法是在寄存器eax填一個(gè)功能碼,然后執行int 2e。這有點(diǎn)像DOS時(shí)代的DOS和BIOS系統調用。在NT架構中這種機制被稱(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)一側,有一個(gè)重要的角色叫作ntdll.dll,大多數system service都是它調用的。它封裝這些system service,然后提供一個(gè)API接口。這個(gè)接口被稱(chēng)作native API。 native API的用戶(hù)是各個(gè)子系統(subsystem),包括Win32子系統、OS/2子系統、POSIX子系統。各個(gè)子系統為Win32、OS2、POSIX程序提供了運行平臺。
ntdll.dll由于提供了平臺無(wú)關(guān)的API接口,所以被看作是NT系統的原生接口,由之得到了“native API”的匪號。其實(shí)它的主要工作是將調用傳遞到核心態(tài)。
Win32、OS/2、POSIX,聽(tīng)起來(lái)很龐大。其實(shí)真正做好的只有Win32子系統。OS2、POSIX都是Console UI,即只有字符界面。提供OS/2子系統,只因為在1988年,NT的主要設計目標就是與OS/2兼容,后來(lái)由于Windows 3.0賣(mài)得很好,所以設計目標被變更為與Windows兼容。提供POSIX子系統,是為了應付美國政府的一個(gè)編號為FIPS 151-2的標準。
Win32子系統的管理員是一個(gè)叫作csrss.exe的弟兄,它的全名是:Client/Server Run-Time Subsystem。它剛上任時(shí),本來(lái)要分管所有的子系統,但后來(lái)POSIX和OS/2都被分別處理了,所以只管了一個(gè)Win32。即使這樣也很了不起,所有的Win32程序的進(jìn)程、線(xiàn)程們都要向它登記。
不過(guò)Win32程序用得最多的還是Win32子系統的DLL們,最核心的DLL包括:kernel32.dll、User32.dll、Gdi32.dll、Advapi32.dll(高級Win32應用程序接口)。這些DLL包裝了ntdll.dll的native API。其中Gdi32.dll比較特殊,它與核心態(tài)的win32k.sys直接保持聯(lián)系,以提高NT系統的圖形處理能力。Win32子系統的DLL們提供的接口函數在MSDN文檔中被詳細介紹,它們就是Win32 API。
計算機上電后,從BIOS的ROM開(kāi)始運行。BIOS在做一些初始化后會(huì )將硬盤(pán)的第一個(gè)扇區的數據讀入內存,然后將控制權交給它,這段數據被稱(chēng)作Master Boot Record(MBR)。
MBR包含一段啟動(dòng)代碼和硬盤(pán)的主分區表。這段啟動(dòng)代碼掃描主分區表,找到第一個(gè)可以啟動(dòng)的分區,然后將這個(gè)分區的第一個(gè)扇區讀入內存并運行。這個(gè)扇區被稱(chēng)作引導扇區(boot sector)。
引導扇區的代碼具備讀文件系統根目錄的能力,顯然不同的文件系統需要不同的代碼。引導扇區會(huì )從根目錄中讀出一個(gè)叫作ntldr的文件。顧名思義,這個(gè)文件是load NT的主要角色。它的業(yè)績(jì)主要包括將CPU從實(shí)模式轉入保護模式,啟動(dòng)分頁(yè)機制,處理boot.ini等。
如果boot.ini中有一句:
C:\bootsect.rh=”Red Hat Linux”
bootsect.rh的內容是Linux引導扇區,用戶(hù)又選擇了“Red Hat Linux”,ntldr就會(huì )將執行Linux的引導扇區,開(kāi)始Linux的引導。如果用戶(hù)選擇繼續使用Windows,ntldr會(huì )裝載并運行我們前面提到的ntoskrnl.exe。
ntoskrnl.exe會(huì )啟動(dòng)會(huì )話(huà)管理器smss.exe。smss.exe啟動(dòng)csrss.exe和winlogon.exe。smss.exe會(huì )永遠等待csrss.exe和winlogon.exe返回。如果兩者之一異常中止,就會(huì )導致系統崩潰。所以病毒們經(jīng)常以打擊csrss.exe為樂(lè )。
winlogon.exe負責用戶(hù)登錄,在完成登錄后,它會(huì )啟動(dòng)注冊表HKLM\SOFTWARE\Microsoft\Windows NT\Current Version\Winlogon項下Userinit值指定的程序。該值的缺省數據是userinit.exe。userinit.exe會(huì )裝載個(gè)人設置,讓硬盤(pán)響個(gè)不停,并考驗我們的耐性,最后啟動(dòng)注冊表同一項下Shell值指定的程序。該值的缺省數據是Explorer.exe。Explorer.exe運行后,我們就會(huì )看到熟悉的開(kāi)始菜單和桌面。
要了解Win32子系統的DLL們提供了哪些API,最直接的方法就是用Win32dsm直接查看DLL們的導出表。這時(shí)我們會(huì )發(fā)現Win32 API中帶字符串的API一般都有兩個(gè)版本,例如CreateFileA和CreateFileW。當然也有例外,例如GetProcAddress函數。
A代表ANSI代碼頁(yè),W是寬字符,即Unicode字符。Windows中的Unicode字符一般指UCS2的UTF16-LE編碼。讓我們通過(guò)幾個(gè)實(shí)例觀(guān)察A/W版本間的關(guān)系。
例1:用WIn32dsm查看gdi32.dll的匯編代碼,可以看到TextOutA調用GdiGetCodePage獲取當前代碼頁(yè),再調用MultiByteToWideChar轉換輸入的字符串,然后調用一個(gè)內部函數。而TextOutW直接調用這個(gè)內部函數。
例2:用調試器跟蹤一個(gè)使用了CreateFileA的程序,可以看到:CreateFileA在將輸入字符串轉換為Unicode后,會(huì )調用CreateFileW。假設輸入文件名是“測試.txt”,對應的數據就是:“B2 E2 CA D4 2E 74 78 74 00”。
在調試器中可以看到傳給CreateFileW的文件名數據是:“4B 6D D5 8B 2E 00 74 00 78 00 74 00 00 00”。 這是”測試.txt”對應的Unicdoe字符串。CreateFileW會(huì )接著(zhù)調用ntdll.dll中的NtCreateFile。順便看看NtCreateFile的代碼:
mov eax, 00000020
lea edx, dword ptr [esp+04]
int 2E
ret 002C
可見(jiàn)這個(gè)native API只是簡(jiǎn)單地調用了核心態(tài)提供的0×20號system service。
例3:gdi32.dll中的GetGlyphOutline函數可以獲取指定字符的字模。GetGlyphOutlineA和GetGlyphOutlineW函數都會(huì )調用同一個(gè)內部函數(記作F)。函數F在返回前將通過(guò)int 2E調用0×10B1號system service。
GetGlyphOutlineW直接調用函數F。GetGlyphOutlineA在調用函數F前,要依次調用GdiGetCodePage、IsDBCSLeadByteEx和MultiByteToWideChar,將當前代碼頁(yè)的字符編碼轉換成Unicode編碼。
如果我們調用GetGlyphOutlineA時(shí)傳入“baba”,這是“漢”字的GBK編碼,用調試器可以看到傳給函數F的字符編碼是“6c49”,這是“漢”字的Unicode編碼。
從以上例子可見(jiàn),A版本總會(huì )在某處將輸入的字符串轉換為Unicode字符串,然后和W版本執行相同的代碼。在由A/W版本API引出MBCS程序和Unicode程序前,讓我們先解釋一下Locale和ANSI代碼頁(yè)。
Locale是指特定于某個(gè)國家或地區的一組設定,包括字符集,數字、貨幣、時(shí)間和日期的格式等。在Windows中,每個(gè)Locale可以用一個(gè)32位數字表示,記作LCID。在winnt.h中可以看到LCID的組成。它的高16位表示字符的排序方法,一般為0。在它的低16位中,低10位是primary language的ID,高4位指定sublanguage。sublanguage被用來(lái)區分同一種語(yǔ)言的不同編碼。下面是部分primary language和sublanguage的常數定義:
#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)
好,現在我們可以計算簡(jiǎn)體中文的LCID了,將sublanguage的常數左移10位,即乘上1024,再加上primary language的常數:2*1024+4=2052,16進(jìn)制是0804。美國英語(yǔ)是:1*1024+9=1033,16進(jìn)制是0409。。繁體中文是1*1024+4=1028,16進(jìn)制是0404。
每個(gè)Locale都聯(lián)系著(zhù)很多信息,可以通過(guò)GetLocalInfo函數讀取。其中最重要的信息就是字符集了,即Locale對應的語(yǔ)言文字的編碼。Windows將字符集稱(chēng)作代碼頁(yè)。
每個(gè)Locale可以對應一個(gè)ANSI代碼頁(yè)和一個(gè)OEM代碼頁(yè)。Win32 API使用ANSI代碼頁(yè),底層設備使用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中有一張很長(cháng)的表。列出了我正在使用的Windows所支持的135個(gè)Locale的部分信息,包括 LCID、國家/地區名稱(chēng)、語(yǔ)言名稱(chēng)、語(yǔ)言縮寫(xiě)和對應的ANSI代碼頁(yè)。
在Windows中,通過(guò)控制面板可以為系統和用戶(hù)分別設置Locale。系統Locale決定代碼頁(yè),用戶(hù)Locale決定數字、貨幣、時(shí)間和日期的格式。這不是一個(gè)好的設計,后面會(huì )談到它帶來(lái)的問(wèn)題。
使用GetSystemDefaultLCID函數和GetUserDefaultLCID函數分別得到系統和用戶(hù)的LCID。有很多材料將這兩個(gè)函數和另外兩個(gè)函數混淆:GetSystemDefaultUILanguage和GetUserDefaultUILanguage。
GetSystemDefaultUILanguage和GetUserDefaultUILanguage得到的是您當前使用的Windows版本所帶的UI資源的語(yǔ)言。
用戶(hù)程序缺省使用的代碼頁(yè)是當前系統Locale的ANSI代碼頁(yè),可以稱(chēng)作ANSI編碼,也就是A版本的Win32 API默認的字符編碼。對于一個(gè)未指定編碼方式的文本文件,Windows會(huì )按照ANSI編碼解釋。
如果一個(gè)文本文件采用BIG5編碼,系統當前的ANSI代碼頁(yè)是GBK。打開(kāi)這個(gè)文件,就會(huì )顯示亂碼。例如“中文”在BIG5中的編碼是A4A4、A4E5,這兩個(gè)編碼在GBK中對應的字符是“いゅ”。這是日文的兩個(gè)平假名。
在Windows XP平臺有一個(gè)AppLocale程序,可以以指定的語(yǔ)言運行非Unicode程序。用Win32dsm打開(kāi)看一看,其實(shí)它只是在運行程序前設置了兩個(gè)環(huán)境變量。我們可以用個(gè)批處理文件模仿一下:
@ECHO OFF
SET __COMPAT_LAYER=#ApplicationLocale
SET ApplocaleID=0404
start notepad.exe
在簡(jiǎn)體中文平臺,用這個(gè)批處理文件啟動(dòng)的記事本可以正確顯示BIG5編碼的文本文件。用它打開(kāi)GBK編碼的文本文件會(huì )怎么樣?“中文”會(huì )被顯示為“笢恅”。設置這兩個(gè)環(huán)境變量會(huì )作用于當前進(jìn)程和其子進(jìn)程。Windows 2000平臺不支持這個(gè)方法。
讓我們回到Win32 API。我們在程序中使用的Win32 API沒(méi)有A/W后綴,Windows的頭文件會(huì )根據編譯參數UNICODE將沒(méi)有后綴的函數名替換為A版本或W版本,例如:
#ifdef UNICODE
#define CreateFile CreateFileW
#else
#define CreateFile CreateFileA
#endif
C RunTime庫(CRT)使用_UNICODE和_MBCS來(lái)區分三套字符串處理函數,分別用于SBCS、MBCS和Unicdoe字符串。SBCS和MBCS分別指單字節字符串和多字節字符串。例如_tcsclen的3個(gè)版本分別為strlen、_mbslen和wcslen ,猜猜以下函數返回幾?
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字符串轉換為Unicode字符串,這是VC++編譯器提供的一個(gè)小甜點(diǎn)。不過(guò)我們應該用宏:_T(”ANSI字符串”)。_T宏只在我們定義了_UNICODE時(shí)才轉換。這樣同一套代碼既可以編譯MBCS版本,也可以編譯Unicode版本。
MFC用_UNICODE參數區分Unicode版本特有的代碼,決定使用什么版本的導入庫或靜態(tài)庫。
Unicode程序直接使用Unicode版本的CRT和Win32 API。Unicode程序的運行與當前的ANSI代碼頁(yè)沒(méi)有關(guān)系。MBCS程序的運行依賴(lài)于A(yíng)NSI代碼頁(yè)。如果設計者和使用者使用不同的代碼頁(yè),就可能出現亂碼。微軟開(kāi)發(fā)的程序大都是Unicode程序,不管我們怎樣變換系統Locale,它們總能正常運行。
使用VCL類(lèi)庫的Delphi程序都是MBCS程序。VCL框架在程序啟動(dòng)會(huì )調用GetThreadLocale獲取當前用戶(hù)的LCID,然后在當前目錄查找對應的資源文件,命名規則是:程序名+’.‘+語(yǔ)言縮寫(xiě),語(yǔ)言縮寫(xiě)可以參見(jiàn)附錄1。在找不到時(shí)才會(huì )使用EXE文件中的資源。不過(guò)如果系統LCID是English(United States),用戶(hù)LCID是Chinese(PRC),由VCL產(chǎn)生的程序就會(huì )出現亂碼。讀者可以自己分析原因。
為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項目。我在學(xué)習時(shí)將它改成了VC6項目,并糾正了它的兩個(gè)問(wèn)題:
1、用GetUserDefaultUILanguage讀到的是Windows資源版本,不是當前用戶(hù)設置的代碼頁(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í)根據當前Locale裝載對應的資源DLL。必要時(shí)動(dòng)態(tài)切換資源。為了標記不同語(yǔ)言的資源,可以將它們放到不同的目錄中,以L(fǎng)CID作為目錄名,例如“2052”、“1033”。當然我們也可以用其它方法聯(lián)系LCID和資源DLL。
MFC程序可以在A(yíng)pp類(lèi)的InitInstance函數中用AfxSetResourceHandle函數設置資源DLL。在Delphi中動(dòng)態(tài)切換資源可以參考Delphi Demo目錄RichEdit項目的ReInit.pas。在讀取當前設定時(shí),建議用GetSystemDefaultLCID函數,因為系統Locale決定ANSI代碼頁(yè)。
通過(guò)檢查可執行文件,我們可以確定VC和Delphi的資源編譯器都以Unicode保存字符資源。在VC環(huán)境編輯資源時(shí),我們會(huì )指定資源的代碼頁(yè)。編譯器根據資源的代碼頁(yè),將其轉換到Unicode。
Unicode程序直接使用以Unicode編碼保存的資源。MBCS程序需要將Unicode資源先轉換回當前ANSI代碼頁(yè),然后再使用。如果資源中的Unicode字符串不能映射到當前代碼頁(yè)中的字符,就會(huì )出現??。
例如Windows的標準對話(huà)框也會(huì )出現亂碼。假設我們使用簡(jiǎn)體中文Windows,當前Locale是Chinese (TW),我們的程序是MBCS的,使用標準的打開(kāi)文件對話(huà)框。因為在BIG5中沒(méi)有“開(kāi)”這個(gè)字,所以“打開(kāi)”會(huì )被顯示成“打?”。將程序編譯成Unicode版本,就可以避免這個(gè)問(wèn)題。
如果字符不是保存在資源中,而是硬編碼在程序中。然后開(kāi)發(fā)者和用戶(hù)使用不同的代碼頁(yè),就會(huì )導致亂碼。假設開(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程序根據用戶(hù)LCID轉換資源中的字符串。如果用戶(hù)LCID是Chinese (PRC),系統LCID是English (US)。那么資源中的Unicode字符串會(huì )被轉換為GBK編碼,然后按照Latin I顯示,這時(shí)我們看到的就是類(lèi)似“???t”的東東,不是??。
既然資源是以Unicode保存的,MBCS程序如果不將其轉換到ANSI代碼頁(yè),而用W版本的函數直接顯示,就不會(huì )產(chǎn)生亂碼。例如MFC程序菜單里的中文,在English (US)的Locale也可以正常顯示。不過(guò)這取決于各部分代碼的具體實(shí)現,menu bar控件里的中文在English (US)的Locale會(huì )全部顯示成??。
本文的第0節和附錄0主要參考了《Inside Windows 2000 Third Edition》,國內出過(guò)該書(shū)的影印版。DDK文檔中有大量Windows內核的信息。用Win32dsm和各種調試器查看Windows系統文件可以獲得更直接的信息。
關(guān)于Window程序的字符編碼,最好的參考資料是winnt.h等SDK的包含文件、VCL、MFC、CRT的源文件。我們不需要閱讀它們,只要找到自己感興趣的信息就可以了,用Source Insight可能方便一些。
本文所談的不是什么萬(wàn)古不遷的道理,只是別的程序員的一些設定,我們因為需要使用他們的程序,所以有必要了解一些細節。研究問(wèn)題的方法和興趣永遠比問(wèn)題本身重要,如一句拉丁俗語(yǔ)所說(shuō):res, non verba,實(shí)質(zhì)勝于文字。
“明月雖有圓缺,但畢竟永恒不滅,人生卻如過(guò)眼煙云,一去不回,真不知計較為何?”
“蛙聲雖是短促,但卻是萬(wàn)籟中一個(gè)活潑的禪機,也可以說(shuō)萬(wàn)古如斯,永恒不遷,無(wú)奈感受到的,能有幾人?”
這是一本武俠書(shū)中的對話(huà)。在時(shí)間的長(cháng)河中,人生和蛙聲一樣易逝。說(shuō)到蛙聲,我的20個(gè)月的小寶寶在喝湯后,略加醞釀,就會(huì )緊閉著(zhù)嘴巴,發(fā)出很像蛙鳴的聲音。我們會(huì )逗他說(shuō):“小青蛙又來(lái)了”。小家伙益發(fā)得意,不管我的抗議,將連湯帶油的小下巴親熱地貼在我的身上。
使用EnumSystemLocales函數可以枚舉系統支持的LCID。用GetLocaleInfo可以得到ANSI代碼頁(yè)的ID,再通過(guò)GetCPInfoEx可以獲得代碼頁(yè)的全稱(chēng)。以下是我在中文Windows XP上讀到的內容。
| LCID | 國家或地區 | 語(yǔ)言 | 語(yǔ)言縮寫(xiě) | ANSI代碼頁(yè) |
| 1025 | 沙特阿拉伯 | 阿拉伯語(yǔ)(沙特阿拉伯) | ARA | 1256 (ANSI - 阿拉伯文) |
| 1026 | 保加利亞 | 保加利亞語(yǔ) | BGR | 1251 (ANSI - 西里爾文) |
| 1027 | 西班牙 | 加泰隆語(yǔ) | CAT | 1252 (ANSI - 拉丁文 I) |
| 1028 | 臺灣 | 中文(臺灣) | CHT | 950 (ANSI/OEM - 繁體中文 Big5) |
| 1029 | 捷克共和國 | 捷克語(yǔ) | CSY | 1250 (ANSI - 中歐) |
| 1030 | 丹麥 | 丹麥語(yǔ) | DAN | 1252 (ANSI - 拉丁文 I) |
| 1031 | 德國 | 德語(yǔ)(德國) | DEU | 1252 (ANSI - 拉丁文 I) |
| 1032 | 希臘 | 希臘語(yǔ) | ELL | 1253 (ANSI - 希臘文) |
| 1033 | 美國 | 英語(yǔ)(美國) | ENU | 1252 (ANSI - 拉丁文 I) |
| 1034 | 西班牙 | 西班牙語(yǔ)(傳統) | ESP | 1252 (ANSI - 拉丁文 I) |
| 1035 | 芬蘭 | 芬蘭語(yǔ) | FIN | 1252 (ANSI - 拉丁文 I) |
| 1036 | 法國 | 法語(yǔ)(法國) | 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 | 泰國 | 泰語(yǔ) | THA | 874 (ANSI/OEM - 泰文) |
| 1055 | 土耳其 | 土耳其語(yǔ) | TRK | 1254 (ANSI - 土耳其文) |
| 1056 | 巴基斯坦伊斯蘭共和國 | 烏都語(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 | 前南斯拉夫馬其頓共和國 | 馬其頓語(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 | 中華人民共和國 | 中文(中國) | CHS | 936 (ANSI/OEM - 簡(jiǎn)體中文 GBK) |
| 2055 | 瑞士 | 德語(yǔ)(瑞士) | DES | 1252 (ANSI - 拉丁文 I) |
| 2057 | 英國 | 英語(yǔ)(英國) | 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 | 文萊達魯薩蘭 | 馬來(lái)語(yǔ)(文萊達魯薩蘭) | MSB | 1252 (ANSI - 拉丁文 I) |
| 2115 | 烏茲別克斯坦 | 烏茲別克語(yǔ)(西里爾文) | UZB | 1251 (ANSI - 西里爾文) |
| 3073 | 埃及 | 阿拉伯語(yǔ)(埃及) | ARE | 1256 (ANSI - 阿拉伯文) |
| 3076 | 香港特別行政區 | 中文(香港特別行政區) | ZHH | 950 (ANSI/OEM - 繁體中文 Big5) |
| 3079 | 奧地利 | 德語(yǔ)(奧地利) | DEA | 1252 (ANSI - 拉丁文 I) |
| 3081 | 澳大利亞 | 英語(yǔ)(澳大利亞) | ENA | 1252 (ANSI - 拉丁文 I) |
| 3082 | 西班牙 | 西班牙語(yǔ)(國際) | 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)特別行政區 | 中文(澳門(mén)特別行政區) | ZHM | 950 (ANSI/OEM - 繁體中文 Big5) |
| 5127 | 列支敦士登 | 德語(yǔ)(列支敦士登) | DEC | 1252 (ANSI - 拉丁文 I) |
| 5129 | 新西蘭 | 英語(yǔ)(新西蘭) | ENZ | 1252 (ANSI - 拉丁文 I) |
| 5130 | 哥斯達黎加 | 西班牙語(yǔ)(哥斯達黎加) | 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 | 摩納哥公國 | 法語(yǔ)(摩納哥) | FRM | 1252 (ANSI - 拉丁文 I) |
| 7169 | 突尼斯 | 阿拉伯語(yǔ)(突尼斯) | ART | 1256 (ANSI - 阿拉伯文) |
| 7177 | 南非 | 英語(yǔ)(南非) | ENS | 1252 (ANSI - 拉丁文 I) |
| 7178 | 多米尼加共和國 | 西班牙語(yǔ)(多米尼加共和國) | ESD | 1252 (ANSI - 拉丁文 I) |
| 8193 | 阿曼 | 阿拉伯語(yǔ)(阿曼) | ARO | 1256 (ANSI - 阿拉伯文) |
| 8201 | 牙買(mǎi)加 | 英語(yǔ)(牙買(mǎi)加) | ENJ | 1252 (ANSI - 拉丁文 I) |
| 8202 | 委內瑞拉 | 西班牙語(yǔ)(委內瑞拉) | 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 | 特立尼達和多巴哥 | 英語(yǔ)(特立尼達) | 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 | 菲律賓共和國 | 英語(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ǔ)言,在表中列出國家名只是為了增加趣味性。例如可以看到以色列還在使用古老的希伯來(lái)語(yǔ)?!跋2畞?lái)語(yǔ)”的法文是hébreu,這個(gè)單詞還有一個(gè)意思,就是“不能理解的東西”。
在winnt.h中,對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)不能訪(fǎng)問(wèn)核心態(tài)的任何函數和變量,所以system call不同于一般的API調用。system call可以被看作:將要調用的功能ID放到eax,然后執行INT 2e。
ntdll.dll通過(guò)system call使用核心態(tài)的ntoskrnl.exe和win32k.sys提供的功能。ntoskrnl.exe被尊稱(chēng)為“Executive”,可以看作是NT的大腦級模塊。win32k.sys提供NT圖形庫接口的API。
聯(lián)系客服