//
// Copyright (c) 2009 Microsoft Corporation. All rights reserved.
//
// DISCLAIMER OF WARRANTY: The software is licensed “as-is.” You
// bear the risk of using it. Microsoft gives no express warranties,
// guarantees or conditions. You may have additional consumer rights
// under your local laws which this agreement cannot change. To the extent
// permitted under your local laws, Microsoft excludes the implied warranties
// of merchantability, fitness for a particular purpose and non-infringement.
namespace Microsoft.Samples.PowerShell.Host
{
using System;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using PowerShell = System.Management.Automation.PowerShell;
///
/// Simple PowerShell interactive console host listener implementation. This class
/// implements a basic read-evaluate-print loop or 'listener' allowing you to
/// interactively work with the PowerShell engine.
///
internal class PSListenerConsoleSample
{
///
/// Indicator to tell the host application that it should exit.
///
private bool shouldExit;
///
/// The exit code that the host application will use to exit.
///
private int exitCode;
///
/// Holds a reference to the PSHost object for this interpreter.
///
private MyHost myHost;
///
/// Holds a reference to the runspace for this interpeter.
///
private Runspace myRunSpace;
///
/// Holds a reference to the currently executing pipeline so that
/// it can be stopped by the control-C handler.
///
private PowerShell currentPowerShell;
///
/// Used to serialize access to instance data.
///
private object instanceLock = new object();
///
/// Gets or sets a value indicating whether the host applcation
/// should exit.
///
public bool ShouldExit
{
get { return this.shouldExit; }
set { this.shouldExit = value; }
}
///
/// Gets or sets the exit code that the host application will use
/// when exiting.
///
public int ExitCode
{
get { return this.exitCode; }
set { this.exitCode = value; }
}
///
/// Creates and initiates the listener.
///
/// The parameter is not used
private static void Main(string[] args)
{
// Display the welcome message.
Console.Title = "Windows PowerShell Console Host Application Sample";
ConsoleColor oldFg = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(" Windows PowerShell Console Host Interactive Sample");
Console.WriteLine(" ==================================================");
Console.WriteLine(string.Empty);
Console.WriteLine("This is an example of a simple interactive console host that uses ");
Console.WriteLine("the Windows PowerShell engine to interpret commands.");
Console.WriteLine("Type 'exit' to exit.");
Console.WriteLine(string.Empty);
Console.ForegroundColor = oldFg;
// Create the listener and run it - this never returns...
PSListenerConsoleSample listener = new PSListenerConsoleSample();
listener.Run();
}
///
/// Create this instance of the console listener.
///
private PSListenerConsoleSample()
{
// Create the host and runspace instances for this interpreter. Note that
// this application doesn't support console files so only the default snapins
// will be available.
this.myHost = new MyHost(this);
this.myRunSpace = RunspaceFactory.CreateRunspace(this.myHost);
this.myRunSpace.Open();
// Create a PowerShell object that will be used to execute the commands
// to create $profile and load the profiles.
lock (this.instanceLock)
{
this.currentPowerShell = PowerShell.Create();
}
try
{
this.currentPowerShell.Runspace = this.myRunSpace;
PSCommand[] profileCommands = Microsoft.Samples.PowerShell.Host.HostUtilities.GetProfileCommands("SampleHost04");
foreach (PSCommand command in profileCommands)
{
this.currentPowerShell.Commands = command;
this.currentPowerShell.Invoke();
}
}
finally
{
// Dispose of the pipeline line and set it to null, locked because currentPowerShell
// may be accessed by the ctrl-C handler...
lock (this.instanceLock)
{
this.currentPowerShell.Dispose();
this.currentPowerShell = null;
}
}
}
///
/// A helper class that builds and executes a pipeline that writes to the
/// default output path. Any exceptions that are thrown are just passed to
/// the caller. Since all output goes to the default outter, this method()
/// won't return anything.
///
/// The script to run
/// Any input arguments to pass to the script. If null
/// then nothing is passed in.
private void executeHelper(string cmd, object input)
{
// Just ignore empty command lines.
if (String.IsNullOrEmpty(cmd))
{
return;
}
// Create the pipeline object and make it available
// to the ctrl-C handle through the currentPowerShell instance
// variable
lock (this.instanceLock)
{
this.currentPowerShell = PowerShell.Create();
}
this.currentPowerShell.Runspace = this.myRunSpace;
// Create a pipeline for this execution - place the result in the currentPowerShell
// instance variable so it is available to be stopped.
try
{
this.currentPowerShell.AddScript(cmd);
// Now add the default outputter to the end of the pipe and indicate
// that it should handle both output and errors from the previous
// commands. This will result in the output being written using the PSHost
// and PSHostUserInterface classes instead of returning objects to the hosting
// application.
this.currentPowerShell.AddCommand("out-default");
this.currentPowerShell.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
// If there was any input specified, pass it in, otherwise just
// execute the pipeline...
if (input != null)
{
this.currentPowerShell.Invoke(new object[] { input });
}
else
{
this.currentPowerShell.Invoke();
}
}
finally
{
// Dispose of the pipeline line and set it to null, locked because currentPowerShell
// may be accessed by the ctrl-C handler...
lock (this.instanceLock)
{
this.currentPowerShell.Dispose();
this.currentPowerShell = null;
}
}
}
///
/// An exception occurred that we want to display
/// using the display formatter. To do this we run
/// a second pipeline passing in the error record.
/// The runtime will bind this to the $input variable
/// which is why $input is being piped to out-string.
/// We then call WriteErrorLine to make sure the error
/// gets displayed in the correct error color.
///
/// The exception to display
private void ReportException(Exception e)
{
if (e != null)
{
object error;
IContainsErrorRecord icer = e as IContainsErrorRecord;
if (icer != null)
{
error = icer.ErrorRecord;
}
else
{
error = (object)new ErrorRecord(e, "Host.ReportException", ErrorCategory.NotSpecified, null);
}
lock (this.instanceLock)
{
this.currentPowerShell = PowerShell.Create();
}
this.currentPowerShell.Runspace = this.myRunSpace;
try
{
this.currentPowerShell.AddScript("$input").AddCommand("out-string");
// Don't merge errors, this function will swallow errors.
Collection result;
PSDataCollection