【问题标题】:Script to detect if Windows System Locale is using UTF-8 code page?检测 Windows 系统区域设置是否使用 UTF-8 代码页的脚本?
【发布时间】:2021-03-04 11:33:17
【问题描述】:

在最新版本的 Win10 上,可以将活动代码页 (ACP) 设置为 UTF-8 代码页。 正如here 所讨论的,可以设置系统区域设置(用于在 Windows API 的“A”版本和“W”版本之间进行映射)以使用 UTF-8 代码页。

脚本如何检测 UTF-8 代码页是否正在使用?

正如herehere 所讨论的,通常可以使用WMI 来获取系统代码页ID:

For Each os In wmi.ExecQuery("SELECT * FROM Win32_OperatingSystem")
    cs = os.CodeSet
Next

当我在 Win10 上尝试使用美式英语中的 'beta' utf-8 支持来支持非 unicode 程序时,WMI 继续报告代码页为 1252。即使显然不是这种情况(代码第 1252 页在 128 处有一个代码点,但在 49800 处没有:UTF-8 在 49800 处有一个代码点,在 128 处没有)。

脚本如何检测到实际系统区域设置正在使用 UTF-8 代码页?

【问题讨论】:

  • @Lankymart 这个问题似乎是在询问如何强制 PowerShell 窗口使用 UTF-8。相反,这个问题似乎是在问如何判断本地系统正在使用什么。
  • @TylerH 很公平,但更重要的是事实标记了 powershell 和 vbscript 的问题。答案是纯粹的 powershell 并已被接受为解决方案。
  • 几乎所有你可以在 powershell 中做的事情,你可以在 vbscript 中做,反之亦然。大多数 powershell 只是调用 COM 对象,大多数异常都可用于 COM shell 或作为可执行文件,而剩下的少数异常中的大多数都有等价物。下面的答案不是 PowerShell 答案:它是通用答案的 PowerShell 示例。

标签: powershell utf-8 vbscript windows-10 wmi


【解决方案1】:

PowerShell(基于 shell)解决方案:

要确定系统区域设置的(系统范围的)OEM 代码页 - 这是控制台应用程序使用的代码页,请使用注册表:

# $true, if the OEM code page is set to UTF-8 (code page 65001)
'65001' -eq (Get-ItemPropertyValue HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage OEMCP)

注意:

  • 使用 system-wide UTF-8 support 还将 ANSI 代码页 (ACP) 设置为 65001旧版 GUI 应用程序使用,但尤其是 Windows PowerShell[1],表示 Windows PowerShell 对 Get-ContentSet-Content cmdlet 的默认编码,例如,发生变化。

  • cmd.exe,您可以运行
    reg.exe query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage /v OEMCP,但您必须解析其文本输出以仅提取代码页号。

  • 请注意,遗憾的是,PowerShell 的 Get-WinSystemLocale cmdlet 在撰写本文时无法使用,因为它返回的 [cultureinfo] 实例确实反映了可能存在的 UTF-8 覆盖 -见this ServerFault answer


确定当前控制台的活动 OEM 代码页 - 这可能反映也可能不反映系统区域设置,因为控制台窗口可以配置为使用自定义代码页,并且代码页甚至可以事先在会话中更改:

# $true, if the OEM code page is set to UTF-8 (code page 65001)
65001 -eq [Console]::OutputEncoding.CodePage

注意:

  • 您可以从 cmd.exe 执行 chcp chcp.com,但随后您必须解析其文本输出以仅提取代码页编号

基于 Windows API 的解决方案

从已编译的应用程序中,您可以使用GetACP()GetOEMCP() Windows API 函数分别查询活动的 ANSI 和 OEM 代码页。

您甚至可以从 PowerShell 执行此操作(尽管它需要按需编译这一事实使得注册表解决方案成为首选):

# Compile a helper type that calls the WinAPI functions.
Add-Type -Namespace Util -Name WinApi -MemberDefinition @'
  [DllImport("Kernel32.dll")]
  public static extern uint GetACP();
  [DllImport("Kernel32.dll")]
  public static extern uint GetOEMCP();
'@

[Util.WinAPI]::GetOEMCP(), [Util.WinAPI]::GetACP()

注意:

  • 如果您编译的应用程序是控制台应用程序,并且您想知道相关控制台的当前 OEM 代码页 - 这可能是也可能不是通过系统语言环境 - 请改用 GetConsoleOutputCP() 函数。

[1] 活动的 ANSI 代码页不再与 PowerShell [Core] v6+ 相关,后者始终将无 BOM 的 UTF-8 用于其 cmdlet,但在 Windows 上,活动的正如[Console]::OutputEncoding 所反映的,OEM 代码页在与外部程序通信时仍然很重要。

【讨论】:

  • 我的原始评论在哪里?不,chcp.com 只是一个程序:如果执行,即通过CreateProcess() 并立即重定向其输出,则根本不应该涉及任何控制台。不过,也可以立即致电GetACP()
  • @AmigoJack:从已编译的应用程序中,您不会产生为chcp.com 创建子进程的开销——实际上您会使用 WinAPI。请注意,问题是关于 scripting(也反映在标签 [powershell] 和 [vbscript] 中),因此可以公平地假设涉及 console,其中,如前所述,chcp 的输出可能反映也可能不反映真实的系统语言环境。但是,考虑到您可能想知道控制台的当前代码页,我已经更新了解决这两个用例的答案,包括编译应用程序的注意事项。
  • 啊,现在我了解我的cmt了。此外,我现在确信您的回答比以前更加详细和完整。不知道 PowerShell 需要对 DLL 导入进行按需编译 - 认为它足够“智能”来使用它。当然,查询 Registry 的性能影响应该是最低的。
  • @AmigoJack:如果 PowerShell 让您 直接 调用 Windows API 函数(考虑到 PowerShell 的跨平台自然,显然将仅限于 Windows),但我认为它作为一个 shell 允许近乎无限的直接访问 已经足够了.NET API(在所有支持的平台上)。
猜你喜欢
  • 1970-01-01
  • 2011-01-28
  • 2021-12-31
  • 2012-04-26
  • 2019-12-14
  • 1970-01-01
  • 2011-06-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多