Skip to content

powerShell/getPSHostProcesses & powerShell/getRunspace #1002

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public async Task StartAsync()
.WithHandler<ReferencesHandler>()
.WithHandler<DocumentSymbolHandler>()
.WithHandler<DocumentHighlightHandler>()
.WithHandler<PSHostProcessAndRunspaceHandlers>()
.WithHandler<CodeLensHandlers>();

logger.LogInformation("Handlers added");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using OmniSharp.Extensions.Embedded.MediatR;
using OmniSharp.Extensions.JsonRpc;

namespace PowerShellEditorServices.Engine.Services.Handlers
{
[Serial, Method("powerShell/getPSHostProcesses")]
public interface IGetPSHostProcessesHandler : IJsonRpcRequestHandler<GetPSHostProcesssesParams, PSHostProcessResponse[]> { }

public class GetPSHostProcesssesParams : IRequest<PSHostProcessResponse[]> { }

public class PSHostProcessResponse
{
public string ProcessName { get; set; }

public int ProcessId { get; set; }

public string AppDomainName { get; set; }

public string MainWindowTitle { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using OmniSharp.Extensions.Embedded.MediatR;
using OmniSharp.Extensions.JsonRpc;

namespace PowerShellEditorServices.Engine.Services.Handlers
{
[Serial, Method("powerShell/getRunspace")]
public interface IGetRunspaceHandler : IJsonRpcRequestHandler<GetRunspaceParams, RunspaceResponse[]> { }

public class GetRunspaceParams : IRequest<RunspaceResponse[]>
{
public string ProcessId {get; set; }
}

public class RunspaceResponse
{
public int Id { get; set; }

public string Name { get; set; }

public string Availability { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace PowerShellEditorServices.Engine.Services.Handlers
{
public class PSHostProcessAndRunspaceHandlers : IGetPSHostProcessesHandler, IGetRunspaceHandler
{
private readonly ILogger<GetVersionHandler> _logger;

public PSHostProcessAndRunspaceHandlers(ILoggerFactory factory)
{
_logger = factory.CreateLogger<GetVersionHandler>();
}

public Task<PSHostProcessResponse[]> Handle(GetPSHostProcesssesParams request, CancellationToken cancellationToken)
{
var psHostProcesses = new List<PSHostProcessResponse>();

int processId = System.Diagnostics.Process.GetCurrentProcess().Id;

using (var pwsh = PowerShell.Create())
{
pwsh.AddCommand("Get-PSHostProcessInfo")
.AddCommand("Where-Object")
.AddParameter("Property", "ProcessId")
.AddParameter("NE")
.AddParameter("Value", processId.ToString());

var processes = pwsh.Invoke<PSObject>();

if (processes != null)
{
foreach (dynamic p in processes)
{
psHostProcesses.Add(
new PSHostProcessResponse
{
ProcessName = p.ProcessName,
ProcessId = p.ProcessId,
AppDomainName = p.AppDomainName,
MainWindowTitle = p.MainWindowTitle
});
}
}
}

return Task.FromResult(psHostProcesses.ToArray());
}

public Task<RunspaceResponse[]> Handle(GetRunspaceParams request, CancellationToken cancellationToken)
{
IEnumerable<PSObject> runspaces = null;

if (request.ProcessId == null) {
request.ProcessId = "current";
}

// 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(request.ProcessId, out int pid))
{
// Create a remote runspace that we will invoke Get-Runspace in.
using(var rs = RunspaceFactory.CreateRunspace(new NamedPipeConnectionInfo(pid)))
using(var ps = PowerShell.Create())
{
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<PSObject>();
}
}
else
{
// TODO: Bring back
// 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.ExecuteCommandAsync<PSObject>(psCommand, sb);
}

var runspaceResponses = new List<RunspaceResponse>();

if (runspaces != null)
{
foreach (dynamic runspace in runspaces)
{
runspaceResponses.Add(
new RunspaceResponse
{
Id = runspace.Id,
Name = runspace.Name,
Availability = runspace.RunspaceAvailability.ToString()
});
}
}

return Task.FromResult(runspaceResponses.ToArray());
}
}
}
24 changes: 24 additions & 0 deletions test/Pester/EditorServices.Integration.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,30 @@ Write-Host 'Goodbye'
$response.Result[1].Range.End.Character | Should -BeExactly 10
}

It "Can handle a powerShell/getPSHostProcesses request" {
$request = Send-LspRequest -Client $client -Method "powerShell/getPSHostProcesses"
$response = Get-LspResponse -Client $client -Id $request.Id
$response.Result | Should -Not -BeNullOrEmpty

$processInfos = @(Get-PSHostProcessInfo)

# We need to subtract one because this message fiilters out the "current" process.
$processInfos.Count - 1 | Should -BeExactly $response.Result.Count

$response.Result[0].processName |
Should -MatchExactly -RegularExpression "((pwsh)|(powershell))(.exe)*"
}

It "Can handle a powerShell/getRunspace request" {
$processInfos = Get-PSHostProcessInfo

$request = Send-LspGetRunspaceRequest -Client $client -ProcessId $processInfos[0].ProcessId
$response = Get-LspResponse -Client $client -Id $request.Id

$response.Result | Should -Not -BeNullOrEmpty
$response.Result.Count | Should -BeGreaterThan 0
}

It "Can handle a textDocument/codeLens Pester request" {
$filePath = New-TestFile -FileName ("$([System.IO.Path]::GetRandomFileName()).Tests.ps1") -Script '
Describe "DescribeName" {
Expand Down
1 change: 1 addition & 0 deletions tools/PsesPsClient/PsesPsClient.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ FunctionsToExport = @(
'Send-LspDocumentSymbolRequest',
'Send-LspDocumentHighlightRequest',
'Send-LspReferencesRequest',
'Send-LspGetRunspaceRequest',
'Send-LspCodeLensRequest',
'Send-LspCodeLensResolveRequest',
'Send-LspShutdownRequest',
Expand Down
20 changes: 19 additions & 1 deletion tools/PsesPsClient/PsesPsClient.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ function Send-LspDocumentHighlightRequest
return Send-LspRequest -Client $Client -Method 'textDocument/documentHighlight' -Parameters $documentHighlightParams
}

function Send-LspCodeLensRequest
function Send-LspGetRunspaceRequest
{
[OutputType([PsesPsClient.LspRequest])]
param(
Expand All @@ -496,6 +496,24 @@ function Send-LspCodeLensRequest
$Client,

[Parameter(Mandatory)]
[int]
$ProcessId
)

$params = [PowerShellEditorServices.Engine.Services.Handlers.GetRunspaceParams]@{
ProcessId = $ProcessId
}
return Send-LspRequest -Client $Client -Method 'powerShell/getRunspace' -Parameters $params
}

function Send-LspCodeLensRequest
{
[OutputType([PsesPsClient.LspRequest])]
param(
[Parameter(Position = 0, Mandatory)]
[PsesPsClient.PsesLspClient]
$Client,

[string]
$Uri
)
Expand Down