weitom1982

向各位技术前辈学习,学习再学习.
posts - 299, comments - 79, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
 

 

 

作者: 寒潭惊鹤影   提交日期: 2005-6-11 07:59:00

 

 写这篇文章的起因是这么一个问题:我们在使用和安装 Windows 程序时,有时会看到以 “2052” “1033” 这些数字为名的文件夹,这些数字似乎和字符集有关,但它们究竟是什么意思呢?
  
  研究这个问题的同时,又会遇到其它问题。我们会谈到 Windows 的内部架构、 Win32 API A/W 函数、 Locale ANSI 代码页、与字符编码有关的编译参数、 MBCS Unicode 程序、资源和乱码等,一起经历这段琐碎细节为主,间或乐趣点缀的旅程。
  
   0 Where is Win32 API
   Windows 程序有用户态和核心态的说法。在 32 位地址空间中, 0x80000000 以下属于用户态, 0x80000000 以上属于核心态。所有硬件管理都在核心态。用户态程序的不能直接使用核心态的任何代码。所谓核心态其实只是 CPU 的一种保护模式。在 x86 CPU 上,用户态处于 ring 0 ,核心态处于 ring 3
  
  从用户态进入核心态的最常用的方法是在寄存器 eax 填一个功能码,然后执行 int 2e 。这有点像 DOS 时代的 DOS BIOS 系统调用。在 NT 架构中这种机制被称作 system service
  
  在核心态提供 system service 的有两个家伙: ntoskrnl.exe win32k.sys ntoskrnl.exe Windows 的大脑,它的上层被称为 Executive ,下层被称作 Kernel Win32k.sys 提供与显示有关的 system service
  
  在用户态一侧,有一个重要的角色叫作 ntdll.dll ,大多数 system service 都是它调用的。它封装这些 system service ,然后提供一个 API 接口。这个接口被称作 native API native API 的用户是各个子系统( subsystem ),包括 Win32 子系统、 OS/2 子系统、 POSIX 子系统。各个子系统为 Win32 OS2 POSIX 程序提供了运行平台。
  
   ntdll.dll 由于提供了平台无关的 API 接口,所以被看作是 NT 系统的原生接口,由之得到了 “native API” 的匪号。其实它的主要工作是将调用传递到核心态。
  
   Win32 OS/2 POSIX ,听起来很庞大。其实真正做好的只有 Win32 子系统。 OS2 POSIX 都是 Console UI ,即只有字符界面。提供 OS/2 子系统,只因为在 1988 年, NT 的主要设计目标就是与 OS/2 兼容,后来由于 Windows 3.0 卖得很好,所以设计目标被变更为与 Windows 兼容。提供 POSIX 子系统,是为了应付美国政府的一个编号为 FIPS 151-2 的标准。
  
   Win32 子系统的管理员是一个叫作 csrss.exe 的弟兄,它的全名是: Client/Server Run-Time Subsystem 。它刚上任时,本来要分管所有的子系统,但后来 POSIX OS/2 都被分别处理了,所以只管了一个 Win32 。即使这样也很了不起,所有的 Win32 程序的进程、线程们都要向它登记。
  
  不过 Win32 程序用得最多的还是 Win32 子系统的 DLL 们,最核心的 DLL 包括: kernel32.dll User32.dll Gdi32.dll Advapi32.dll 。这些 DLL 包装了 ntdll.dll native API 。其中 Gdi32.dll 比较特殊,它与核心态的 win32k.sys 直接保持联系,以提高 NT 系统的图形处理能力。 Win32 子系统的 DLL 们提供的接口函数在 MSDN 文档中被详细介绍,它们就是 Win32 API
  

 

 

