Click to Rate and Give Feedback
MSDN
MSDN Library
Creating a Console Application that Has a Basic PSHost Implementation

This section describes how to write a hosting application that supports a custom host interface (referred to as the "host"). This type of application implements the PSHost class to define the custom host. This host does not define its own user interface; instead, it uses the default.

For illustration purposes, the discussion uses a sample application called Host01, with a custom host called MyHost. This application executes a script for exiting and informs the custom host when it should exit.

Topics in this section include the following:

Defining Properties to Support the Custom Host

A hosting application should define properties that reflect the parameters for the custom host. This application defines two properties. The first property is a ShouldExit property that the custom host uses to tell the application when it should exit. The second property is an ExitCode property that the custom host uses to identify the exit code for the application to issue when exiting.

C#
public bool ShouldExit
{
  get { return shouldExit; }
  set { shouldExit = value; }
}
private bool shouldExit;

/// <summary>
/// Define the property that the PSHost implementation will
/// use to tell the host application what exit code to use
/// when exiting.
/// </summary>
public int ExitCode
{
  get { return exitCode; }
  set { exitCode = value; }
}
private int exitCode;

Creating the Custom Host Class

This hosting application implements a MyHost class that defines the functionality of the custom host. The MyHost class is derived from the PSHost class; however, it does not implement all the application programming interfaces (APIs) of the PSHost class. If an API is not implemented, a NotImplementedException exception is returned.

Declare the Host Class

The following code shows a typical class declaration for a custom host. The constructor keeps a reference to the hosting application so that it can be notified when it should exit.

C#
public MyHost(Host01 program)
{
  this.program = program;
}
private Host01 program;

Defining Host Name and Version

This host implements the read-only Name property to represent its user-friendly name. This name is used, for example, by the hosting application's runspace to determine the startup scripts to run at initialization. The format of the host name is undefined, but a short, simple string is recommended.

Here is the implementation of the Name property from MyHost:

C#
public override string Name
{
  get { return "MySampleConsoleHostImplementation"; }
}

This host implements the read-only Version property to represent the version number of MyHost. This number should remain unique for each build. Typically, it should match the version resource in the hosting application.

Here is the implementation of the Version property from MyHost:

C#
public override Version Version
{
  get {return new Version(1,0,0,0); }
}

Defining Host Culture Information

The culture of the host indicates globalization information that callers can use to perform conversions, comparisons, and formatting. This custom host implements the read-only CurrentCulture and CurrentUICulture properties to represent culture information for the host and the host user interface, respectively.

Both properties retrieve CultureInfo objects indicating culture details, such as culture name and language, as well as information about common operations, such as formatting dates. The runspace of the hosting application uses the information from the culture properties to set the current culture on new threads, and the Resource Manager uses the information to look up culture-specific resources at run time.

Here are the implementations of these properties from MyHost. Each property retrieves culture information from the thread that created the MyHost object.

C#
public override System.Globalization.CultureInfo CurrentCulture
{
  get { return originalCultureInfo; }
}
private CultureInfo originalCultureInfo = 
System.Threading.Thread.CurrentThread.CurrentCulture;
C#
public override System.Globalization.CultureInfo CurrentUICulture
{
  get { return originalUICultureInfo; }
}
private CultureInfo originalUICultureInfo =
System.Threading.Thread.CurrentThread.CurrentUICulture;

Defining an Instance Identifier

This host implements the InstanceId property to define a unique identifier for each instance of MyHost. The Windows PowerShell runtime will always maintain a one-to-one relationship between a host instance and the current Runspace object.

Here is the implementation of the Version property from MyHost:

C#
public override Guid InstanceId
{
  get { return myId; }
}

Defining Private Data

Hosts can override the read-only PrivateData property to define any private data that it wants to expose to callers as a PSObject object. If the host does not define private data (which is the default behavior), this property has a value of null. MyHost does not override this property; instead, it uses the default.

Things To Remember About Implementing Private Data

If the hosting application is using an out-of-process runspace, the value of the PrivateData property is serialized when crossing the process boundary. In this case, the BaseObject property will be null.

If the hosting application is using an in-process runspace, the BaseObject property can have a non-null value. The base object is responsible for ensuring its own thread safety and re-entrance safety. Be aware that threads accessing that object might not necessarily be the same from one access to the next.

The host implementation of the PrivateData.get function should always retrieve a unique PSObject instance so that changes to the object state will not be reflected across processes. For example, if a cmdlet reads PrivateData and then changes the state of the retrieved object, that change will not be visible to a host that is in another process.

Identify the User Interface

This host implements the UI property to define its user interface. The type of this property is an instance of the PSHostUserInterface base class. The value of the property is null if the host does not support a user interface.

