Search code examples
c#powershellrunspace

C# Execute PowerShell Commands and Scripts using existing Powershell instance


I have written an PowerShell CmdLet with C#. Within this class I want to be able to execute other PowerShell Scripts in the same instance.

I've tried different ways, but nothing is working....

Thank you

...
[Cmdlet(VerbsCommon.Show, "XamlWindow")]
public class ShowXamlWindowPS : PSCmdlet {
    [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ValueFromPipeline = true, ParameterSetName = "FromXml")]
    public XmlDocument Xml { private get; set; }

    /// ...
    protected override void ProcessRecord() {
            XmlNodeReader reader = null;
            switch (this.ParameterSetName) {
                case "FromXml": {
                    reader =new (Xml);
                    break;
                }
                case "FromString": {
                    XmlDocument xml =new ();
                    xml.LoadXml(Xaml);
                    reader = new XmlNodeReader(xml);
                    break;
                }
                case "FromFile": {
                    XmlDocument xml = new();
                    xml.Load(this.Path.FullName);
                    reader = new XmlNodeReader(xml);
                    break;

                }
            }
            var host = (System.Management.Automation.Host.PSHost)SessionState.PSVariable.GetValue("Host");            
            PowerShell ps = PowerShell.Create();
   
            ps.Runspace = Runspace.DefaultRunspace; 
            // script to providing a new function in the PS instance
            string script = "function Test-Function1{";
            script += "Get-Process";
            script += "}";
            
            ps.AddStatement().AddScript(script).Invoke(); // does not work, pipeline is busy within the default runspace
                        
            var t = XamlReader.Load(reader);
            WriteObject(t);
        }
}

I looking for a solution to access the calling Powershell instance.


Solution

  • In a PSCmdlet-derived cmdlet implementation, SessionState.InvokeCommand.InvokeScript() allows you to invoke arbitrary commands in the current runspace (albeit in a new pipeline)

    The following simplified example in PowerShell code (which uses the automatic $PSCmdlet variable) demonstrates this:

    1..2 | ForEach-Object { 
      # This script block with a [CmdletBinding()] parameter is the equivalent
      # of a PSCmdlet-derived C# class implementation.
      & { 
        [CmdletBinding()] param()
        # Pass an arbitrary snippet of PowerShell code to .InvokeScript(),
        # which may include calls to script files.
        $PSCmdlet.SessionState.InvokeCommand.InvokeScript("Write-Output $_") 
      } 
    }