//
// Copyright (c) 2006 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
using System;
using System.Collections.Generic;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
namespace Microsoft.Samples.PowerShell.Host
{
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.
///
class PSListenerConsoleSample
{
///
/// Property that the PSHost implementation will use to tell the host
/// application that it should exit.
///
public bool ShouldExit
{
get { return shouldExit; }
set { shouldExit = value; }
}
private bool shouldExit;
///
/// Promperty that the PSHost implementation will use to tell the host
/// application what code to use when exiting.
///
public int ExitCode
{
get { return exitCode; }
set { exitCode = value; }
}
private int exitCode;
///
/// Holds the instance of the PSHost implementation for this interpreter.
///
private MyHost myHost;
///
/// Holds the runspace for this interpeter.
///
private Runspace myRunSpace;
///
/// Holds a reference to the currently executing pipeline so it can be
/// stopped by the control-C handler.
///
private PowerShell currentPowerShell;
///
/// Used to serialize access to instance data...
///
private object instanceLock = new object();
///
/// Create this instance of the console listener.
///
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.
myHost = new MyHost(this);
myRunSpace = RunspaceFactory.CreateRunspace(myHost);
myRunSpace.Open();
}
///
/// 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.
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 (instanceLock)
{
currentPowerShell = PowerShell.Create();
}
currentPowerShell.Runspace = myRunSpace;
// Create a pipeline for this execution - place the result in the currentPowerShell
// instance variable so it is available to be stopped.
try
{
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.
currentPowerShell.AddCommand("out-default");
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)
{
currentPowerShell.Invoke(new object[] { input });
}
else
{
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 (instanceLock)
{
currentPowerShell.Dispose();
currentPowerShell = null;
}
}
}
///
/// Basic script execution routine - any runtime exceptions are
/// caught and passed back into the engine to display.
///
///
void Execute(string cmd)
{
try
{
// execute the command with no input...
executeHelper(cmd, null);
}
catch (RuntimeException rte)
{
// 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-default
executeHelper("$input | out-default", rte.ErrorRecord);
}
}
///
/// Method used to handle control-C's from the user. It calls the
/// pipeline Stop() method to stop execution. If any exceptions occur
/// they are printed to the console but otherwise ignored.
///
/// See ConsoleCancelEventHandler documentation
/// See ConsoleCancelEventHandler documentation
void HandleControlC(object sender, ConsoleCancelEventArgs e)
{
try
{
lock (instanceLock)
{
if (currentPowerShell != null && currentPowerShell.InvocationStateInfo.State == PSInvocationState.Running)
currentPowerShell.Stop();
}
e.Cancel = true;
}
catch (Exception exception)
{
this.myHost.UI.WriteErrorLine(exception.ToString());
}
}
///
/// Implements the basic listener loop. It sets up the ctrl-C handler, then
/// reads a command from the user, executes it and repeats until the ShouldExit
/// flag is set.
///
private void Run()
{
// Set up the control-C handler.
Console.CancelKeyPress += new ConsoleCancelEventHandler(HandleControlC);
Console.TreatControlCAsInput = false;
// loop reading commands to execute until ShouldExit is set by
// the user calling "exit".
while (!ShouldExit)
{
myHost.UI.Write(ConsoleColor.Cyan, ConsoleColor.Black, "\nPSConsoleSample: ");
string cmd = Console.ReadLine();
Execute(cmd);
}
// and exit with the desired exit code that was set by exit command.
// This is set in the host by the MyHost.SetShouldExit() implementation.
Environment.Exit(ExitCode);
}
///
/// Creates and initiates the listener instance...
///
/// Ignored for now...
static void Main(string[] args)
{
// Display the welcome message...
Console.Title = "PowerShell Console Host Sample Application";
ConsoleColor oldFg = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(" PowerShell Console Host Interactive Sample");
Console.WriteLine(" =====================================");
Console.WriteLine("");
Console.WriteLine("This is an example of a simple interactive console host using the PowerShell");
Console.WriteLine("engine to interpret commands. Type 'exit' to exit.");
Console.WriteLine("");
Console.ForegroundColor = oldFg;
// Create the listener and run it - this never returns...
PSListenerConsoleSample listener = new PSListenerConsoleSample();
listener.Run();
}
}
}