C# Runspace Powershell (Interactive)
Asked Answered
S

3

5

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;
Spherics answered 21/9, 2014 at 20:33 Comment(5)
Can you post the code you're using?Manifold
Try to remove Write-Host, leave just $date. exit is not needed, too.Byrnes
[code]$date = Get-Date -Format 'h:m:s' | ConvertTo-Json; $date;[/code] Gives the same output.Spherics
If you want to use cmdlets that require a host you need to create a runspace and pass a PSHost instance. Cmdlets like Write-Host or Read-Host actually require that you also implement PSHostUserInterface that is returned from a property on the PSHost instance. AFAIK there are no default public implementations of these abstract classes. You would have to write them yourself, however there are some examples available on MSDN.Unclassical
For an example of how to implement them (or to just use the ps1 -> exe wrapper) see this blog post - rkeithhill.wordpress.com/2010/09/21/make-ps1exewrapperShu
C
5

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?

Coltin answered 22/9, 2014 at 6:58 Comment(0)
P
8

Use Write-Output instead of Write-Host

Primm answered 11/8, 2016 at 19:39 Comment(2)
I googled, I clicked, I scrolled and saw this answer. I switched from -Host to -Output and stopped getting the error message posted by the OP. This answer is not only correct but also concise and helpful.Rotate
Worked perfect.Lamas
C
5

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?

Coltin answered 22/9, 2014 at 6:58 Comment(0)
C
1

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"
Communicant answered 23/8, 2016 at 13:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.