Here is the implementation of the UI property from MyHost. It retrieves null because this host uses no user interface.

C#
public override PSHostUserInterface UI
{
  get { return null; }
}

Notify Application to Shut Down

This host implements the SetShouldExit method to inform the hosting application that it should shut down at its next opportunity. The runspace of the hosting application calls this method with an internal flag indicating that it is shutting down and that the application should be informed.

Here is the implementation of the SetShouldExit method from MyHost. The input is the exit code that the hosting application should use when exiting the process. This corresponds to the ExitCode parameter for the host.

C#
public override void SetShouldExit(int exitCode)
{
  program.ShouldExit = true;
  program.ExitCode = exitCode;
}

Support Nested Prompts

A nested prompt loop is a cycle of prompt, input, and execute operations. It is supported to allow a pipeline to be suspended at prompts so that the user can execute ancillary command lines to verify responses to prompts. When the user chooses a "nested prompt" option for a cmdlet, the runspace will begin a nested prompt loop. There are no particular restrictions on the commands that can be run in a nested prompt loop.

The host implements the EnterNestedPrompt method to start a nested prompt loop, presenting a special prompt to the user. When called by the runspace, this method interrupts the currently running pipeline to start a new loop. If you are implementing a noninteractive host, this method can throw a NotImplemented exception.

To shut down a nested prompt loop when the user types "exit" at the prompt for the nested loop, the host implements the ExitNestedPrompt method. Called by the runspace of the hosting application, this method exits a nested loop and resumes the pipeline that was interrupted by the call to EnterNestedPrompt.

Here are the EnterNestedPrompt and ExitNestedPrompt methods from MyHost. Notice that these methods are not implemented by MyHost, and they throw a NotImplementedException exception.

C#
public override void EnterNestedPrompt()
{
  throw new NotImplementedException(
        "The method or operation is not implemented.");
}
C#
public override void ExitNestedPrompt()
{
  throw new NotImplementedException(
        "The method or operation is not implemented.");
}

Support External Console Applications

As described in How Windows PowerShell Works, the runspace can execute a variety of command types in a pipeline, such as cmdlets, scripts, and applications. Your host needs to be notified by the runspace when an external console application (for example, Ping.exe) is beginning to execute. Such an application reads from stdin, writes to stdout and stderr, or uses any of the Win32 console APIs.

To handle execution of an external application, the host should implement the NotifyBeginApplication method. This method is called by the hosting application before an external application process is started. Typically, it is used to save the state that the child process might alter so that the parent hosting application can restore that state when the child exits. The implementation of this method can support a variety of operations, for example, setting or removing break handler hooks, redirecting stream handles, and the like.

The host also needs to implement the NotifyEndApplication method, which the runspace of the hosting application calls when the external application is finished executing. It is used to restore the state that the child process might have altered. The runspace will call the NotifyBeginApplication and NotifyEndApplication methods in matching pairs, possibly several times during command processing for a session.

Here are the NotifyBeginApplication and NotifyEndApplication methods from MyHost. Because this host does not need these methods, the implementations return nothing.

C#
public override void NotifyBeginApplication()
{
  return;  // Do nothing...
}

Providing a Callback Function for I/O Notifications

To receive I/O notifications from the runspace, hosting application need to define a callback function. This hosting application does not define a call back function.

Code Sample for Hosting Application Class

Here is the executable code for this hosting application:

C#
//
// 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
{
  class Host01
  {
    /// <summary>
    /// Define the property that the PSHost implementation will 
    /// use to tell the host application that it should exit.
    /// </summary>
    public bool ShouldExit
    {
      get { return shouldExit; }
      set { shouldExit = value; }
    }
    private bool shouldExit;

    /// <summary>
    /// Define the property that the PSHost implementation will
    /// use to tell the host application what exit code to use
    /// when exiting.
    /// </summary>
    public int ExitCode
    {
      get { return exitCode; }
      set { exitCode = value; }
    }
    private int exitCode;
    /// <summary>
    /// This sample uses the RunspaceInvoke class to execute
    /// a script that calls exit. The host application looks at
    /// this and prints out the results.
    /// </summary>
    /// <param name="args">Unused</param>
    static void Main(string[] args)
    {
      // Create an instance of this class so that the PowerShell 
      // will have access to the ShouldExit and ExitCode 
      // parameters.
      Host01 me = new Host01();

      // Create the host instance to use.
      MyHost myHost = new MyHost(me);

      // Create and open the runspace, passing the host 
      // instance just created.
      Runspace myRunSpace = RunspaceFactory.CreateRunspace(myHost);
      myRunSpace.Open();

      // Create the invoker and use it to execute the script.
      RunspaceInvoke invoker = new RunspaceInvoke(myRunSpace);
      string script = "exit (2+2)";
      invoker.Invoke(script);

      // Check the flags and see if they were set propertly.
      Console.WriteLine(
                        "ShouldExit={0} (should be True); ExitCode={1} (should be 4)",
                        me.ShouldExit, me.ExitCode);

      // Close the runspace.
      myRunSpace.Close();

      Console.WriteLine("Hit any key to exit...");
      Console.ReadKey();
    }
  }
}

