Win10 1903引入了将系统和用户程序的代码页设置为UTF-8的方法
1903开始可以在intl.cpl的“管理”选项卡的“非Unicode程序的语言”设置系统代码页为UTF-8(使用ACP=OEMCP=65001),同时也支持了将Unicode-only locale设为“非Unicode程序的语言”(这些Unicode-only locale始终使用UTF-8的系统代码页)
1903开始支持了<activeCodePage>的manifest标签,1903开始支持UTF-8这一个值,可以单独将程序代码页设为UTF-8,WinServer2022/Win11开始支持了Legacy和ja-JP等设置为当前“非Unicode程序的语言”对应遗留代码页或特定locale的默认代码页(Legacy:如果是Unicode-only locale,则ACP=1251、OEMCP=437;xx-YY:如果是Unicode-only locale,则ACP=OEMCP=65001)
不过这些仅适用于kernel32之类的核心API,不适用于user32/gdi32之类的GUI API,会各种乱码,如果使用了这些,user32/gdi32之类的GUI API建议使用W系列的基于wchar_t和Unicode (UTF-16LE)的API
具体情况可以参考
- The activeCodePage manifest element can be used for more than just setting UTF-8 as the active code page - The Old New Thing (microsoft.com)
那么如何获取代码页更靠谱呢?
- GetACP、GetOEMCP:只能获取当前程序的ACP、OEMCP
- 读取注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage的{A|OEM}CP字符串值:
- 可以准确获得系统环境当前实际使用的ACP、OEMCP
- LOCALE[_NAME]_SYSTEM_LOCALE的LOCALE_IDEFAULT[ANSI]CODEPAGE:
- 使用GetLocaleInfo{Ex|W|A}获取,可以|LOCALE_RETURN_NUMBER直接获取UINT
- 获取的是当前非Unicode程序的语言的遗留ACP、OEMCP代码页
- 如果该非Unicode程序的语言是Unicode-only,会返回ACP=0、OEMCP=1(如果在转换函数中使用这一组值,则相当于使用当前ACP、OEMCP,即UTF-8,但0和1并非所有函数都接受)
- LOCALE[_NAME]_SYSTEM_LOCALE的LOCALE_IUSEUTF8LEGACY{A|OEM}CP=0x{666|999}:
- 使用GetLocaleInfo{Ex|W|A}获取,可以|LOCALE_RETURN_NUMBER直接获取UINT
- 获取的是当前非Unicode程序的语言的默认ACP、OEMCP代码页
- 如果该非Unicode程序的语言是Unicode-only,会返回ACP=OEMCP=65001
- 1903才支持LOCALE_IUSEUTF8LEGACY{A|OEM}CP=0x{666|999}这两个常量
综上所述:
- 要获取当前程序的ACP、OEMCP,可以直接使用GetACP、GetOEMCP
- 要获取系统环境的ACP、OEMCP,可以读取注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage的{A|OEM}CP字符串值
- 要始终返回Legacy的ACP、OEMCP,可以使用上面的方法
如果是Unicode-only locale,有以下几种选择:
- 使用UTF-8,ACP=OEMCP=65001
- fallback到一组合理的遗留代码页,如ACP=1252、OEMCP=437
- 禁用依赖遗留代码页的相关功能