MSH Logo – allowing users to extend its functionality

Tue, Feb 14, 2006 3-minute read

In the last article, we continued to develop our version of MSH Logo.  In typical software-geek fashion, we designed a train-wreck for a user interface.  Rather than provide our users with the power required to make fancy graphics, we shackled them with a handful of ineffective commands.  But it was all we thought the users needed!

Many situations aren’t even this clear-cut.  For example, the Microsoft Office System is a phenomenal suite of applications.  It has more features than you could ever want to count.  But that still isn’t the whole story.  The scripting support in Microsoft Office turns it into an infinitely extensible, bona fide, application development platform.

Roy Osherove goes over some great approaches to adding scripting support to an application, and we’ll take a similar approach by hosting the Monad engine.

To do this, we’ll tack on another page to our tab strip, and place a textbox in it.  We’ll also include a button that users can press to execute the MSH script inside.  We will expose a single extension point to the user – our Turtle object – which they can control with much greater ease than our ridiculous hyperlinks on the other tab.

The code that sits behind the “Run” button is quite simple.  After adding references to the required Monad assemblies, we:

  • Create and open a Runspace
  • Expose our Turtle object to scripts in the Runspace
  • Create a Pipeline inside the Runspace, populated with the user’s script
  • Invoke the Pipeline
  • Clean up.

In 6 lines of code, our application is now scripting-enabled.

private void run_Click(object sender, EventArgs e)
{
    drawingSurface.DrawImage(savedImage, 0, 0);
    // Create and open a Monad runspace.  A runspace is a container 
    // that holds Monad pipelines, and provides access to the state
    // of the Runspace session (aka SessionState.)
    Runspace runspace = RunspaceFactory.CreateRunspace();
    runspace.Open();

    // Create a variable, called "$turtle" in the Runspace, and populate
    // it with a reference to the turtle object in our host.  Pipeline
    // commands can interact with this object like any other .Net object.
    runspace.SessionStateProxy.SetVariable("turtle", turtle);

    // Create a pipeline, and populate it with the script given in the
    // edit box of the form.
    Pipeline pipeline = runspace.CreatePipeline(scriptText.Text);

    // Invoke (execute) the pipeline, and close the runspace.
    pipeline.Invoke();
    runspace.Close();
    
    savedImage = new Bitmap(this.pictureBox.Image);
    turtle.Draw();
    this.pictureBox.Refresh();
}

You can download the completed application here: [monad_hosting_2.zip].  It includes a text file chock full of MSH Logo examples.  That includes one that I may well get fired for implementing: using the output of get-process to graph the working set of system processes.

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]