Code Sample for the Custom Host Class

Here's the executable code for the custom host MyHost.

C#
//
// 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.Globalization;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;

namespace Microsoft.Samples.PowerShell.Host
{
  /// <summary>
  /// This is a sample implementation of the PSHost abstract class for 
  /// console applications. Not all members of the class are implemented. 
  /// Those that are not implemented throw a NotImplementedException.
  /// </summary>
  class MyHost : PSHost
  {
    /// <summary>
    /// Construct an instance of this PSHost implementation. Keep
    /// a reference to the hosting application object so it can
    /// be informed of when to exit.
    /// </summary>
    /// <param name="program">
    /// A reference to the host application object.
    /// </param>
    public MyHost(Host01 program)
    {
      this.program = program;
    }
    private Host01 program;
      
    /// <summary>
    /// This API returns the culture information to use. This 
    /// implementation returns a snapshot of the curture 
    /// information of the thread that created this object.
    /// </summary>
    public override System.Globalization.CultureInfo CurrentCulture
    {
      get { return originalCultureInfo; }
    }
    private CultureInfo originalCultureInfo = 
    System.Threading.Thread.CurrentThread.CurrentCulture;
    /// <summary>
    /// This API returns the UI culture info to use. This 
    /// implementation returns a snapshot of the UI curture 
    /// information of the thread that created this object.
    /// </summary>
    public override System.Globalization.CultureInfo CurrentUICulture
    {
      get { return originalUICultureInfo; }
    }
    private CultureInfo originalUICultureInfo =
    System.Threading.Thread.CurrentThread.CurrentUICulture;

    /// <summary>
    /// This API is not implemented by this example class.
    /// The call fails with an exception.
    /// </summary>
    public override void EnterNestedPrompt()
    {
      throw new NotImplementedException(
            "The method or operation is not implemented.");
    }

    /// <summary>
    /// This API is not implemented by this example class.
    /// The call fails with an exception.
    /// </summary>
    public override void ExitNestedPrompt()
    {
      throw new NotImplementedException(
            "The method or operation is not implemented.");
    }

    static Guid myId = Guid.NewGuid();
    /// <summary>
    /// Return the GUID allocated at instantiation time 
    /// for the InstanceId.
    /// </summary>
    public override Guid InstanceId
    {
      get { return myId; }
    }

    /// <summary>
    /// This API returns an appropriate string to identify 
    /// this host implementation. Keep in mind that this 
    /// string may be used by script writers to identify when 
    /// this host is being used.
    /// </summary>
    public override string Name
    {
      get { return "MySampleConsoleHostImplementation"; }
    }

    /// <summary>
    /// This API is called before an external application 
    /// process is started. Typically it is used to save 
    /// state that the child process may alter so the 
    /// parent can restore that state when the child exits. 
    /// In this example, it is not needed so the method 
    /// does nothing.
    /// </summary>
    public override void NotifyBeginApplication()
    {
      return;  // Do nothing...
    }

    /// <summary>
    /// This API is called after an external application 
    /// process finishes. Typically it is used to restore 
    /// state that the child process may have altered. In 
    /// this example, it is not needed so the method
    /// does nothing.
    /// </summary>
    public override void NotifyEndApplication()
    {
      return; // Do nothing...
    }

    /// <summary>
    /// This API indicates to the host application that 
    /// an exit has been requested. It passes the exit code 
    /// that the host application should use when exiting 
    /// the process.
    /// </summary>
    /// <param name="exitCode"></param>
    public override void SetShouldExit(int exitCode)
    {
      program.ShouldExit = true;
      program.ExitCode = exitCode;
    }

    /// <summary>
    /// This example does not implement a PSHostUserInterface 
    /// component so this property returns null.
    /// </summary>
    public override PSHostUserInterface UI
    {
      get { return null; }
    }

    /// <summary>
    /// Return the version object for this application. 
    /// Typically, this object should match the version 
    /// resource in the application.
    /// </summary>
    public override Version Version
    {
      get {return new Version(1,0,0,0); }
    }
  }
}

Build the Application

See Also

Footer image

Send comments about this topic to Microsoft.

Tags What's this?: Add a tag
Community Content   What is Community Content?
Add new content RSS  Annotations
Processing
© 2008 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Page view tracker