namenlos
namenlos

Reputation: 5181

How to capture a Powershell CmdLet's verbose output when the CmdLet is programmatically Invoked from C#

BACKGROUND

REFERENCE

THE SOURCE CODE

COMMENTS

UPDATE ON 2009-07-20

Here is is the source code based on the answer below.

Some things are still not clear to me: * How to call the "Get-Colors" cmdlet (ideally without having to pass it as a string to the ps objet) * How to get the verbose output as it is generated instead of getting an collection of them at the end.

    using System;
    using System.Management.Automation;   

    namespace DemoCmdLet1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var ps = System.Management.Automation.PowerShell.Create();

                ps.Commands.AddScript("$verbosepreference='continue'; write-verbose 42");

                foreach ( var i in ps.Invoke<string>())
                {
                   Console.WriteLine("normal output: {0}" , i );   
                }
                foreach (var i in ps.Streams.Verbose)
                {
                    Console.WriteLine("verbose output: {0}" , i);
                }

            }
        }

        [Cmdlet("Get", "Colors")]
        public class GetColorsCommand : Cmdlet
        {
            protected override void ProcessRecord()
            {
                this.WriteObject("Red");
                this.WriteVerbose("r");
                this.WriteObject("Green");
                this.WriteVerbose("g");
                this.WriteObject("Blue");
                this.WriteVerbose("b");

            }

        }
    }

The code above generates this output:

d:\DemoCmdLet1\DemoCmdLet1>bin\Debug\DemoCmdLet1.exe
verbose output: 42

UPDATE ON 2010-01-16

by using the Powershell class (found in System.Management.Automation but only in the version of the assembly that comes with the powershell 2.0 SDK, not what comes out-of-the-box on Windows 7) I can programmatically call the cmdlet and get the verbose output. The remaining part is to actually add a custom cmdlet to that powershell instance - because that was my original goal - to unit test my cmdlets not those that come with powershell.

class Program
{
    static void Main(string[] args)
    {
        var ps = System.Management.Automation.PowerShell.Create();
        ps.AddCommand("Get-Process");
        ps.AddParameter("Verbose");
        ps.Streams.Verbose.DataAdded += Verbose_DataAdded;
        foreach (PSObject result in ps.Invoke())
        {
            Console.WriteLine(
                    "output: {0,-24}{1}",
                    result.Members["ProcessName"].Value,
                    result.Members["Id"].Value);
        } 
        Console.ReadKey();
    }

    static void Verbose_DataAdded(object sender, DataAddedEventArgs e)
    {
        Console.WriteLine( "verbose output: {0}", e.Index);
    }
}


[Cmdlet("Get", "Colors")]
public class GetColorsCommand : Cmdlet
{
    protected override void ProcessRecord()
    {
        this.WriteObject("Hello");
        this.WriteVerbose("World");
    }
}

Upvotes: 9

Views: 12625

Answers (3)

carrvo
carrvo

Reputation: 638

First off, if you are unit testing cmdlets, likely Pester is a better (and easier) option.

As per your many updates, all you are likely missing at this point is a strongly typed approach to reference the C# cmdlet

ps.AddCommand(new CmdletInfo("Get-MyCS", typeof(GetMyCS)));

DISCLAIMER: I know this works for PowerShell 5.0, but don't have experience with the older PowerShell 2.0.

Upvotes: 0

Varun
Varun

Reputation: 763

1.        string scriptFile = "Test.ps1";
2.        using (PowerShell ps = PowerShell.Create())
3.        {
4.          const string getverbose = "$verbosepreference='continue'"; 
5.          ps.AddScript(string.Format(getverbose));
6.          ps.Invoke();
7.          ps.Commands.Clear();
8.          ps.AddScript(@".\" + scriptFile);
9.          ps.Invoke();
10.         foreach (var v in ps.Streams.Verbose)
11.         {
12.               Console.WriteLine(v.Message);
13.         }
14.       }

Important lines are line 5 and 6. This basically set the $verbosepreference for the session and for upcoming new commands and scripts.

Upvotes: 0

x0n
x0n

Reputation: 52410

  • Verbose output is not actually output unless $VerbosePreference is set at least to "Continue."
  • Use the PowerShell type to run your cmdlet, and read VerboseRecord instances from the Streams.Verbose propery

Example in powershell script:

ps> $ps = [powershell]::create()
ps> $ps.Commands.AddScript("`$verbosepreference='continue'; write-verbose 42")
ps> $ps.invoke()
ps> $ps.streams.verbose
Message   InvocationInfo                          PipelineIterationInfo
-------   --------------                          ---------------------
42        System.Management.Automation.Invocat... {0, 0}

This should be easy to translate into C#.

Upvotes: 10

Related Questions