Make-A-Cmdlet: Part 3, The Code

Okay, without further ado, here’s the next evolution of my code. I want to point out that I’m blogging as I’m going – if you follow along with this step and things don’t work… well, maybe they didn’t work for me, either. Learning process! Stay tuned, if that’s the case :), and we’ll explore my mistakes together.

And, by the way – if you’re doing this in Visual Basic Express, remember to add a reference (using the Tools menu) to the System.Management and System.Management.Automation DLLs. That way Express knows to actually load those DLLs. Stick with me, now…

I’m going to put comments in-line to explain what’s going on. First, I have two lines of code that import the necessary .NET namespaces for what I want to do.

Imports System.Management.Automation
Imports System.Management

Next up is my cmdlet declaration. Notice that I’ve changed my mind on the cmdlet name, making it Ping-Computer… I felt obliged to stick with the official naming. “Where-Object,” a built-in cmdlet, is actually a special kind of exception to the naming scheme, and I didn’t want to base my name on an exception. Notice that the cmdlet name is defined in two parts: Verb and Noun. I had missed this last time, and made my cmdlet name something like Where-WhereObjectCmd or something bizarre. Anyway – figured it out, now. For the verb, I’m using the built-in VerbsDiagnostic.Ping enumeration. The name of my class – PingComputerCmd – is abritrary; nobody will see it but me.

<Cmdlet(VerbsDiagnostic.Ping, “Computer”, SupportsShouldProcess:=True)> _
Public Class PingComputerCmd
Inherits Cmdlet

Now I need to define a variable which will hold my input – I’ve blogged this bit before, although I’ve standardized the property name to Name rather than Address; the PowerShell SDK recommends using Name whenever it’s appropriate. My _Name variable is for use within the cmdlet itself, and it’s defined as a String array.

Private _Name As String()

Now I’m defining the Name parameter, which is public – meaning users of the cmdlet will see it. Notice that this parameter is in position zero, meaning you don’t have to specify a parameter name at all. Also, it’ll accept input from the pipeline just as easily.

<Parameter(Position:=0, Mandatory:=False, ValueFromPipeline:=True)> _
Public Property Name() As String()
Get
Return _Name
End Get
 Set(ByVal value As String())
_Name = value
End Set
End Property

Now I need to override one of the built-in cmdlet methods, ProcessRecord. The default implementation for this method doesn’t do anything, so I need to insert the code that makes my cmdlet do whatever it’s supposed to do.

Protected Overrides Sub ProcessRecord()
Dim ThisName As String
For Each ThisName In _Name
WriteDebug(“Attempting to ping ” & ThisName)
  Dim Query As SelectQuery
Query = New SelectQuery(“SELECT StatusCode FROM Win32_PingStatus WHERE Address = ‘” & ThisName & “‘”)
  Dim Searcher As ManagementObjectSearcher
Searcher = New ManagementObjectSearcher(Query)
  Dim result As ManagementObject
For Each result In Searcher.Get
If result.GetPropertyValue(“StatusCode”) Then
WriteDebug(“Successful ping of ” & ThisName)
WriteObject(ThisName)
Else
WriteDebug(“Could not ping ” & ThisName)
End If
Next
Next
End Sub

All I’m doing is using WMI to ping the specified computer name. Remember that _Name can be an array, so I’m using a For Each loop to go through each element in the array. Also notice my use of WriteDebug() and WriteOutput(). These are two built-in functions, meaning I don’t have to define them. WriteOutput() writes an object to the pipeline – here, I’m writing the name of the computer if I could ping it. WriteDebug() writes text to PowerShell’s debug stream. And that’s it – the last line of code finishes the class.

End Class

But we’re not quite done. In Visual Studio, it’s time to hit the Build button to make a DLL. Then, I want to execute the InstallUtil utility, which is located in the .NET Framework’s folder (usually C:\Windows\Microsoft.NET\Framework\v2.0.50727). This utility will “register” my snap-in so that PowerShell can see it. Remember from the previous post in this series that my Visual Studio project is not just a cmdlet – I started from a template that also included the snap-in “package” code.

So, I run:

installutil “c:\projects\primaltoys.dll”

And then, in PowerShell, I check to make sure PowerShell can see the registration:

get-pssnapin -registered

Yup, it’s there! Now I can add it to the shell:

add-pssnapin primaltoys

http://msdn2.microsoft.com/en-us/library/ms714644.aspx has more details on this process, including how to “export” the shell into a shortcut so you can always start up with your custom snap-ins loaded.

So it’s time to try it out:

ping-computer localhost

What happens? Did it work for you? Yes? No? Well… stay tuned until next week, and you’ll see MY results.

Technorati Tags: