Search code examples
c#.netpowershellsystem.management

Create Runspaces in separate powershell shells with System.Management


I'm trying to run different PowerShell commands using .NET's System.Management library but I see that every time the runspaces share the same shell. To be more specific, if I open one PowerShell window on my OS and I run the following command:

Get-Runspace

I get the following output:

Id Name            ComputerName    Type          State         Availability
-- ----            ------------    ----          -----         ------------
 1 Runspace1       localhost       Local         Opened        Busy

If I now open another window and run the same command again I get the same output, from which I understand that I have different runspaces with the same name in different sessions. Correct me here if I'm wrong!

Now if I run the following code that runs the Get-Runspace command twice:

static void Main(string[] args)
{
    for (int i = 1; i <= 2; i++)
    {
        PowerShell ps = PowerShell.Create();
        ps.AddCommand("Get-Runspace");
        var result = ps.Invoke();

        foreach (PSObject elem in result)
        {
            Runspace runspace = (Runspace)elem.BaseObject;
            Console.WriteLine(runspace.Name);
        }
        Console.WriteLine();
    }
    Console.ReadLine();
}

I get the following output

Runspace1

Runspace1
Runspace2

which means that Runspace1 is visible from Runspace2 so they share the same shell.

I would instead expect the following output, in which the two runspaces are in separate shells:

Runspace1

Runspace1

Is this behaviour possible or my logic is broken? Note that closing the Runspace will not be enough to solve my problem.


Solution

  • If by "same shell" you mean "the same process", then yes.

    A Runspace is necessarily restricted to a single application domain, and application domains in .NET can't span multiple processes, so the relationship is:

    +---------------------------------+
    | Process                         |
    | +-----------------------------+ |
    | | AppDomain                   | |
    | | +-----------+ +-----------+ | |
    | | | Runspace1 | | Runspace2 | | |
    | | +-----------+ +-----------+ | |
    | +-----------------------------+ |
    +---------------------------------+
    

    Get-Runspace just enumerates the runspaces in the current host application domain.


    You can isolate runspaces in a separate process if need be, use RunspaceFactory.CreateOutOfProcessRunspace():

    using (PowerShell ps = PowerShell.Create())
    {
        var isolatedShell = new PowerShellProcessInstance();
        var isolatedRunspace = RunspaceFactory.CreateOutOfProcessRunspace(TypeTable.LoadDefaultTypeFiles(), isolatedShell);
    
        ps.Runspace = isolatedRunspace;
    
        ps.AddCommand("Get-Runspace");
        var result = ps.Invoke();
    
        foreach (PSObject elem in result)
        {
            Runspace runspace = (Runspace)elem.BaseObject;
            Console.WriteLine(runspace.Name);
        }
        Console.WriteLine();
    }