【问题标题】:c# change user culture, equivalent of Set-Culture from powershellc#更改用户文化,相当于powershell中的Set-Culture
【发布时间】:2020-06-26 17:06:17
【问题描述】:

我需要在系统级别而不是线程级别运行一段代码(c# 或 powershell)来设置 USER 文化。即,所述代码将运行,当我再次打开控制面板时,我会看到格式设置更改为该设置。我看过很多关于 CultureInfo 的对话,但它们都是针对那个线程的,我需要 USER 级别。使用 .NET 4.6.1 和 Windows 7。powershell 中的 cmdlet 仅适用于 Windows8 和 10。

我在 CultureInfo 中尝试了很多方法,如在 .net4.6 及更高版本中,它是读/写的。但是,它似乎只适用于当前线程或线程。但执行此操作后(见下文),我进入控制面板并没有看到我的更改。 powershell cmdlet 会为您执行此操作。

CultureInfo.CurrentCulture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US");
CultureInfo.CurrentUICulture = new CultureInfo("en-US");

似乎没有一个简单的 powershell cmdlet 可以做到。

# works for win8 and win10 only
set-culture en-US

我希望有一种方法可以更改 USER 的当前文化,然后通过以下自动化任务打开的任何应用程序都将使用该设置执行。提前致谢!

【问题讨论】:

  • .NET 代码的好处是它很容易用一个体面的反汇编程序进行反编译。不好的是,您现在会发现这样做需要什么。最好去this route。请注意,该 cmdlet 似乎已停止使用,我看到了一条关于它存在错误的说明。
  • C# Changing sytem locale的可能重复

标签: c# .net


【解决方案1】:

这是因为,正如文档明确指出的那样,通过 CultureInfo 设置当前文化是特定于线程的。

Powershell 的 set-culture 命令通过调用非托管 Win32 函数来完成一些完全不同的操作,正如您从 cmdlet 的反汇编中看到的那样:

[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")]
    protected override void ProcessRecord()
    {
        if (LPAPIWrapper.NlsUpdateLocale(cultureinfo.Name, NLS_LOCALE_SELECT | NLS_LOCALE_CLEAR_USER_DATA) == 0)
        {
            NumberFormatInfo numberFormat = cultureinfo.NumberFormat;
            DateTimeFormatInfo dateTimeFormat = cultureinfo.DateTimeFormat;
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ICURRDIGITS, numberFormat.CurrencyDecimalDigits.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SMONDECIMALSEP, numberFormat.CurrencyDecimalSeparator.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SMONTHOUSANDSEP, numberFormat.CurrencyGroupSeparator.ToString());
            SetGroupSizes(LOCALE_SMONGROUPING, numberFormat.CurrencyGroupSizes);
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_INEGCURR, numberFormat.CurrencyNegativePattern.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ICURRENCY, numberFormat.CurrencyPositivePattern.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SCURRENCY, numberFormat.CurrencySymbol.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDIGITSUBSTITUTION, numberFormat.DigitSubstitution.ToString());
            string text = "";
            string[] nativeDigits = numberFormat.NativeDigits;
            foreach (string str in nativeDigits)
            {
                text += str;
            }
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SNATIVEDIGITS, text);
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SNEGATIVESIGN, numberFormat.NegativeSign.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, numberFormat.NumberDecimalDigits.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, numberFormat.NumberDecimalSeparator.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, numberFormat.NumberGroupSeparator.ToString());
            SetGroupSizes(LOCALE_SGROUPING, numberFormat.NumberGroupSizes);
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, numberFormat.NumberNegativePattern.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SPOSITIVESIGN, numberFormat.PositiveSign.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, dateTimeFormat.DateSeparator.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIME, dateTimeFormat.TimeSeparator.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, dateTimeFormat.ShortDatePattern.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SLONGDATE, dateTimeFormat.LongDatePattern.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIMEFORMAT, dateTimeFormat.LongTimePattern.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_S1159, dateTimeFormat.AMDesignator.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_S2359, dateTimeFormat.PMDesignator.ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, ((int)(dateTimeFormat.FirstDayOfWeek + 6) % 7).ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR, ((int)dateTimeFormat.CalendarWeekRule).ToString());
            LPAPIWrapper.SetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SYEARMONTH, dateTimeFormat.YearMonthPattern.ToString());
            LPAPIWrapper.SendNotifyMessage((IntPtr)65535, 26u, IntPtr.Zero, "intl");
        }
    }

如果你想进一步分析发生了什么,你可以反汇编位于C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.InternationalSettings.Commands\v4.0_3.0.0.0__31bf3856ad364e35\Microsoft.InternationalSettings.Commands.dll的dll

【讨论】:

    【解决方案2】:

    这是一个老歌,但万一有人偶然发现: 我反汇编了 DLL,最后得到了以下代码来设置语言环境。 还请查看我在代码库中的注释。

    [DllImport ("kernelbase.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern int NlsUpdateLocale (string LocaleName, int Flags);
    
    public static void SetLocale (string locale)
    {
      // NOTE: this code was derived by disassembling the Set-Culture PowerShell command located at C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.InternationalSettings.Commands\v4.0_3.0.0.0__31bf3856ad364e35\Microsoft.InternationalSettings.Commands.dll
      //   However the PowerShell command has a branch that executes when the result of NlsUpdateLocale is not 0.
      //   This branch then sets all values that make up a locale separately. This is probably for cultures that are customized.
      //   Thus this branch is not implemented here and 0 simply leads to an error stating that Set-Culture should be called for that user manually in order to have the correct date, time, number and currency format for Excel rendering.
      var NLS_LOCALE_SELECT = 1;
      var NLS_LOCALE_CLEAR_USER_DATA = 2;
    
      var result = NlsUpdateLocale (locale, NLS_LOCALE_SELECT | NLS_LOCALE_CLEAR_USER_DATA);
    
      if (result != 0)
        throw new Exception ($"There ws an error setting the user locale to '{locale}'. Please set the locale for the service user of this service manually by executing the PowerShell command 'Set-Culture {locale}'. Result of NlsUpdateLocale was {result} instead of 0. ");
    }
    

    当使用 Windows 已知的语言环境(或文化,无论您如何称呼它们)时,此方法应该没问题。例如:en-US、de-AT、de-DE 等。

    请注意,某些较旧的操作系统可能无法支持较新操作系统所支持的所有文化。例如,Windows Server 2012 在 .NET 代码中没有可用的 en-CH,而 Windows 10 有。但是,我没有测试这是否也适用于该 DLL 方法,但我想是的。

    您可以通过发出以下 PowerShell 命令获取支持的 .NET 文化列表:[System.Globalization.CultureInfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-16
      • 1970-01-01
      • 2017-02-24
      • 1970-01-01
      • 1970-01-01
      • 2014-08-23
      • 2011-12-09
      相关资源
      最近更新 更多