From a96b28266943c46e95fffc2aedc4a7215fa9b3aa Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 21 Mar 2019 14:56:49 -0700 Subject: [PATCH] backport #881 in a different way (#884) * backport #881 in a different way * simpler * switch back to reflecting * address feedback * more feedback * safe check --- .../Server/LanguageServer.cs | 78 ++++++++++++------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs index 9298ec942..78b19cfe5 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs @@ -16,9 +16,9 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Management.Automation; using System.Management.Automation.Language; using System.Management.Automation.Runspaces; +using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -28,6 +28,8 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.Server { + using System.Management.Automation; + public class LanguageServer { private static CancellationTokenSource s_existingRequestCancellation; @@ -42,6 +44,15 @@ public class LanguageServer private static readonly SymbolInformation[] s_emptySymbolResult = new SymbolInformation[0]; + // Since the NamedPipeConnectionInfo type is only available in 5.1+ + // we have to use Activator to support older version of PS. + // This code only lives in the v1.X of the extension. + // The 2.x version of the code can be found here: + // https://github.com/PowerShell/PowerShellEditorServices/pull/881 + private static readonly ConstructorInfo s_namedPipeConnectionInfoCtor = typeof(PSObject).GetTypeInfo().Assembly + .GetType("System.Management.Automation.Runspaces.NamedPipeConnectionInfo") + ?.GetConstructor(new [] { typeof(int) }); + private ILogger Logger; private bool profilesLoaded; private bool consoleReplStarted; @@ -1234,11 +1245,17 @@ protected async Task HandleCommentHelpRequest( await requestContext.SendResult(result); } + private static Runspace GetRemoteRunspace(int pid) + { + var namedPipeConnectionInfoInstance = s_namedPipeConnectionInfoCtor.Invoke(new object[] { pid }); + return RunspaceFactory.CreateRunspace(namedPipeConnectionInfoInstance as RunspaceConnectionInfo); + } + protected async Task HandleGetRunspaceRequestAsync( string processId, RequestContext requestContext) { - var runspaceResponses = new List(); + IEnumerable runspaces = null; if (this.editorSession.PowerShellContext.LocalPowerShellVersion.Version.Major >= 5) { @@ -1246,36 +1263,43 @@ protected async Task HandleGetRunspaceRequestAsync( processId = "current"; } - var isNotCurrentProcess = processId != null && processId != "current"; - - var psCommand = new PSCommand(); - - if (isNotCurrentProcess) { - psCommand.AddCommand("Enter-PSHostProcess").AddParameter("Id", processId).AddStatement(); - } - - psCommand.AddCommand("Get-Runspace"); - - StringBuilder sb = new StringBuilder(); - IEnumerable runspaces = await editorSession.PowerShellContext.ExecuteCommand(psCommand, sb); - if (runspaces != null) + // If the processId is a valid int, we need to run Get-Runspace within that process + // otherwise just use the current runspace. + if (int.TryParse(processId, out int pid)) { - foreach (var p in runspaces) + + // Create a remote runspace that we will invoke Get-Runspace in. + using(Runspace rs = GetRemoteRunspace(pid)) + using(var ps = PowerShell.Create()) { - runspaceResponses.Add( - new GetRunspaceResponse - { - Id = p.Id, - Name = p.Name, - Availability = p.RunspaceAvailability.ToString() - }); + rs.Open(); + ps.Runspace = rs; + // Returns deserialized Runspaces. For simpler code, we use PSObject and rely on dynamic later. + runspaces = ps.AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace").Invoke(); } } + else + { + var psCommand = new PSCommand().AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace"); + var sb = new StringBuilder(); + // returns (not deserialized) Runspaces. For simpler code, we use PSObject and rely on dynamic later. + runspaces = await editorSession.PowerShellContext.ExecuteCommand(psCommand, sb); + } + } - if (isNotCurrentProcess) { - var exitCommand = new PSCommand(); - exitCommand.AddCommand("Exit-PSHostProcess"); - await editorSession.PowerShellContext.ExecuteCommand(exitCommand); + var runspaceResponses = new List(); + + if (runspaces != null) + { + foreach (dynamic runspace in runspaces) + { + runspaceResponses.Add( + new GetRunspaceResponse + { + Id = runspace.Id, + Name = runspace.Name, + Availability = runspace.RunspaceAvailability.ToString() + }); } }