Skip to content

Commit 831ca1e

Browse files
nick-beerjkotas
andauthored
#98441 - Ignore invalid handle/access when setting console cp (#98641)
* Ignore invalid handle/access when setting console cp There are situations where setting the console code page is expected to fail. For instance, if the application was started with DETACHED_PROCESS, the attempting to set the code page will fail with ERROR_INVALID_HANDLE. In these cases, ignore the returned error. Fix #98441 --------- Co-authored-by: Jan Kotas <[email protected]>
1 parent 771afaf commit 831ca1e

File tree

3 files changed

+67
-3
lines changed

3 files changed

+67
-3
lines changed

src/libraries/Common/src/Interop/Windows/Interop.Errors.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ internal static partial class Errors
1313
internal const int ERROR_ACCESS_DENIED = 0x5;
1414
internal const int ERROR_INVALID_HANDLE = 0x6;
1515
internal const int ERROR_NOT_ENOUGH_MEMORY = 0x8;
16+
internal const int ERROR_INVALID_ACCESS = 0xC;
1617
internal const int ERROR_INVALID_DATA = 0xD;
1718
internal const int ERROR_OUTOFMEMORY = 0xE;
1819
internal const int ERROR_INVALID_DRIVE = 0xF;

src/libraries/System.Console/src/System/ConsolePal.Windows.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ public static void SetConsoleInputEncoding(Encoding enc)
108108
if (enc.CodePage != UnicodeCodePage)
109109
{
110110
if (!Interop.Kernel32.SetConsoleCP(enc.CodePage))
111-
throw Win32Marshal.GetExceptionForWin32Error(Marshal.GetLastPInvokeError());
111+
{
112+
HandleSetConsoleEncodingError(Marshal.GetLastPInvokeError());
113+
}
112114
}
113115
}
114116

@@ -122,10 +124,24 @@ public static void SetConsoleOutputEncoding(Encoding enc)
122124
if (enc.CodePage != UnicodeCodePage)
123125
{
124126
if (!Interop.Kernel32.SetConsoleOutputCP(enc.CodePage))
125-
throw Win32Marshal.GetExceptionForWin32Error(Marshal.GetLastPInvokeError());
127+
{
128+
HandleSetConsoleEncodingError(Marshal.GetLastPInvokeError());
129+
}
126130
}
127131
}
128132

133+
private static void HandleSetConsoleEncodingError(int lastError)
134+
{
135+
if (lastError == Interop.Errors.ERROR_INVALID_HANDLE
136+
|| lastError == Interop.Errors.ERROR_INVALID_ACCESS)
137+
{
138+
// no console, or not a valid handle, so fail silently
139+
return;
140+
}
141+
142+
throw Win32Marshal.GetExceptionForWin32Error(lastError);
143+
}
144+
129145
/// <summary>Gets whether Console.In is targeting a terminal display.</summary>
130146
public static bool IsInputRedirectedCore()
131147
{

src/libraries/System.Console/tests/ConsoleEncoding.Windows.cs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5-
using System.Text;
65
using System.Runtime.InteropServices;
6+
using System.Text;
77
using Microsoft.DotNet.RemoteExecutor;
88
using Xunit;
99

@@ -38,6 +38,28 @@ public void InputEncoding_SetUnicodeEncoding_SilentlyIgnoredInternally()
3838
}).Dispose();
3939
}
4040

41+
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
42+
[PlatformSpecific(TestPlatforms.Windows)]
43+
public void InputEncoding_SetEncodingWhenDetached_ErrorIsSilentlyIgnored()
44+
{
45+
RemoteExecutor.Invoke(() =>
46+
{
47+
Encoding encoding = Console.InputEncoding.CodePage != Encoding.ASCII.CodePage
48+
? Encoding.ASCII
49+
: Encoding.Latin1;
50+
51+
// use FreeConsole to detach the current console - simulating a process started with the "DETACHED_PROCESS" flag
52+
FreeConsole();
53+
54+
// Setting the input encoding should not throw an exception
55+
Console.InputEncoding = encoding;
56+
// The internal state of Console should have updated, despite the failure to change the console's input encoding
57+
Assert.Equal(encoding, Console.InputEncoding);
58+
// Operations on the console are no longer valid - GetConsoleCP fails.
59+
Assert.Equal(0u, GetConsoleCP());
60+
}).Dispose();
61+
}
62+
4163
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
4264
[PlatformSpecific(TestPlatforms.Windows)]
4365
public void OutputEncoding_SetDefaultEncoding_Success()
@@ -67,9 +89,34 @@ public void OutputEncoding_SetUnicodeEncoding_SilentlyIgnoredInternally()
6789
}).Dispose();
6890
}
6991

92+
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
93+
[PlatformSpecific(TestPlatforms.Windows)]
94+
public void OutputEncoding_SetEncodingWhenDetached_ErrorIsSilentlyIgnored()
95+
{
96+
RemoteExecutor.Invoke(() =>
97+
{
98+
Encoding encoding = Console.OutputEncoding.CodePage != Encoding.ASCII.CodePage
99+
? Encoding.ASCII
100+
: Encoding.Latin1;
101+
102+
// use FreeConsole to detach the current console - simulating a process started with the "DETACHED_PROCESS" flag
103+
FreeConsole();
104+
105+
// Setting the output encoding should not throw an exception
106+
Console.OutputEncoding = encoding;
107+
// The internal state of Console should have updated, despite the failure to change the console's output encoding
108+
Assert.Equal(encoding, Console.OutputEncoding);
109+
// Operations on the console are no longer valid - GetConsoleOutputCP fails.
110+
Assert.Equal(0u, GetConsoleOutputCP());
111+
}).Dispose();
112+
}
113+
70114
[LibraryImport("kernel32.dll")]
71115
public static partial uint GetConsoleCP();
72116

73117
[LibraryImport("kernel32.dll")]
74118
public static partial uint GetConsoleOutputCP();
119+
120+
[LibraryImport("kernel32.dll")]
121+
public static partial int FreeConsole();
75122
}

0 commit comments

Comments
 (0)