Skip to content

Instantly share code, notes, and snippets.

@santisq
Last active September 13, 2023 00:47
Show Gist options
  • Save santisq/93fd0f5a4ff9be5c542d1cbceb50aed0 to your computer and use it in GitHub Desktop.
Save santisq/93fd0f5a4ff9be5c542d1cbceb50aed0 to your computer and use it in GitHub Desktop.
using namespace System.Diagnostics
using namespace System.Management.Automation.Runspaces
using namespace System.System.Diagnostics
using namespace System.Management.Automation
using namespace System.Management.Automation.Host
class PwshPipe : IDisposable {
[Process] $pwshProcess
[NamedPipeConnectionInfo] $pipeinfo
[powershell] $pwsh
[runspace] $runspace
[bool] $disposed
PwshPipe([PSHost] $pshost) {
# ideally, this should be a Job object instead so if the parent process is terminated,
# this process also dies with it.
# https://learn.microsoft.com/en-us/windows/win32/procthread/job-objects
$this.pwshProcess = [Process]::Start(
[ProcessStartInfo]@{
CreateNoWindow = $true
FileName = 'pwsh.exe'
WindowStyle = [ProcessWindowStyle]::Hidden
UseShellExecute = $true
LoadUserProfile = $false
Arguments = '-NoProfile'
})
$this.pipeinfo = [NamedPipeConnectionInfo]::new($this.pwshProcess.Id)
$this.runspace = [runspacefactory]::CreateRunspace($pshost, $this.pipeinfo)
$this.runspace.Open()
$this.PSObject.Members.Add([psscriptproperty]::new(
'IsAlive',
{ -not $this.disposed -and -not $this.pwshProcess.HasExited }))
}
[void] Invoke(
[scriptblock] $scriptblock,
[object[]] $arguments,
[PSCmdlet] $cmdlet
) {
# ideally this should be handled with extension methods but pwsh class support sucks..
$cmdlet.WriteObject(
$this.AddScript($scriptblock).
AddArguments($arguments).
pwsh.Invoke(),
$true)
if ($this.pwsh.HadErrors) {
foreach ($record in $this.pwsh.Streams.Error) {
$cmdlet.WriteError($record)
}
}
}
[PwshPipe] AddScript([scriptblock] $scriptblock) {
if ($this.pwsh) {
$this.pwsh.Dispose()
}
$this.pwsh = [powershell]::Create().AddScript($scriptblock, $false)
$this.pwsh.Runspace = $this.runspace
return $this
}
[PwshPipe] AddArguments([object[]] $arguments) {
foreach ($argument in $arguments) {
$this.pwsh = $this.pwsh.AddArgument($argument)
}
return $this
}
hidden [void] Dispose([bool] $disposing) {
if (-not $this.disposed -and $disposing) {
if ($this.pwsh) {
$this.pwsh.Dispose()
}
if ($this.runspace) {
$this.runspace.Dispose()
}
if ($this.pwshProcess) {
$this.pwshProcess.Kill()
}
$this.disposed = $true
}
}
[void] Dispose() {
$this.Dispose($true)
[GC]::SuppressFinalize($this)
}
}
function Invoke-Pwsh7 {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[scriptblock] $ScriptBlock,
[Parameter()]
[object[]] $ArgumentList
)
end {
if (-not $script:pipe.IsAlive) {
$script:pipe = [PwshPipe]::new($Host)
}
$script:pipe.Invoke($ScriptBlock, $ArgumentList, $PSCmdlet)
}
}
function Clear-Pipe {
$script:pipe.Dispose()
}
$ExecutionContext.SessionState.Module.OnRemove = {
if ($script:pipe.IsAlive) {
$script:pipe.Dispose()
}
}
Import-Module (Join-Path $PSScriptRoot invokepwsh7pseudomodule.psm1) -Force
Invoke-Pwsh7 { $PSVersionTable }
Invoke-Pwsh7 {
0..10 | ForEach-Object -Parallel {
Start-Sleep -Milliseconds 200
$_
}
}
Invoke-Pwsh7 {
Write-Host 'testing streams'
Write-Verbose 'testing streams' -Verbose
Write-Error 'testing streams'
Write-Warning 'testing streams'
}
Invoke-Pwsh7 {
param($foo, $bar, $baz)
$PSBoundParameters
} -ArgumentList hello, world, 123
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment