diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 631e1d3b48256..c783b29c210e2 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -5791,6 +5791,10 @@ public void Codepage() parsedArgs.Errors.Verify(); Assert.Equal("Unicode (UTF-8)", parsedArgs.Encoding.EncodingName); + parsedArgs = DefaultParse(new[] { "/CodePage:1252", "a.cs" }, WorkingDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal("Western European (Windows)", parsedArgs.Encoding.EncodingName); + // error parsedArgs = DefaultParse(new[] { "/codepage:0", "a.cs" }, WorkingDirectory); parsedArgs.Errors.Verify(Diagnostic(ErrorCode.FTL_BadCodepage).WithArguments("0")); diff --git a/src/Compilers/Core/Portable/CommandLine/CommandLineParser.cs b/src/Compilers/Core/Portable/CommandLine/CommandLineParser.cs index 37008c11970f0..64b37f79b9f8d 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommandLineParser.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommandLineParser.cs @@ -24,6 +24,7 @@ public abstract class CommandLineParser internal readonly bool IsScriptCommandLineParser; private static readonly char[] s_searchPatternTrimChars = new char[] { '\t', '\n', '\v', '\f', '\r', ' ', '\x0085', '\x00a0' }; internal const string ErrorLogOptionFormat = "[,version={1|1.0|2|2.1}]"; + private static bool s_registeredEncodingProvider = CodePagesEncodingProvider.Instance == null; internal CommandLineParser(CommonMessageProvider messageProvider, bool isScriptCommandLineParser) { @@ -1220,10 +1221,49 @@ internal IEnumerable ParseRecurseArgument(string arg, str && long.TryParse(arg, NumberStyles.None, CultureInfo.InvariantCulture, out long codepage) && (codepage > 0)) { +try_again: try { return Encoding.GetEncoding((int)codepage); } + catch (NotSupportedException) when (!s_registeredEncodingProvider) + { + // From documentation: + // - 'GetEncoding' throws NotSupportedException when codepage is not supported by the underlying platform. + // - 'EncodingProvider.Instance' gets an encoding provider for code pages supported + // in the desktop .NET Framework but not by the current underlying platform. + // - 'Encoding.RegisterProvider' makes character encodings available on a platform that does not otherwise support them. + // * Once the encoding provider is registered, the encodings that it supports can be retrieved by calling any + // Encoding.GetEncoding overload. + // * Registering an encoding provider by using the 'RegisterProvider' method also affects the behavior of + // GetEncoding(Int32) when passed an argument of 0. + // * If multiple providers are registered, GetEncoding(Int32) attempts to retrieve the encoding from the most recently + // registered provider first. + // * If the 'RegisterProvider' method is called to register multiple providers that handle the same encoding, + // the last registered provider is the used for all encoding and decoding operations. Any previously registered providers are ignored. + // * If the same encoding provider is used in multiple calls to the 'RegisterProvider' method, + // only the first method call registers the provider. Subsequent calls are ignored. + // + // Given all that: + // - We don't call 'Encoding.RegisterProvider' unconditionally to avoid changing environment + // that is already configured to support the requested codepage. We call it only when we encounter + // a 'NotSupportedException'. + // - We also do not attempt to call 'Encoding.RegisterProvider' more than once. + try + { + // Ignore any exceptions from an attempt to register the provider. + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } + catch + { + } + + s_registeredEncodingProvider = true; + + // Try to get the encoding again after attempting to register the provider. + // Since we set `s_registeredEncodingProvider` to true, we won't get here again. + goto try_again; + } catch (Exception) { return null; diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 0163745a8053b..6d9e647d4f190 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -1863,6 +1863,10 @@ End Module").Path parsedArgs.Errors.Verify() Assert.Equal("Unicode (UTF-8)", parsedArgs.Encoding.EncodingName) + parsedArgs = DefaultParse({"/CodePage:1252", "a.vb"}, _baseDirectory) + parsedArgs.Errors.Verify() + Assert.Equal("Western European (Windows)", parsedArgs.Encoding.EncodingName) + ' errors parsedArgs = DefaultParse({"/codepage:0", "a.vb"}, _baseDirectory) parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_BadCodepage).WithArguments("0"))