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 星空
  文末给出的两个链接都失效了。
只有注册用户登录后才能发表评论。