5
votes

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;
3
Can you post the code you're using? - DanM7
Hi, i have added the code. - user2702653
Try to remove Write-Host, leave just $date. exit is not needed, too. - Roman Kuzmin
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. - Mike Zboray
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-ps1exewrapper - Keith Hill

3 Answers

8
votes

Use Write-Output instead of Write-Host

5
votes

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?

1
votes

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"