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.
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.
|
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:
|
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:
|
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.
|
public override System.Globalization.CultureInfo CurrentCulture
{
get { return originalCultureInfo; }
}
private CultureInfo originalCultureInfo =
System.Threading.Thread.CurrentThread.CurrentCulture;
|
|
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:
|
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.
|
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.
|
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.
|
public override void EnterNestedPrompt()
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
|
|
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.
|
public override void NotifyBeginApplication()
{
return; // Do nothing...
}
|