Skip to content

Commit a96b282

Browse files
backport PowerShell#881 in a different way (PowerShell#884)
* backport PowerShell#881 in a different way * simpler * switch back to reflecting * address feedback * more feedback * safe check
1 parent 900b1b8 commit a96b282

File tree

1 file changed

+51
-27
lines changed

1 file changed

+51
-27
lines changed

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
using System.Diagnostics;
1717
using System.IO;
1818
using System.Linq;
19-
using System.Management.Automation;
2019
using System.Management.Automation.Language;
2120
using System.Management.Automation.Runspaces;
21+
using System.Reflection;
2222
using System.Text;
2323
using System.Text.RegularExpressions;
2424
using System.Threading;
@@ -28,6 +28,8 @@
2828

2929
namespace Microsoft.PowerShell.EditorServices.Protocol.Server
3030
{
31+
using System.Management.Automation;
32+
3133
public class LanguageServer
3234
{
3335
private static CancellationTokenSource s_existingRequestCancellation;
@@ -42,6 +44,15 @@ public class LanguageServer
4244

4345
private static readonly SymbolInformation[] s_emptySymbolResult = new SymbolInformation[0];
4446

47+
// Since the NamedPipeConnectionInfo type is only available in 5.1+
48+
// we have to use Activator to support older version of PS.
49+
// This code only lives in the v1.X of the extension.
50+
// The 2.x version of the code can be found here:
51+
// https://github.com/PowerShell/PowerShellEditorServices/pull/881
52+
private static readonly ConstructorInfo s_namedPipeConnectionInfoCtor = typeof(PSObject).GetTypeInfo().Assembly
53+
.GetType("System.Management.Automation.Runspaces.NamedPipeConnectionInfo")
54+
?.GetConstructor(new [] { typeof(int) });
55+
4556
private ILogger Logger;
4657
private bool profilesLoaded;
4758
private bool consoleReplStarted;
@@ -1234,48 +1245,61 @@ protected async Task HandleCommentHelpRequest(
12341245
await requestContext.SendResult(result);
12351246
}
12361247

1248+
private static Runspace GetRemoteRunspace(int pid)
1249+
{
1250+
var namedPipeConnectionInfoInstance = s_namedPipeConnectionInfoCtor.Invoke(new object[] { pid });
1251+
return RunspaceFactory.CreateRunspace(namedPipeConnectionInfoInstance as RunspaceConnectionInfo);
1252+
}
1253+
12371254
protected async Task HandleGetRunspaceRequestAsync(
12381255
string processId,
12391256
RequestContext<GetRunspaceResponse[]> requestContext)
12401257
{
1241-
var runspaceResponses = new List<GetRunspaceResponse>();
1258+
IEnumerable<PSObject> runspaces = null;
12421259

12431260
if (this.editorSession.PowerShellContext.LocalPowerShellVersion.Version.Major >= 5)
12441261
{
12451262
if (processId == null) {
12461263
processId = "current";
12471264
}
12481265

1249-
var isNotCurrentProcess = processId != null && processId != "current";
1250-
1251-
var psCommand = new PSCommand();
1252-
1253-
if (isNotCurrentProcess) {
1254-
psCommand.AddCommand("Enter-PSHostProcess").AddParameter("Id", processId).AddStatement();
1255-
}
1256-
1257-
psCommand.AddCommand("Get-Runspace");
1258-
1259-
StringBuilder sb = new StringBuilder();
1260-
IEnumerable<Runspace> runspaces = await editorSession.PowerShellContext.ExecuteCommand<Runspace>(psCommand, sb);
1261-
if (runspaces != null)
1266+
// If the processId is a valid int, we need to run Get-Runspace within that process
1267+
// otherwise just use the current runspace.
1268+
if (int.TryParse(processId, out int pid))
12621269
{
1263-
foreach (var p in runspaces)
1270+
1271+
// Create a remote runspace that we will invoke Get-Runspace in.
1272+
using(Runspace rs = GetRemoteRunspace(pid))
1273+
using(var ps = PowerShell.Create())
12641274
{
1265-
runspaceResponses.Add(
1266-
new GetRunspaceResponse
1267-
{
1268-
Id = p.Id,
1269-
Name = p.Name,
1270-
Availability = p.RunspaceAvailability.ToString()
1271-
});
1275+
rs.Open();
1276+
ps.Runspace = rs;
1277+
// Returns deserialized Runspaces. For simpler code, we use PSObject and rely on dynamic later.
1278+
runspaces = ps.AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace").Invoke<PSObject>();
12721279
}
12731280
}
1281+
else
1282+
{
1283+
var psCommand = new PSCommand().AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace");
1284+
var sb = new StringBuilder();
1285+
// returns (not deserialized) Runspaces. For simpler code, we use PSObject and rely on dynamic later.
1286+
runspaces = await editorSession.PowerShellContext.ExecuteCommand<PSObject>(psCommand, sb);
1287+
}
1288+
}
12741289

1275-
if (isNotCurrentProcess) {
1276-
var exitCommand = new PSCommand();
1277-
exitCommand.AddCommand("Exit-PSHostProcess");
1278-
await editorSession.PowerShellContext.ExecuteCommand(exitCommand);
1290+
var runspaceResponses = new List<GetRunspaceResponse>();
1291+
1292+
if (runspaces != null)
1293+
{
1294+
foreach (dynamic runspace in runspaces)
1295+
{
1296+
runspaceResponses.Add(
1297+
new GetRunspaceResponse
1298+
{
1299+
Id = runspace.Id,
1300+
Name = runspace.Name,
1301+
Availability = runspace.RunspaceAvailability.ToString()
1302+
});
12791303
}
12801304
}
12811305

0 commit comments

Comments
 (0)