Using PowerShell 7 in the Windows PowerShell ISE

PowerShell PowerShell ISE PowerShell 7

December 19, 2019

quote Discuss this Article

PowerShell 7 is the way forward for PowerShell. The PowerShell team is no longer investing in Windows PowerShell 5.1 or the PowerShell ISE. It’s recommended that you should upgrade to the newest versions to get new features. That said, many people are still using Windows PowerShell and the PowerShell ISE. In this post, we will look at how to use the PowerShell ISE with PowerShell 7.

Different Architectures

PowerShell 7 are built on .NET Core. Windows PowerShell is built on the .NET Framework. .NET Core is the cross-platform, cooler cousin of .NET Framework. Because of the difference in architecture, you won’t be able to load .NET Core assemblies (like PowerShell 7) into a .NET Framework process like the PowerShell ISE.

To learn more about the difference between .NET Core and .NET Framework, check out this article.

Local Remoting

Since there is the issue of different architectures, we need to start a new PowerShell process to actually host the PowerShell 7 runtime. We can then use remoting to connect to the pwsh.exe process. We can use the named pipe transport of PowerShell remoting to establish a connection to the remote process. Once the connection is established, we can push the runspace to the current host and execute commands against PowerShell 7 via the ISE.

An interesting side note is that a named pipe transport is also what you are using when you use the VS Code extension for PowerShell. It also starts an out-of-process pwsh.exe to execute the scripts and invoke IntelliSense. Albeit, VS Code uses the language server protocol for the protocol while this technique uses the PowerShell Remoting protocol.

Step-by-Step

The first step is to start a PowerShell 7 pwsh.exe process. Obviously, you’ll need PowerShell 7 installed.

$Process = Start-Process PWSH -ArgumentList @("-NoExit") -PassThru -WindowStyle Hidden

Once the process is running, we will want to create a new out-of-process runspace that connects to the pwsh.exe process. You can use this simple function to establish a connection to that local process. To create a named pipe runspace, all you need to know is the process ID of the PowerShell.exe or Pwsh.exe process you want to connect to. You can then pass that process ID to the constructor for the NamedPipeConnectionInfo class.

function New-OutOfProcRunspace {
    param($ProcessId)

    $ci = New-Object -TypeName System.Management.Automation.Runspaces.NamedPipeConnectionInfo -ArgumentList @($ProcessId)
    $tt = [System.Management.Automation.Runspaces.TypeTable]::LoadDefaultTypeFiles()

    $Runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($ci, $Host, $tt)

    $Runspace.Open()
    $Runspace
}

The final step is to push the runspace variable as the current runspace within the ISE. This effectively establishes the runspace as the current runspace to use.

$Host.PushRunspace($Runspace)

Now, we can execute PowerShell commands against the pwsh.exe process. If you run $PSVersionTable, you’ll see you are executing commands against PowerShell 7.

You’ll also notice that features such as IntelliSense are running against the PowerShell 7 process. As you can see here, Split-Path does not contain the parameter -LeafBase in Windows PowerShell.

In our augmented PowerShell ISE console, you’ll see that the Split-Path cmdlet does in fact contain the -LeafBase parameter that has been added in future versions.

PowerShell ISE Add-On Command

To make it easy to pop in and out of PowerShell 7 sessions, you can add a new command to the add-on menu and provide a key binding for easy switching between the two versions of PowerShell.

$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Clear()
$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Switch to PowerShell 7", { 
        function New-OutOfProcRunspace {
            param($ProcessId)

            $ci = New-Object -TypeName System.Management.Automation.Runspaces.NamedPipeConnectionInfo -ArgumentList @($ProcessId)
            $tt = [System.Management.Automation.Runspaces.TypeTable]::LoadDefaultTypeFiles()

            $Runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($ci, $Host, $tt)

            $Runspace.Open()
            $Runspace
        }

        $PowerShell = Start-Process PWSH -ArgumentList @("-NoExit") -PassThru -WindowStyle Hidden
        $Runspace = New-OutOfProcRunspace -ProcessId $PowerShell.Id
        $Host.PushRunspace($Runspace)
}, "ALT+F5") | Out-Null

$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Switch to Windows PowerShell", { 
    $Host.PopRunspace()

    $Child = Get-CimInstance -ClassName win32_process | where {$_.ParentProcessId -eq $Pid}
    $Child | ForEach-Object { Stop-Process -Id $_.ProcessId }

}, "ALT+F6") | Out-Null

The final result is a quick switch between Windows PowerShell and PowerShell 7.