From 2cdf74bc55ebe84fbf57a2e5c834661fc2ab224d Mon Sep 17 00:00:00 2001 From: dronov-dmitry Date: Thu, 10 Sep 2026 22:02:30 +0200 Subject: [PATCH 1/2] Fix encoding 866 crash in SharedIO: fallback to UTF-8 when console encoding unavailable SharedIO.InitializeInput/Output/ErrorOutput accessed Console.InputEncoding and Console.Out/Console.Error without error handling. On systems where cp866 (or other console encodings) are not registered (e.g. Dynamo/Revit hosting environment, .NET Core), this threw ArgumentException: 'No data is available for encoding 866' Added TryGetConsoleEncoding() helper that wraps encoding access in try-catch and falls back to Encoding.UTF8 / TextWriter.Null when the console encoding is unavailable. --- .../Microsoft.Scripting/Runtime/SharedIO.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core/Microsoft.Scripting/Runtime/SharedIO.cs b/src/core/Microsoft.Scripting/Runtime/SharedIO.cs index 4ab7605f..8ca25895 100644 --- a/src/core/Microsoft.Scripting/Runtime/SharedIO.cs +++ b/src/core/Microsoft.Scripting/Runtime/SharedIO.cs @@ -97,13 +97,21 @@ public override void SetLength(long value) { internal SharedIO() { } + private static Encoding TryGetConsoleEncoding(Func getter, Encoding fallback) { + try { + return getter(); + } catch { + return fallback; + } + } + private void InitializeInput() { if (_inputStream is null) { lock (_mutex) { if (_inputStream is null) { switch (ConsoleSupportLevel) { case SupportLevel.Full: - _inputEncoding = Console.InputEncoding; + _inputEncoding = TryGetConsoleEncoding(() => Console.InputEncoding, Encoding.UTF8); _inputStream = ConsoleInputStream.Instance; _inputReader = Console.In; break; @@ -131,7 +139,9 @@ private void InitializeOutput() { switch (ConsoleSupportLevel) { case SupportLevel.Full: _outputStream = Console.OpenStandardOutput(); - _outputWriter = Console.Out; + _outputWriter = TryGetConsoleEncoding(() => Console.Out.Encoding, Encoding.UTF8) is var enc && enc.CodePage is 1200 or 65001 + ? Console.Out + : new StreamWriter(_outputStream, enc, bufferSize: 64); break; case SupportLevel.Basic: _outputStream = new TextStream(Console.Out); @@ -157,7 +167,9 @@ private void InitializeErrorOutput() { switch (ConsoleSupportLevel) { case SupportLevel.Full: _errorStream = Console.OpenStandardError(); - _errorWriter = Console.Error; + _errorWriter = TryGetConsoleEncoding(() => Console.Error.Encoding, Encoding.UTF8) is var enc && enc.CodePage is 1200 or 65001 + ? Console.Error + : new StreamWriter(_errorStream, enc, bufferSize: 64); break; case SupportLevel.Basic: _errorStream = new TextStream(Console.Error); From 968fe047f63fdb2caf7e70522626b8a577dfa3a5 Mon Sep 17 00:00:00 2001 From: dronov-dmitry Date: Sun, 11 Oct 2026 05:37:30 +0200 Subject: [PATCH 2/2] Fix incomplete console fallback in SharedIO The previous change guarded Console.InputEncoding/Console.Out.Encoding but still failed in the situations it was meant to survive: 1. Console.In is built from Console.InputEncoding (see ConsolePal.GetOrCreateReader), so it threw again right after the guarded read and the engine still failed to start. 2. The output/error ternary evaluated Console.Out / Console.Error a second time outside the try, so the UTF-8 fallback re-entered the throwing getter and the exception escaped. 3. The replacement StreamWriter did not set AutoFlush, while Console.Out is created with AutoFlush = true, so output smaller than the 64 byte buffer was silently dropped. Console.In / Console.Out / Console.Error are now each evaluated once inside a guard, with readers/writers that keep stdin/stdout open, auto-flush and fall back to UTF-8 (or TextReader.Null) when the console is unavailable. The non UTF-16/UTF-8 writer replacement is gone: it provided no benefit and dropped the synchronized wrapper Console.Out normally has. --- .../Microsoft.Scripting/Runtime/SharedIO.cs | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/core/Microsoft.Scripting/Runtime/SharedIO.cs b/src/core/Microsoft.Scripting/Runtime/SharedIO.cs index 8ca25895..4fb94215 100644 --- a/src/core/Microsoft.Scripting/Runtime/SharedIO.cs +++ b/src/core/Microsoft.Scripting/Runtime/SharedIO.cs @@ -105,6 +105,27 @@ private static Encoding TryGetConsoleEncoding(Func getter, Encoding fa } } + private static TextReader TryGetConsoleReader(Func getter, Encoding fallbackEncoding) { + try { + return getter(); + } catch { + // Console.In is built from Console.InputEncoding, so it throws for the same reason. + try { + return new StreamReader(Console.OpenStandardInput(), fallbackEncoding, detectEncodingFromByteOrderMarks: false, bufferSize: 1024, leaveOpen: true); + } catch { + return TextReader.Null; + } + } + } + + private static TextWriter TryGetConsoleWriter(Func getter, Func fallback) { + try { + return getter(); + } catch { + return fallback(); + } + } + private void InitializeInput() { if (_inputStream is null) { lock (_mutex) { @@ -113,7 +134,7 @@ private void InitializeInput() { case SupportLevel.Full: _inputEncoding = TryGetConsoleEncoding(() => Console.InputEncoding, Encoding.UTF8); _inputStream = ConsoleInputStream.Instance; - _inputReader = Console.In; + _inputReader = TryGetConsoleReader(() => Console.In, _inputEncoding); break; case SupportLevel.Basic: _inputEncoding = Encoding.UTF8; @@ -139,9 +160,9 @@ private void InitializeOutput() { switch (ConsoleSupportLevel) { case SupportLevel.Full: _outputStream = Console.OpenStandardOutput(); - _outputWriter = TryGetConsoleEncoding(() => Console.Out.Encoding, Encoding.UTF8) is var enc && enc.CodePage is 1200 or 65001 - ? Console.Out - : new StreamWriter(_outputStream, enc, bufferSize: 64); + _outputWriter = TryGetConsoleWriter( + () => Console.Out, + () => new StreamWriter(_outputStream, Encoding.UTF8, bufferSize: 64, leaveOpen: true) { AutoFlush = true }); break; case SupportLevel.Basic: _outputStream = new TextStream(Console.Out); @@ -167,9 +188,9 @@ private void InitializeErrorOutput() { switch (ConsoleSupportLevel) { case SupportLevel.Full: _errorStream = Console.OpenStandardError(); - _errorWriter = TryGetConsoleEncoding(() => Console.Error.Encoding, Encoding.UTF8) is var enc && enc.CodePage is 1200 or 65001 - ? Console.Error - : new StreamWriter(_errorStream, enc, bufferSize: 64); + _errorWriter = TryGetConsoleWriter( + () => Console.Error, + () => new StreamWriter(_errorStream, Encoding.UTF8, bufferSize: 64, leaveOpen: true) { AutoFlush = true }); break; case SupportLevel.Basic: _errorStream = new TextStream(Console.Error);