作者: 寒潭惊鹤影  回复日期: 2005-6-11   07:59:53  

 

  附录 0 Windows 的启动
  计算机上电后,从 BIOS ROM 开始运行。 BIOS 在做一些初始化后会将硬盘的第一个扇区的数据读入内存,然后将控制权交给它,这段数据被称作 Master Boot Record MBR )。
  
   MBR 包含一段启动代码和硬盘的主分区表。这段启动代码扫描主分区表,找到第一个可以启动的分区,然后将这个分区的第一个扇区读入内存并运行。这个扇区被称作引导扇区( boot sector )。
  
  引导扇区的代码具备读文件系统根目录的能力,显然不同的文件系统需要不同的代码。引导扇区会从根目录中读出一个叫作 ntldr 的文件。顾名思义,这个文件是 load NT 的主要角色。它的业绩主要包括将 CPU 从实模式转入保护模式,启动分页机制,处理 boot.ini 等。
  
  如果 boot.ini 中有一句:
  
   C:\bootsect.rh= Red Hat Linux
  
   bootsect.rh 的内容是 Linux 引导扇区,用户又选择了 “Red Hat Linux” ntldr 就会将执行 Linux 的引导扇区,开始 Linux 的引导。如果用户选择继续使用 Windows ntldr 会装载并运行我们前面提到的 ntoskrnl.exe
  
   ntoskrnl.exe 会启动会话管理器 smss.exe smss.exe 启动 csrss.exe winlogon.exe smss.exe 会永远等待 csrss.exe winlogon.exe 返回。如果两者之一异常中止,就会导致系统崩溃。所以病毒们经常以打击 csrss.exe 为乐。
  
   winlogon.exe 负责用户登录,在完成登录后,它会启动注册表 HKLM\SOFTWARE\Microsoft\Windows NT\Current Version\Winlogon 项下 Userinit 值指定的程序。该值的缺省数据是 userinit.exe userinit.exe 会装载个人设置,让硬盘响个不停,并考验我们的耐性,最后启动注册表同一项下 Shell 值指定的程序。该值的缺省数据是 Explorer.exe Explorer.exe 运行后,我们就会看到熟悉的开始菜单和桌面。

 

 

作者: 寒潭惊鹤影  回复日期: 2005-6-11   08:01:14  

 

   1 Win32 API A/W 函数
  要了解 Win32 子系统的 DLL 们提供了哪些 API ,最直接的方法就是用 Win32dsm 直接查看 DLL 们的导出表。这时我们会发现 Win32 API 中带字符串的 API 一般都有两个版本,例如 CreateFileA CreateFileW 。当然也有例外,例如 GetProcAddress 函数。
  
   A 代表 ANSI 代码页, W 是宽字符,即 Unicode 字符。 Windows 中的 Unicode 字符一般指 UCS2 UTF16-LE 编码。让我们通过几个实例观察 A/W 版本间的关系。
  
  例 1 :用 WIn32dsm 查看 gdi32.dll 的汇编代码,可以看到 TextOutA 调用 GdiGetCodePage 获取当前代码页,再调用 MultiByteToWideChar 转换输入的字符串,然后调用一个内部函数。而 TextOutW 直接调用这个内部函数。
  
  例 2 :用调试器跟踪一个使用了 CreateFileA 的程序,可以看到: CreateFileA 在将输入字符串转换为 Unicode 后,会调用 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 会接着调用 ntdll.dll 中的 NtCreateFile 。顺便看看 NtCreateFile 的代码:
   mov eax, 00000020
   lea edx, dword ptr [esp+04]
   int 2E
   ret 002C
  可见这个 native API 只是简单地调用了核心态提供的 0x20 system service
  
  例 3 gdi32.dll 中的 GetGlyphOutline 函数可以获取指定字符的字模。 GetGlyphOutlineA GetGlyphOutlineW 函数都会调用同一个内部函数(记作 F )。函数 F 在返回前将通过 int 2E 调用 0x10B1 system service
   GetGlyphOutlineW 直接调用函数 F GetGlyphOutlineA 在调用函数 F 前,要依次调用 GdiGetCodePage IsDBCSLeadByteEx MultiByteToWideChar ,将当前代码页的字符编码转换成 Unicode 编码。
  如果我们调用 GetGlyphOutlineA 时传入 “baba” ,这是 字的 GBK 编码,用调试器可以看到传给函数 F 的字符编码是 “6c49” ,这是 字的 Unicode 编码。
  
  从以上例子可见, A 版本总会在某处将输入的字符串转换为 Unicode 字符串,然后和 W 版本执行相同的代码。在由 A/W 版本 API 引出 MBCS 程序和 Unicode 程序前,让我们先解释一下 Locale ANSI 代码页。

 

 

