C# Runspace Powershell (Interactive)

2019-04-30 21:33发布

问题:

I am trying with C# to execute a Powershell file with paramters in a runspace. Unfortunately i get the following output:

A command that prompts the user failed because the host program or the command type does not support user interaction. Try a host program that supports user interaction, such as the Windows PowerShell Console or Windows PowerShell ISE, and remove prompt-related commands from command types that do not support user interaction, such as Windows PowerShell workflows.

What could i do?

Current c# code. this needs to execute commands that will be in a PS file and needs to return a json string.

public string ExecuteCommandDirect(int psId, string psMaster, string psFile)
{
    String FullPsFilePath = @"C:\CloudPS\" + psFile + ".ps1";

    String PsParameters = FullPsFilePath + " -psId " + psId + " -psMaster " + psMaster + " -NonInteractive";

    // Create Powershell Runspace
    Runspace runspace = RunspaceFactory.CreateRunspace();

    runspace.Open();

    // Create pipeline and add commands
    Pipeline pipeline = runspace.CreatePipeline();
    pipeline.Commands.AddScript(PsParameters);

    // Execute Script
    Collection<PSObject> results = new Collection<PSObject>();
    try
    {
        results = pipeline.Invoke();
    }
    catch (Exception ex)
    {
        results.Add(new PSObject((object)ex.Message));
    }

    // Close runspace
    runspace.Close();

    //Script results to string
    StringBuilder stringBuilder = new StringBuilder();
    foreach (PSObject obj in results)
    {
        Debug.WriteLine(obj);
        stringBuilder.AppendLine(obj.ToString());
    }

    return stringBuilder.ToString();

}

PS Code:

param([int]$psId = 0, [string]$psMaster = 'localhost');

$date = Get-Date -Format 'h:m:s' | ConvertTo-Json;

Write-Host $date;

exit;

回答1:

If you need to run a script that uses non-critical cmdlets like write-host that require the presence of a fully-fledged pshost, you can add "fake" functions to the runspace that either do nothing or log to a text file (or anything else you want.) See my other answer here:

How can I execute scripts in a code created powershell shell that has Write-Host commands in it?



回答2:

Use Write-Output instead of Write-Host



回答3:

I have solved this problem myself for Read-Host and Write-Host, by overriding those two cmdlets. In my solution, I silently drop anything sent to Write-Host, but I need to implement Read-Host. This starts a new powershell process and gets the results back.

        string readHostOverride = @"
            function Read-Host {
                param(
                    [string]$Prompt
                )



                $StartInfo = New-Object System.Diagnostics.ProcessStartInfo
                #$StartInfo.CreateNoWindow = $true 
                $StartInfo.UseShellExecute = $false 
                $StartInfo.RedirectStandardOutput = $true 
                $StartInfo.RedirectStandardError = $true 
                $StartInfo.FileName = 'powershell.exe' 
                $StartInfo.Arguments = @(""-Command"", ""Read-Host"", ""-Prompt"", ""'$Prompt'"")
                $Process = New-Object System.Diagnostics.Process
                $Process.StartInfo = $StartInfo
                [void]$Process.Start()
                $Output = $Process.StandardOutput.ReadToEnd() 
                $Process.WaitForExit() 
                return $Output.TrimEnd()
            }
        ";

        string writeHostOverride = @"
            function Write-Host {
            }
        ";

        Run(readHostOverride);
        Run(writeHostOverride);

Run() is, unsurprisingly, my method for executing powershell.

You could easily extend this to implement Write-Host too; let me know if you have any difficulty.

To commenters suggesting switching to Write-Output instead of Write-Host, this is NOT a solution for many use cases. You may not want your user notifications going into your output stream, for example.

Should you need to use the original cmdlets, use this:

Microsoft.PowerShell.Utility\Read-Host -Prompt "Enter your input"