Click here to Skip to main content
Click here to Skip to main content

Introduction to MSH Cmdlets

, 20 Jan 2006
Rate this:
Please Sign up or sign in to vote.
An introduction to MSH Cmdlets.

What is MSH

Microsoft Shell (codename Monad), hereafter abbreviated MSH, is a revolutionary new console shell and environment for Windows. The primary feature that makes it revolutionary is that MSH is designed around the manipulation of objects. While Unix and Linux shells pipe text from one application to another, MSH allows the piping of .NET objects.

The feature is revolutionary because text strings were a limitation. Data had to be represented in text and broken up into units that other programs could understand, and we had to rely on other programs to do this, such as grep. Programs had to know the structure of text in advance in order to parse it, and although there were some conventions, conventions don't solve all problems.

Some common problems with text parsing:

  1. It is often necessary to do excessive parsing or multiple passes on input.
  2. Text parsing is often inaccurate. The setup parsing scripts cannot account for changes to the structure.
  3. It's easy to have incompatibilities between different versions of programs that change output.
  4. Text formatting needs to be done cautiously. If formatting symbols occur, they need to be escaped, etc.

MSH aims to solve these problems. Addressing each of them:

  1. MSH pipes data as objects. Multiple passes are often unnecessary with object streams, since they can be avoided or incorporated into one pass. A consumer of text does not know where one textual element begins and another ends, without actually parsing the entire stream up to that point. Object streams are much easier to work with than text streams, because even if multiple passes are required, random access is available.
  2. Since no parsing happens (programs access object members for information), there can be no inaccuracy. Meaning is encoded unambiguously into the structure of the objects.
  3. Version control is handled the same way it is anywhere else in .NET. .NET has extensive mechanisms in place to gracefully handle version differences, although they are beyond the scope of this article.
  4. As information is transmitted as .NET objects, formatting is not an issue.

Introduction to MSH

This article is an introduction to writing cmdlets. Some very good tutorials exist already to basic MSH features, such as its programming language and basic syntax. Rather than duplicate their efforts, I would point readers unfamiliar with these aspects of MSH to a good one: Ars Technica's Guided tour of the Microsoft Command Shell. The rest of this guide assumes the reader is familiar with these basic features.

What are Cmdlets

Cmdlets are the basic units of execution of MSH. Although MSH is able to execute traditional command-line programs (such as, say, ipconfig), such simple programs would communicate with the shell using primitive text streams. In order to get the advantages MSH offers, one must interact with it using its favored execution units, the cmdlet.

A cmdlet is named by two parts: a verb and an action. The cmdlet is written with the noun following the verb, with a dash in between. An example is get-member, which is a cmdlet that prints out all public members of the object passed to it. verb-noun is the naming convention that Microsoft developed in order to specify with clarity what cmdlets do. This naming convention is enforced programmatically in the construction of cmdlets.

Building Your First Cmdlet

Following the age-old tradition of programming tutorials, starting with K&R, we will first develop a simple cmdlet that prints "Hello, world!". Following the naming convention, let's name this cmdlet greet-world.

A cmdlet is specified by the attribute CmdletAttribute. This attribute resides in the namespace System.Management.Automation. That namespace is not present in standard .NET libraries, but rather is provided by an assembly in the Microsoft Command Shell folder, wherever you installed it; by default, it is installed in C:\Program Files\Microsoft Command Shell. It is necessary to add an assembly reference in order to access the CmdletAttribute class attribute.

With the assembly added to the project, you add the Cmdlet attribute to the class. The attribute's constructor takes two parameters: the verb and noun name mentioned previously. For our first cmdlet, let's name it greet-world, so the attribute specification will look like this:

[Cmdlet("greet", "world")]

Our class is going to take no parameters from the shell, so the class definition will be very simple. The class contains one method, the ProcessRecord function, which executes when our cmdlet is being processed. Inside this method, we can write an object to the cmdlet's output stream. In this case, the object is our greeting, "Hello, world!". So the class looks like this:

GreetWorld.cs:

[Cmdlet("greet", "world")]
public sealed class GreetWorld : Cmdlet {
    protected override void ProcessRecord() {
        WriteObject("Hello, world!");
    }
}

In order to test the cmdlet, we have to build it. Unfortunately, this is a complex process. The first step is to compile the class library like normal, which can be done with Visual Studio or the C# command line compiler. Once the assembly is compiled, we have to inject it into the MSH environment.

Although Microsoft developers are working on a way to inject cmdlets into MSH at runtime, the most stable current process is to build a new MSH shell that includes the cmdlets. Fortunately, Microsoft has included a shell compiler; it takes cmdlet dependencies and builds a new MSH instance that provides access to them. This shell compiler is make-shell. The make-shell shell compiler also resides in the Microsoft Command Shell directory. To build a new MSH instance, you must specify a few parameters:

  • -out <target.exe> specifies the destination executable name to build.
  • -namespace <namespace> tells the shell compiler which namespace the cmdlet resides in.
  • -reference <target1.dll,target2.dll,...> specifies in which existing assemblies the shell compiler should search for cmdlets. The compiler will look in the namespace specified by -namespace.

Once your cmdlet assembly is built, execute the shell compiler. An example execution is shown below, for the code file, GreetWorld.cs:

make-shell -namespace Sirophix.Articles.Cmdlets 
           -out myshell.exe -reference Tutorial.dll

If everything goes smoothly, the output from make-shell should look like the following:

Microsoft Command Shell MakeKit
Copyright (C) 2005 Microsoft Corporation. All rights reserved.
Shell myshell.exe is created successfully.

Simply run myshell.exe and you'll have a new shell that includes greet-world. At the time of this writing, newly compiled shells start up with a number of exceptions; these exceptions seem relatively harmless. The shell will still start up successfully. When it's running, try executing your new cmdlet:

MSH C:\> greet-world
Hello, world!

I mentioned earlier in the article that the power of MSH is that it uses .NET objects instead of strings. This is the case for our cmdlet as well: it returns an actual System.String, not mere text. You can see that this is the case by piping the return from greet-world to get-member.

MSH C:\> greet-world | get-member

TypeName            Name        MemberType Definition
--------            ----        ---------- ----------
System.String       Clone       Method System.Object Cl...
System.String       CompareTo   Method System.Int32 Com...
System.String       Contains    Method System.Boolean C...
System.String       CopyTo      Method System.Void Copy...
System.String       EndsWith    Method System.Boolean E...
[... and more]

As you can see, the return value from greet-world is an actual object with members and methods. In fact, we can execute these members directly, just like we could in a .NET language. For example, we can just print out the type name to verify what it is:

MSH C:\> (greet-world).GetType().ToString()
System.String

This output demonstrates for sure that we're returning a System.String. We can even perform transformations on this object:

MSH C:\> (greet-world).ToUpper()
HELLO, WORLD!

Implications

It should be apparent to the reader that MSH is an incredibly powerful environment. In addition to its innate features, Microsoft is also committed to exposing every aspect of system management within MSH. This means that any administrative feature available can be controlled with an MSH script.

MSH has transcended the classic notions of what a shell environment is capable of. MSH has the power of a programming environment with the simplicity of other shell scripting languages. From within MSH, one can script many complex actions (such as connecting to a database, manipulating objects) that would otherwise have to be written in a C++ or C# program.


The next article in the series will cover more advanced features of cmdlets, such as passing named parameters, with and without pipes, and the other types of execution notifications cmdlets can receive.


This article was written with much help from two other developers, Mathias Ricken, a Rice University programming languages researcher, and redwyre from #C++ on DALnet.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Xiphoris

United States United States
Justin is currently an undergraduate computer science student at Rice University.
 
Justin's programming background is extensive, and he has worked for such companies as Amazon.com and R7Solutions.com. He will be working for Microsoft on the C# language team starting summer of 2006.

Comments and Discussions

 
GeneralInteresting PinmemberSuper Lloyd24-Jan-06 9:32 
GeneralRe: Interesting PinmemberXiphoris24-Jan-06 16:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 20 Jan 2006
Article Copyright 2006 by Xiphoris
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid