From 07b0964be402dc99defe1eacc4ef9bbcf847b3fa Mon Sep 17 00:00:00 2001 From: "Jason Shirk (POWERSHELL)" Date: Mon, 26 Sep 2016 10:30:42 -0700 Subject: [PATCH] Fix hang when reading stdin from ruby This is a workaround for a full clr issue that causes a hang when calling PowerShell from ruby. They use named pipes instead of anonymous pipes, and for some reason that triggers the hang. --- .../host/msh/ConsoleControl.cs | 3 +++ .../host/msh/ConsoleHost.cs | 19 +++++++++++++++++++ .../host/msh/ConsoleHostUserInterface.cs | 7 ++++--- .../host/msh/Executor.cs | 3 +-- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs index cbf3bc95aa..4143670932 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs @@ -3467,6 +3467,9 @@ namespace Microsoft.PowerShell [DllImport(PinvokeDllNames.GetLargestConsoleWindowSizeDllName, SetLastError = true, CharSet = CharSet.Unicode)] internal static extern COORD GetLargestConsoleWindowSize(NakedWin32Handle consoleOutput); + [DllImport(PinvokeDllNames.GetStdHandleDllName, SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern IntPtr GetStdHandle(int handleId); + [DllImport(PinvokeDllNames.ReadConsoleDllName, SetLastError = true, CharSet = CharSet.Unicode)] internal static extern bool ReadConsole ( diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index 71838ee2c7..2b3f195c22 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -2868,6 +2868,25 @@ namespace Microsoft.PowerShell private System.Threading.Thread _breakHandlerThread; private bool _isDisposed; internal ConsoleHostUserInterface ui; + +#if CORECLR + internal Lazy ConsoleIn { get; } = new Lazy(() => Console.In); +#else + internal Lazy ConsoleIn { get; } = new Lazy(() => + { + // This is a workaround for a full clr issue that causes a hang when calling PowerShell from ruby. + // They use named pipes instead of anonymous pipes, and for some reason that triggers a hang + // reading from Console.In. + var inputHandle = ConsoleControl.NativeMethods.GetStdHandle(-10); + var s = new FileStream(new ConsoleHandle(inputHandle, false), FileAccess.Read); + + uint codePage = (uint) ConsoleControl.NativeMethods.GetConsoleCP(); + Encoding encoding = Encoding.GetEncoding((int) codePage); + + return TextReader.Synchronized(new StreamReader(s, encoding, false)); + }); +#endif + private string _savedWindowTitle = ""; private Version _ver = PSVersionInfo.PSVersion; private int _exitCodeFromRunspace; diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index 478d05902e..83ce13c7be 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -1560,9 +1560,10 @@ namespace Microsoft.PowerShell sb.Append('\n'); } + var consoleIn = _parent.ConsoleIn.Value; while (true) { - var inC = Console.In.Read(); + var inC = consoleIn.Read(); if (inC == -1) { // EOF - we return null which tells our caller to exit @@ -1577,10 +1578,10 @@ namespace Microsoft.PowerShell if (c == '\r') { // Treat as newline, but consume \n if there is one. - if (Console.In.Peek() == '\n') + if (consoleIn.Peek() == '\n') { if (!NoPrompt) Console.Out.Write('\n'); - Console.In.Read(); + consoleIn.Read(); } break; } diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs index fa8f2af8c6..d7e09f2993 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs @@ -212,8 +212,7 @@ namespace Microsoft.PowerShell if ((options & ExecutionOptions.ReadInputObjects) > 0 && Console.IsInputRedirected) { // read input objects from stdin - - WrappedDeserializer des = new WrappedDeserializer(_parent.InputFormat, "Input", Console.In); + WrappedDeserializer des = new WrappedDeserializer(_parent.InputFormat, "Input", _parent.ConsoleIn.Value); while (!des.AtEnd) { object o = des.Deserialize();