From 0883438e5524efd8558a29cdbbd2bd63bebe9484 Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 6 Apr 2017 05:22:18 +0400 Subject: [PATCH] Fix Default/OEM encoding behavior PowerShell Core (#3467) Based on the conclusion in discussion #3248, this PR fix "OEM" and "Default" encoding in powershell. 1. OEM -- We need an internal fix because .Net Core has no "OEM" encoding. The fix gives the following PowerShell Core behavior on "OEM" encoding: - on Windows - as Windows PowerShell based on GetOEMCP() and legacy encodings (System.Text.Encoding.CodePages) - on Unix - the same as default encoding in PowerShell Core 2. Default -- We need internal fix because CoreFX `Encoding.default` for all platforms is UTF8. Until we complete the `Encoding RFC` we should restore behavior as in Windows PowerShell. The fix gives the following PowerShell Core behavior on "Default" encoding: - on Windows - as Windows PowerShell based on GetACP() and legacy encodings (System.Text.Encoding.CodePages) - on Unix - UTF-8 without BOM (We are expecting that the same will be [in CoreFX](https://github.com/dotnet/coreclr/issues/10643)) --- .../Microsoft.PowerShell.SDK.csproj | 1 - .../System.Management.Automation.csproj | 1 + .../namespaces/FileSystemProvider.cs | 8 +--- .../utils/ClrFacade.cs | 44 +++++++++++++++---- .../utils/PInvokeDllNames.cs | 2 + 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj index 4906981bbc..2f93bb761e 100644 --- a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj +++ b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj @@ -85,7 +85,6 @@ - diff --git a/src/System.Management.Automation/System.Management.Automation.csproj b/src/System.Management.Automation/System.Management.Automation.csproj index 5cab5d30e1..4be687a262 100644 --- a/src/System.Management.Automation/System.Management.Automation.csproj +++ b/src/System.Management.Automation/System.Management.Automation.csproj @@ -47,6 +47,7 @@ + diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 706893c594..d1a8eb68cf 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -7569,8 +7569,7 @@ namespace Microsoft.PowerShell.Commands case FileSystemCmdletProviderEncoding.Oem: { - uint oemCP = NativeMethods.GetOEMCP(); - encoding = System.Text.Encoding.GetEncoding((int)oemCP); + encoding = ClrFacade.GetOEMEncoding(); } break; @@ -7607,11 +7606,6 @@ namespace Microsoft.PowerShell.Commands } // get } // WasStreamTypeSpecified - private static class NativeMethods - { - [DllImport(PinvokeDllNames.GetOEMCPDllName, SetLastError = false, CharSet = CharSet.Unicode)] - internal static extern uint GetOEMCP(); - } } // class FileSystemContentDynamicParametersBase /// diff --git a/src/System.Management.Automation/utils/ClrFacade.cs b/src/System.Management.Automation/utils/ClrFacade.cs index 5ab5111de8..6a14967bba 100644 --- a/src/System.Management.Automation/utils/ClrFacade.cs +++ b/src/System.Management.Automation/utils/ClrFacade.cs @@ -530,22 +530,26 @@ namespace System.Management.Automation #region Encoding /// - /// Facade for Encoding.Default + /// Facade for getting default encoding /// internal static Encoding GetDefaultEncoding() { if (s_defaultEncoding == null) { -#if CORECLR // Encoding.Default is not in CoreCLR - // As suggested by CoreCLR team (tarekms), use latin1 (ISO-8859-1, CodePage 28591) as the default encoding. - // We will revisit this if it causes any failures when running tests on Core PS. - s_defaultEncoding = Encoding.GetEncoding(28591); -#else +#if UNIX // PowerShell Core on Unix + s_defaultEncoding = new UTF8Encoding(false); +#elif CORECLR // PowerShell Core on Windows + EncodingRegisterProvider(); + + uint currentAnsiCp = NativeMethods.GetACP(); + s_defaultEncoding = Encoding.GetEncoding((int)currentAnsiCp); +#else // Windows PowerShell s_defaultEncoding = Encoding.Default; #endif } return s_defaultEncoding; } + private static volatile Encoding s_defaultEncoding; /// @@ -555,18 +559,34 @@ namespace System.Management.Automation { if (s_oemEncoding == null) { -#if CORECLR // The OEM code page '437' is not supported by CoreCLR. - // Use the default encoding (ISO-8859-1, CodePage 28591) as the OEM encoding in OneCore powershell. +#if UNIX // PowerShell Core on Unix s_oemEncoding = GetDefaultEncoding(); -#else +#elif CORECLR // PowerShell Core on Windows + EncodingRegisterProvider(); + + uint oemCp = NativeMethods.GetOEMCP(); + s_oemEncoding = Encoding.GetEncoding((int)oemCp); + +#else // Windows PowerShell uint oemCp = NativeMethods.GetOEMCP(); s_oemEncoding = Encoding.GetEncoding((int)oemCp); #endif } return s_oemEncoding; } + private static volatile Encoding s_oemEncoding; +#if CORECLR + private static void EncodingRegisterProvider() + { + if (s_defaultEncoding == null && s_oemEncoding == null) + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } + } +#endif + #endregion Encoding #region Security @@ -1033,6 +1053,12 @@ namespace System.Management.Automation [DllImport(PinvokeDllNames.GetOEMCPDllName, SetLastError = false, CharSet = CharSet.Unicode)] internal static extern uint GetOEMCP(); + /// + /// Pinvoke for GetACP to get the Windows operating system code page. + /// + [DllImport(PinvokeDllNames.GetACPDllName, SetLastError = false, CharSet = CharSet.Unicode)] + internal static extern uint GetACP(); + public const int S_OK = 0x00000000; /// diff --git a/src/System.Management.Automation/utils/PInvokeDllNames.cs b/src/System.Management.Automation/utils/PInvokeDllNames.cs index 8497fe09de..5c55e08f83 100644 --- a/src/System.Management.Automation/utils/PInvokeDllNames.cs +++ b/src/System.Management.Automation/utils/PInvokeDllNames.cs @@ -135,6 +135,7 @@ namespace System.Management.Automation internal const string CreateToolhelp32SnapshotDllName = "api-ms-win-core-toolhelp-l1-1-0"; /*120*/ internal const string Process32FirstDllName = "api-ms-win-core-toolhelp-l1-1-0"; /*121*/ internal const string Process32NextDllName = "api-ms-win-core-toolhelp-l1-1-0"; /*122*/ + internal const string GetACPDllName = "api-ms-win-core-localization-l1-2-0.dll"; /*123*/ #else internal const string QueryDosDeviceDllName = "kernel32.dll"; /*1*/ internal const string CreateSymbolicLinkDllName = "kernel32.dll"; /*2*/ @@ -257,6 +258,7 @@ namespace System.Management.Automation internal const string CreateToolhelp32SnapshotDllName = "kernel32.dll"; /*120*/ internal const string Process32FirstDllName = "kernel32.dll"; /*121*/ internal const string Process32NextDllName = "kernel32.dll"; /*122*/ + internal const string GetACPDllName = "kernel32.dll"; /*123*/ #endif } }