/// <summary> /// Default constructor. /// </summary> /// <param name="parentForm">The connection form that will display the PowerShell console.</param> /// <param name="terminal">Terminal control that will display the PowerShell console.</param> /// <param name="executeHelper">Method used to execute PowerShell commands within the current session.</param> /// <param name="progressBar">Progress bar UI element to update when writing progress records.</param> /// <param name="progressLabel">Label UI element to update when writing progress records.</param> public PowerShellHost( PowerShellConnectionForm parentForm, TerminalControl terminal, StreamConnection connection, Func <string, Collection <PSObject> > executeHelper, ToolStripProgressBar progressBar, ToolStripStatusLabel progressLabel) { _parentForm = parentForm; _powerShellHostUi = new PowerShellHostUi(terminal, connection, executeHelper, progressBar, progressLabel); }
/// <summary> /// Default constructor. /// </summary> /// <param name="terminal">Terminal control that will display the PowerShell console.</param> public PowerShellRawUi(TerminalControl terminal, StreamConnection connection) { _terminal = terminal; _backgroundColor = ClosestConsoleColor(_terminal.BackColor); _foregroundColor = ClosestConsoleColor(_terminal.ForeColor); _connection = connection; }
/// <summary> /// This API Reads a pressed, released, or pressed and released keystroke from the keyboard device, blocking processing until a keystroke is typed that /// matches the specified keystroke options. /// </summary> /// <param name="options">Options, such as IncludeKeyDown, used when reading the keyboard.</param> /// <returns>Data for the key that the user pressed.</returns> public override KeyInfo ReadKey(ReadKeyOptions options) { StreamConnection connection = _terminal.TerminalPane.ConnectionTag.Connection as StreamConnection; connection.Capture = true; _inputSemaphore.Reset(); // Start up a thread to watch the terminal's input bufer _inputThread = new Thread(ReadInput) { Name = "PowerShellRawUi Input Thread" }; _inputThread.Start(new Tuple <StreamConnection, ReadKeyOptions>(connection, options)); // ReadInput will signal through this semaphore when a key has been pressed _inputSemaphore.WaitOne(); connection.Capture = false; return(_readKey); }
/// <summary> /// Wires up a PowerShell runspace created via <see cref="RunspaceFactory.CreateRunspace()"/> to the terminal to display the PowerShell to the user. /// </summary> public override void Connect() { GEnv.Options.Font = Connection.Font; _progressBar.Value = 0; _progressLabel.Text = ""; // This is not strictly a network connection: we're relaying information that we receive from the runspace to the terminal over a local stream // (a StreamConnection in this case) TerminalParam terminalParam = TCPTerminalParam.Fake; terminalParam.TerminalType = TerminalType.XTerm; terminalParam.RenderProfile = new RenderProfile(); terminalParam.Encoding = EncodingType.UTF8; StreamConnection connection = new StreamConnection(terminalParam) { Capture = false }; ConnectionTag connectionTag = new ConnectionTag(connection); connectionTag.Receiver.Listen(); // Attach the new "connection" to the terminal control _terminal.TerminalPane.FakeVisible = true; _terminal.TerminalPane.Attach(connectionTag); _terminal.TerminalPane.Focus(); _terminal.TerminalPane.SendShiftTab = true; _terminal.SetPaneColors(Connection.TextColor, Connection.BackgroundColor); try { _powerShellHost = new PowerShellHost(this, _terminal, ExecuteQuiet, _progressBar, _progressLabel); // Create the host and runspace instances for this interpreter. If we're connecting to the local host, don't bother with the connection info. // ReSharper disable StringCompareIsCultureSpecific.3 if (String.Compare(Connection.Host, "localhost", true) != 0 && Connection.Host != "127.0.0.1" && String.Compare(Connection.Host, Environment.MachineName, true) != 0) // ReSharper restore StringCompareIsCultureSpecific.3 { WSManConnectionInfo connectionInfo = new WSManConnectionInfo { ComputerName = Connection.Host }; if (!String.IsNullOrEmpty(Connection.InheritedUsername)) { connectionInfo.Credential = new PSCredential(Connection.InheritedUsername, Connection.InheritedPassword); } Runspace = RunspaceFactory.CreateRunspace(_powerShellHost, connectionInfo); } else { Runspace = RunspaceFactory.CreateRunspace(_powerShellHost); } Runspace.Open(); } catch (Exception e) { OnConnectionLost(this, new ErrorEventArgs(e)); return; } // Start capturing input from the prompt via the input loop _inputThread = new Thread(InputLoop) { Name = "PowerShellConnectionForm Input Thread" }; _inputThread.Start(); ParentForm.Closing += ParentForm_Closing; OnConnected(this, null); }
/// <summary> /// Wires up a PowerShell runspace created via <see cref="RunspaceFactory.CreateRunspace()"/> to the terminal to display the PowerShell to the user. /// </summary> public override void Connect() { _progressBar.Value = 0; _progressLabel.Text = ""; // This is not strictly a network connection: we're relaying information that we receive from the runspace to the terminal over a local stream // (a StreamConnection in this case) ITerminalParameter terminalParam = new EmptyTerminalParameter(); StreamConnection connection = new StreamConnection(terminalParam) { Capture = false }; // Attach the new "connection" to the terminal control try { ITerminalSettings terminalSettings = PoderosaTerminalEmulatorService.CreateDefaultTerminalSettings(Connection.DisplayName, null); TerminalSession session = new TerminalSession(connection, terminalSettings); SessionHost sessionHost = new SessionHost(PoderosaSessionManagerPlugin, session); TerminalView terminalView = new TerminalView(null, _terminal); RenderProfile renderProfile = new RenderProfile(_terminal.GetRenderProfile()); renderProfile.BackColor = Connection.BackgroundColor; renderProfile.ForeColor = Connection.TextColor; renderProfile.FontName = Connection.FontFamily; renderProfile.FontSize = Connection.FontSize; session.TerminalSettings.BeginUpdate(); session.TerminalSettings.RenderProfile = renderProfile; session.TerminalSettings.EndUpdate(); _terminal.Attach(session); session.InternalStart(sessionHost); session.InternalAttachView(sessionHost.DocumentAt(0), terminalView); _powerShellHost = new PowerShellHost(this, _terminal, connection, ExecuteQuiet, _progressBar, _progressLabel); // Create the host and runspace instances for this interpreter. If we're connecting to the local host, don't bother with the connection info. // ReSharper disable StringCompareIsCultureSpecific.3 if (String.Compare(Connection.Host, "localhost", true) != 0 && Connection.Host != "127.0.0.1" && String.Compare(Connection.Host, Environment.MachineName, true) != 0) // ReSharper restore StringCompareIsCultureSpecific.3 { WSManConnectionInfo connectionInfo = new WSManConnectionInfo { ComputerName = Connection.Host }; if (!String.IsNullOrEmpty(Connection.InheritedUsername)) { connectionInfo.Credential = new PSCredential(Connection.InheritedUsername, Connection.InheritedPassword); } Runspace = RunspaceFactory.CreateRunspace(_powerShellHost, connectionInfo); } else { Runspace = RunspaceFactory.CreateRunspace(_powerShellHost); } Runspace.Open(); } catch (Exception e) { OnConnectionLost(this, new ErrorEventArgs(e)); return; } // Start capturing input from the prompt via the input loop _inputThread = new Thread(InputLoop) { Name = "PowerShellConnectionForm Input Thread" }; _inputThread.Start(); ParentForm.Closing += ParentForm_Closing; OnConnected(this, null); }
/// <summary> /// Thread method invoked from <see cref="ReadKey"/> that waits for the user to press a key. /// </summary> /// <param name="state"><see cref="Tuple{T1, T2}"/> object containing a <see cref="StreamConnection"/> object and a <see cref="ReadKeyOptions"/> /// object.</param> private void ReadInput(object state) { StreamConnection connection = (state as Tuple <StreamConnection, ReadKeyOptions>).Item1; ReadKeyOptions options = (state as Tuple <StreamConnection, ReadKeyOptions>).Item2; bool inEscapeSequence = false; bool readKey = false; while (!readKey) { if (connection.OutputQueue.Count > 0) { while (connection.OutputQueue.Count > 0) { byte currentByte = connection.OutputQueue.Dequeue(); // Handle the backspace key if (currentByte == 8) { _readKey = new KeyInfo { VirtualKeyCode = 8 }; // If we're not already at the beginning of the line and we're echoing the output, move the cursor left if ((options & ReadKeyOptions.NoEcho) != ReadKeyOptions.NoEcho && CursorPosition.X > 1) { CursorPosition = new Coordinates(CursorPosition.X - 1, CursorPosition.Y); } readKey = true; break; } // The ^X character signifies the start of an ANSI escape sequence else if (currentByte == 27) { inEscapeSequence = true; } // If we're in an escape sequence, read past the "[" and "~" characters else if (currentByte == 91 && inEscapeSequence) { } else if (currentByte == 126 && inEscapeSequence) { } // ^X7 is the home key else if (currentByte == 55 && inEscapeSequence) { _readKey = new KeyInfo { VirtualKeyCode = 0x24 }; // If we're not already at the beginning of the line and we're echoing the output, move the cursor to the start of the line if ((options & ReadKeyOptions.NoEcho) != ReadKeyOptions.NoEcho) { CursorPosition = new Coordinates(1, CursorPosition.Y); } readKey = true; break; } // ^X8 or ^X3 is the end key else if ((currentByte == 56 || currentByte == 51) && inEscapeSequence) { _readKey = new KeyInfo { VirtualKeyCode = 0x23 }; // If we're not already at the beginning of the line and we're echoing the output, move the cursor to the end of the line if ((options & ReadKeyOptions.NoEcho) != ReadKeyOptions.NoEcho) { CursorPosition = new Coordinates(BufferSize.Width, CursorPosition.Y); } readKey = true; break; } // ^XD is the left arrow else if (currentByte == 68 && inEscapeSequence) { _readKey = new KeyInfo { VirtualKeyCode = 0x25 }; // If we're not already at the beginning of the line and we're echoing the output, move the cursor left if ((options & ReadKeyOptions.NoEcho) != ReadKeyOptions.NoEcho && CursorPosition.X > 1) { CursorPosition = new Coordinates(CursorPosition.X - 1, CursorPosition.Y); } readKey = true; break; } // ^XC is the right arrow else if (currentByte == 67 && inEscapeSequence) { _readKey = new KeyInfo { VirtualKeyCode = 0x27 }; // If we're not already at the beginning of the line and we're echoing the output, move the cursor right if ((options & ReadKeyOptions.NoEcho) != ReadKeyOptions.NoEcho && CursorPosition.X < BufferSize.Width) { CursorPosition = new Coordinates(CursorPosition.X + 1, CursorPosition.Y); } readKey = true; break; } // Handle the carriage return sequence else if (currentByte == 13) { _readKey = new KeyInfo { Character = '\r', VirtualKeyCode = 0x0D }; if ((options & ReadKeyOptions.NoEcho) != ReadKeyOptions.NoEcho) { CursorPosition = new Coordinates(1, CursorPosition.Y); } readKey = true; break; } // Otherwise, get the virtual key code and character and populate _readKey else { short virtualKey = User32.VkKeyScan((char)currentByte); int modifiers = virtualKey >> 8; ControlKeyStates controlKeys = 0; if ((modifiers & 2) != 0) { controlKeys |= ControlKeyStates.LeftCtrlPressed; } if ((modifiers & 4) != 0) { controlKeys |= ControlKeyStates.LeftAltPressed; } _readKey = new KeyInfo { Character = (char)currentByte, VirtualKeyCode = (virtualKey & 0xFF), ControlKeyState = controlKeys }; if ((options & ReadKeyOptions.NoEcho) != ReadKeyOptions.NoEcho) { _terminal.TerminalPane.ConnectionTag.Receiver.DataArrived( new byte[] { currentByte }, 0, 1); } readKey = true; break; } } } // If we didn't read a key, sleep for a bit if (!readKey) { Thread.Sleep(50); } } // Signal to ReadKey() that we've read a key _inputSemaphore.Set(); }