作者: 寒潭惊鹤影  回复日期: 2005-6-11   08:02:13  

 

   2 Locale ANSI 代码页
   2.1 Locale LCID
   Locale 是指特定于某个国家或地区的一组设定,包括字符集,数字、货币、时间和日期的格式等。在 Windows 中,每个 Locale 可以用一个 32 位数字表示,记作 LCID 。在 winnt.h 中可以看到 LCID 的组成。它的高 16 位表示字符的排序方法,一般为 0 。在它的低 16 位中,低 10 位是 primary language ID ,高 4 位指定 sublanguage sublanguage 被用来区分同一种语言的不同编码。下面是部分 primary language sublanguage 的常数定义:
  
   #define LANG_CHINESE 0x04
   #define LANG_ENGLISH 0x09
   #define LANG_FRENCH 0x0c
   #define LANG_GERMAN 0x07
  
   #define SUBLANG_CHINESE_TRADITIONAL 0x01 // Chinese (Taiwan Region)
   #define SUBLANG_CHINESE_SIMPLIFIED 0x02 // Chinese (PR China)
   #define SUBLANG_ENGLISH_US 0x01 // English (USA)
   #define SUBLANG_ENGLISH_UK 0x02 // English (UK)
  
  好,现在我们可以计算简体中文的 LCID 了,将 sublanguage 的常数左移 10 位,即乘上 1024 ,再加上 primary language 的常数: 2*1024+4=2052 16 进制是 0804 。美国英语是: 1*1024+9=1033 16 进制是 0409 。。繁体中文是 1*1024+4=1028 16 进制是 0404
  
   2.2 代码页
  每个 Locale 都联系着很多信息,可以通过 GetLocalInfo 函数读取。其中最重要的信息就是字符集了,即 Locale 对应的语言文字的编码。 Windows 将字符集称作代码页。
  
  每个 Locale 可以对应一个 ANSI 代码页和一个 OEM 代码页。 Win32 API 使用 ANSI 代码页,底层设备使用 OEM 代码页,两者可以相互映射。
  
  例如 English (US) ANSI OEM 代码页分别为 “1252 (ANSI - Latin I)” “437 (OEM - United States)” Chinese (PRC) ANSI OEM 代码页都是 “950 (ANSI/OEM - Traditional Chinese Big5)” Chinese (TW) ANSI OEM 代码页都是 “936 (ANSI/OEM - Simplified Chinese GBK)”
  
  附录 1 中有一张很长的表。列出了我正在使用的 Windows 所支持的 135 Locale 的部分信息,包括 LCID 、国家 / 地区名称、语言名称、语言缩写和对应的 ANSI 代码页。
  
   2.3 系统 Locale 、用户 Locale ,再谈 ANSI 代码页
  在 Windows 中,通过控制面板可以为系统和用户分别设置 Locale 。系统 Locale 决定代码页,用户 Locale 决定数字、货币、时间和日期的格式。这不是一个好的设计,后面会谈到它带来的问题。
  
  使用 GetSystemDefaultLCID 函数和 GetUserDefaultLCID 函数分别得到系统和用户的 LCID 。有很多材料将这两个函数和另外两个函数混淆: GetSystemDefaultUILanguage GetUserDefaultUILanguage
  
   GetSystemDefaultUILanguage GetUserDefaultUILanguage 得到的是您当前使用的 Windows 版本所带的 UI 资源的语言。
  
  用户程序缺省使用的代码页是当前系统 Locale ANSI 代码页,可以称作 ANSI 编码,也就是 A 版本的 Win32 API 默认的字符编码。对于一个未指定编码方式的文本文件, Windows 会按照 ANSI 编码解释。
  
   2.4 AppLocale
  如果一个文本文件采用 BIG5 编码,系统当前的 ANSI 代码页是 GBK 。打开这个文件,就会显示乱码。例如 中文 BIG5 中的编码是 A4A4 A4E5 ,这两个编码在 GBK 中对应的字符是 いゅ 。这是日文的两个平假名。
  
  在 Windows XP 平台有一个 AppLocale 程序,可以以指定的语言运行非 Unicode 程序。用 Win32dsm 打开看一看,其实它只是在运行程序前设置了两个环境变量。我们可以用个批处理文件模仿一下:
  
   @ECHO OFF
   SET __COMPAT_LAYER=#ApplicationLocale
   SET ApplocaleID=0404
   start notepad.exe
  
  在简体中文平台,用这个批处理文件启动的记事本可以正确显示 BIG5 编码的文本文件。用它打开 GBK 编码的文本文件会怎么样? 中文 会被显示为 笢恅 。设置这两个环境变量会作用于当前进程和其子进程。 Windows 2000 平台不支持这个方法。

 

 

作者: 寒潭惊鹤影  回复日期: 2005-6-11   08:03:08  

 

   3 MBCS 程序和 Unicode 程序
   3.1 与字符编码有关的编译参数
  让我们回到 Win32 API 。我们在程序中使用的 Win32 API 没有 A/W 后缀, Windows 的头文件会根据编译参数 UNICODE 将没有后缀的函数名替换为 A 版本或 W 版本,例如:
  
   #ifdef UNICODE
   #define CreateFile CreateFileW
   #else
   #define CreateFile CreateFileA
   #endif
  
   C RunTime 库( CRT )也使用 _UNICODE _MBCS 来区分三套字符串处理函数,分别用于 SBCS MBCS Unicdoe 字符串。 SBCS MBCS 分别指单字节字符串和多字节字符串。例如 _tcsclen 3 个版本分别为 strlen _mbslen wcslen ,猜猜以下函数返回几?
  
   strlen( VOIP 网关" )
   _mbslen((unsigned char *) VOIP 网关" )
   wcslen(L VOIP 网关" )
  
  答案是 8 6 6 L ANSI 字符串"通知编译器将 ANSI 字符串转换为 Unicode 字符串,这是 VC++ 编译器提供的一个小甜点。不过我们应该用宏: _T( ANSI 字符串" ) _T 宏只在我们定义了 _UNICODE 时才转换。这样同一套代码既可以编译 MBCS 版本,也可以编译 Unicode 版本。
  
   MFC _UNICODE 参数区分 Unicode 版本特有的代码,决定使用什么版本的导入库或静态库。
  
   3.2 Unicode 程序、 MBCS 程序和多语言支持
   Unicode 程序直接使用 Unicode 版本的 CRT Win32 API Unicode 程序的运行与当前的 ANSI 代码页没有关系。 MBCS 程序的运行依赖于 ANSI 代码页。如果设计者和使用者使用不同的代码页,就可能出现乱码。微软开发的程序大都是 Unicode 程序,不管我们怎样变换系统 Locale ,它们总能正常运行。
  
  使用 VCL 类库的 Delphi 程序都是 MBCS 程序。 VCL 框架在程序启动会调用 GetThreadLocale 获取当前用户的 LCID ,然后在当前目录查找对应的资源文件,命名规则是:程序名 +''.''+ 语言缩写,语言缩写可以参见附录 1 。在找不到时才会使用 EXE 文件中的资源。不过如果系统 LCID English(United States) ,用户 LCID Chinese(PRC) ,由 VCL 产生的程序就会出现乱码。读者可以自己分析原因。
  
  为 VCL 程序做多语言版本。只要用 Delphi 自带的 Resource DLL Wizard 再做一个特定语言的资源 DLL ,原来的程序都不用改。不过很多程序员用其它组件做多语言版本,例如 TsiLang
  
   MBCS 程序虽然也可以做成多语言版本,但它无法在同时显示不同代码页特有的字符,这时就必须使用 Unicode 程序了。
  
   VS.NET 文档中有个多语言资源的例子: SatDLL 。它只用 Win32 API 的例子,却用了 VC7 项目。我在学习时将它改成了 VC6 项目,并纠正了它的两个问题:
   1 、用 GetUserDefaultUILanguage 读到的是 Windows 资源版本,不是当前用户设置的代码页。
   2 、没有使用资源 DLL 里的初始菜单。
  
  在我的个人主页 (http://fmddlmyy.home4u.china.com) 上可以下载修改过的 SatDLL 。这个程序说明了支持多语言资源的基本思路:将不同语言资源放到不同的 DLL 中,在程序启动时根据当前 Locale 装载对应的资源 DLL 。必要时动态切换资源。为了标记不同语言的资源,可以将它们放到不同的目录中,以 LCID 作为目录名,例如 “2052” “1033”
  
   MFC 程序可以在 App 类的 InitInstance 函数中用 AfxSetResourceHandle 函数设置资源 DLL 。在 Delphi 中动态切换资源可以参考 Delphi Demo 目录 RichEdit 项目的 ReInit.pas 。在读取当前设定时,建议用 GetSystemDefaultLCID 函数,因为系统 Locale 决定 ANSI 代码页。
  
   3.4 资源和乱码
  通过检查可执行文件,我们可以确定 VC Delphi 的资源编译器都以 Unicode 保存字符资源。在 VC 环境编辑资源时,我们会指定资源的代码页。编译器根据资源的代码页,将其转换到 Unicode
  
   Unicode 程序直接使用以 Unicode 编码保存的资源。 MBCS 程序需要将 Unicode 资源先转换回当前 ANSI 代码页,然后再使用。如果资源中的 Unicode 字符串不能映射到当前代码页中的字符,就会出现 ??
  
  例如 Windows 的标准对话框也会出现乱码。假设我们使用简体中文 Windows ,当前 Locale Chinese (TW) ,我们的程序是 MBCS 的,使用标准的打开文件对话框。因为在 BIG5 中没有 这个字,所以 打开 会被显示成 ?” 。将程序编译成 Unicode 版本,就可以避免这个问题。
  
  如果字符不是保存在资源中,而是硬编码在程序中。然后开发者和用户使用不同的代码页,就会导致乱码。假设开发者的 Locale Chinese (PRC) ,用户的 Locale English (US) ,程序中硬编码了字符串 文件 Chinese (PRC) ANSI 代码页是 GBK 文件 的编码 “CE C4 BC FE”
   English (US) ANSI 代码页是 Latin I ,用户按照 Latin I 编码去解释 “CE C4 BC FE” ,就会看到 “???t”
  
  回答我前面提过的一个问题: Delphi 程序根据用户 LCID 转换资源中的字符串。如果用户 LCID Chinese (PRC) ,系统 LCID English (US) 。那么资源中的 Unicode 字符串会被转换为 GBK 编码,然后按照 Latin I 显示,这时我们看到的就是类似 “???t” 的东东,不是 ??
  
  既然资源是以 Unicode 保存的, MBCS 程序如果不将其转换到 ANSI 代码页,而用 W 版本的函数直接显示,就不会产生乱码。例如 MFC 程序菜单里的中文,在 English (US) Locale 也可以正常显示。不过这取决于各部分代码的具体实现, menu bar 控件里的中文在 English (US) Locale 会全部显示成 ??

 

 

作者: 寒潭惊鹤影  回复日期: 2005-6-11   08:04:33  

 

  进一步的参考资料
  本文的第 0 节和附录 0 主要参考了《 Inside Windows 2000 Third Edition 》,国内出过该书的影印版。 DDK 文档中有大量 Windows 内核的信息。用 Win32dsm 和各种调试器查看 Windows 系统文件可以获得更直接的信息。
  
  关于 Window 程序的字符编码,最好的参考资料是 winnt.h SDK 的包含文件、 VCL MFC CRT 的源文件。我们不需要阅读它们,只要找到自己感兴趣的信息就可以了,用 Source Insight 可能方便一些。
  
  本文所谈的不是什么万古不迁的道理,只是别的程序员的一些设定,我们因为需要使用他们的程序,所以有必要了解一些细节。研究问题的方法和兴趣永远比问题本身重要,如一句拉丁俗语所说: res, non verba ,实质胜于文字。
  
  尾声
   明月虽有圆缺,但毕竟永恒不灭,人生却如过眼烟云,一去不回,真不知计较为何?
  
   蛙声虽是短促,但却是万籁中一个活泼的禅机,也可以说万古如斯,永恒不迁,无奈感受到的,能有几人?
  
  这是一本武侠书中的对话。在时间的长河中,人生和蛙声一样易逝。说到蛙声,我的 20 个月的小宝宝在喝汤后,略加酝酿,就会紧闭着嘴巴,发出很像蛙鸣的声音。我们会逗他说: 小青蛙又来了 。小家伙益发得意,不管我的抗议,将连汤带油的小下巴亲热地贴在我的身上。
  

 

 

作者: 寒潭惊鹤影  回复日期: 2005-6-11   08:08:11  

 

  附录 1 一些关于 LCID 的信息
  使用 EnumSystemLocales 函数可以枚举系统支持的 LCID 。用 GetLocaleInfo 可以得到 ANSI 代码页的 ID ,再通过 GetCPInfoEx 可以获得代码页的全称。以下是我在中文 Windows XP 上读到的内容。
  
   LCID 国家或地区 语言 语言缩写 ANSI 代码页
   1025 沙特阿拉伯 阿拉伯语 ( 沙特阿拉伯 ) ARA 1256 (ANSI - 阿拉伯文 )
   1026 保加利亚 保加利亚语 BGR 1251 (ANSI - 西里尔文 )
   1027 西班牙 加泰隆语 CAT 1252 (ANSI - 拉丁文 I)
   1028 台湾 中文 ( 台湾 ) CHT 950 (ANSI/OEM - 繁体中文 Big5)
   1029 捷克共和国 捷克语 CSY 1250 (ANSI - 中欧 )
   1030 丹麦 丹麦语 DAN 1252 (ANSI - 拉丁文 I)
   1031 德国 德语 ( 德国 ) DEU 1252 (ANSI - 拉丁文 I)
   1032 希腊 希腊语 ELL 1253 (ANSI - 希腊文 )
   1033 美国 英语 ( 美国 ) ENU 1252 (ANSI - 拉丁文 I)
   1034 西班牙 西班牙语 ( 传统 ) ESP 1252 (ANSI - 拉丁文 I)
   1035 芬兰 芬兰语 FIN 1252 (ANSI - 拉丁文 I)
   1036 法国 法语 ( 法国 ) FRA 1252 (ANSI - 拉丁文 I)
   1037 以色列 希伯来语 HEB 1255 (ANSI - 希伯来文 )
   1038 匈牙利 匈牙利语 HUN 1250 (ANSI - 中欧 )
   1039 冰岛 冰岛语 ISL 1252 (ANSI - 拉丁文 I)
   1040 意大利 意大利语 ( 意大利 ) ITA 1252 (ANSI - 拉丁文 I)
   1041 日本 日语 JPN 932 (ANSI/OEM - 日文 Shift-JIS)
   1042 朝鲜 朝鲜语 KOR 949 (ANSI/OEM - 韩文 )
   1043 荷兰 荷兰语 ( 荷兰 ) NLD 1252 (ANSI - 拉丁文 I)
   1044 挪威 挪威语 ( 伯克梅尔 ) NOR 1252 (ANSI - 拉丁文 I)
   1045 波兰 波兰语 PLK 1250 (ANSI - 中欧 )
   1046 巴西 葡萄牙语 ( 巴西 ) PTB 1252 (ANSI - 拉丁文 I)
   1048 罗马尼亚 罗马尼亚语 ROM 1250 (ANSI - 中欧 )
   1049 俄罗斯 俄语 RUS 1251 (ANSI - 西里尔文 )
   1050 克罗地亚 克罗地亚语 HRV 1250 (ANSI - 中欧 )
   1051 斯洛伐克语 斯洛伐克语 SKY 1250 (ANSI - 中欧 )
   1052 阿尔巴尼亚 阿尔巴尼亚语 SQI 1250 (ANSI - 中欧 )
   1053 瑞典 瑞典语 SVE 1252 (ANSI - 拉丁文 I)
   1054 泰国 泰语 THA 874 (ANSI/OEM - 泰文 )
   1055 土耳其 土耳其语 TRK 1254 (ANSI - 土耳其文 )
   1056 巴基斯坦伊斯兰共和国 乌都语 URD 1256 (ANSI - 阿拉伯文 )
   1057 印度尼西亚 印度尼西亚语 IND 1252 (ANSI - 拉丁文 I)
   1058 乌克兰 乌克兰语 UKR 1251 (ANSI - 西里尔文 )
   1059 比利时 比利时语 BEL 1251 (ANSI - 西里尔文 )
   1060 斯洛文尼亚 斯洛文尼亚语 SLV 1250 (ANSI - 中欧 )
   1061 爱沙尼亚 爱沙尼亚语 ETI 1257 (ANSI - 波罗的海文 )
   1062 拉脱维亚 拉脱维亚语 LVI 1257 (ANSI - 波罗的海文 )
   1063 立陶宛 立陶宛语 LTH 1257 (ANSI - 波罗的海文 )
   1065 伊朗 法斯语 FAR 1256 (ANSI - 阿拉伯文 )
   1066 越南 越南语 VIT 1258 (ANSI/OEM - 越南 )
   1067 亚美尼亚 亚美尼亚语 HYE 936 (ANSI/OEM - 简体中文 GBK)
   1068 阿塞拜疆 阿塞拜疆语 ( 拉丁文 ) AZE 1254 (ANSI - 土耳其文 )
   1069 西班牙 巴士克语 EUQ 1252 (ANSI - 拉丁文 I)
   1071 前南斯拉夫马其顿共和国 马其顿语 (FYROM) MKI 1251 (ANSI - 西里尔文 )
   1078 南非 南非语 AFK 1252 (ANSI - 拉丁文 I)
   1079 格鲁吉亚 格鲁吉亚语 KAT 936 (ANSI/OEM - 简体中文 GBK)
   1080 法罗群岛 法罗语 FOS 1252 (ANSI - 拉丁文 I)
   1081 印度 印地语 HIN 936 (ANSI/OEM - 简体中文 GBK)
   1086 马来西亚 马来语 ( 马来西亚 ) MSL 1252 (ANSI - 拉丁文 I)
   1087 吉尔吉斯坦 哈萨克语 KKZ 1251 (ANSI - 西里尔文 )
   1088 吉尔吉斯斯坦 吉尔吉斯语 ( 西里尔文 ) KYR 1251 (ANSI - 西里尔文 )
   1089 肯尼亚 斯瓦希里语 SWK 1252 (ANSI - 拉丁文 I)
   1091 乌兹别克斯坦 乌兹别克语 ( 拉丁文 ) UZB 1254 (ANSI - 土耳其文 )
   1092 鞑靼斯坦 鞑靼语 TTT 1251 (ANSI - 西里尔文 )
   1094 印度 旁遮普语 PAN 936 (ANSI/OEM - 简体中文 GBK)
   1095 印度 古吉拉特语 GUJ 936 (ANSI/OEM - 简体中文 GBK)
   1097 印度 泰米尔语 TAM 936 (ANSI/OEM - 简体中文 GBK)
   1098 印度 泰卢固语 TEL 936 (ANSI/OEM - 简体中文 GBK)
   1099 印度 卡纳拉语 KAN 936 (ANSI/OEM - 简体中文 GBK)
   1102 印度 马拉地语 MAR 936 (ANSI/OEM - 简体中文 GBK)
   1103 印度 梵文 SAN 936 (ANSI/OEM - 简体中文 GBK)
   1104 蒙古 蒙古语 ( 西里尔文 ) MON 1251 (ANSI - 西里尔文 )
   1110 西班牙 加里西亚语 GLC 1252 (ANSI - 拉丁文 I)
   1111 印度 孔卡尼语 KNK 936 (ANSI/OEM - 简体中文 GBK)
   1114 叙利亚 叙利亚语 SYR 936 (ANSI/OEM - 简体中文 GBK)
   1125 马尔代夫 第维埃语 DIV 936 (ANSI/OEM - 简体中文 GBK)
   2049 伊拉克 阿拉伯语 ( 伊拉克 ) ARI 1256 (ANSI - 阿拉伯文 )
   2052 中华人民共和国 中文 ( 中国 ) CHS 936 (ANSI/OEM - 简体中文 GBK)

 

 

作者: 寒潭惊鹤影  回复日期: 2005-6-11   08:12:08  

 

  第 0 节第一段有一处笔误,应该是 用户态处于 ring 3 ,核心态处于 ring 0”
  

 

 

作者: 寒潭惊鹤影  回复日期: 2005-6-11   08:23:41  

 

  还有 57 LCID ,不知道为什么发不起来。拆开也不行。有兴趣的朋友可以在我的主页( http://fmddlmyy.home4u.china.com/mytext.html )或者 blog http://blog.csdn.net/fmddlmyy/archive/2005/06/09/390732.aspx )上找到完整的 LCID 表。
  

 


  简体中文的 codepage 936
  
  另外,如果是 win9x 的话 xxxxxxw 是通过调用 xxxxxa 的,内部处理是 ansi 的,和 win2k 刚好相反

 

Feedback

# re: 『IT视界』 [大话IT]谈谈Windows程序中的字符编码(转)  回复  更多评论   

2009-07-16 11:52 by 星空
  文末给出的两个链接都失效了。
只有注册用户登录后才能发表评论。