Windows PowerShell

Allrighty, kind of recoverd again; seems the BeFraLux country party last night had a healing effect. To be honest, I didn't know much of the people over there - I'm a developer after all - but lots of faces were familiar from MSDN/TechNet events in Belgium. So, we had some nice conversations about where all things come together: Windows PowerShell. Yeah baby, it's all over the place!

So right now I'm on the Windows PowerShell ATE booth (desk 28) with lots of great questions and feedback. Seems people start to love it and it's amazing to see people getting overwhelmed by all the power that gets unlocked by using the .NET Framework BCL underneath. Some of our great examples are:

PS C:\Users\Bart>([string](ipconfig | select-string IPv4)).substring(39) 172.22.28.85 PS C:\Users\Bart> [int](get-wmiobject Win32_OperatingSystem).Version.Split(".")[2] 5600 PS C:\Users\Bart> $a = [UriBuilder]"ftp://bar:foo@test.com/dir" PS C:\Users\Bart> $a Fragment : Host : test.com Password : foo Path : /dir Port : 21 Query : Scheme : ftp Uri : ftp://bar:foo@test.com/dir UserName : bar
Isn't this cool? Don't try to do the URL parsing yourself, just rely on the specalized classes in the .NET Framework. Still one hour to go over here on ATE before I'll attend some sessions. Tonight another party is on, from the Visual Studio team, which I'll visit together with the few U2U people who're still in Barcelona. Yeah, devs rule IT Forum :-) Should be fun.Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

What a weird day this was. Woke up terribly sick and tired; guess the roundtrip through the city last weekend on top of the roof of a tourist bus is the evil (maybe the late night edition at Friday has to be blamed as well). As I mentioned earlier today, Bob's keynote kept me awake. But after that I had some pretty difficult times in the ATE to stay awake. Luckily there's that nice Spanish nurse in the cellar of the building that had some asperine that made me survive the afternoon. Never took much medicines before, but in this case it seemed to work pretty well. In the afternoon I attended (well, sort of) Rafal's session on holistic security as well as Steve Riley's one on system protection in Windows Vista, while doing some last minute work on my Speaker Idol demo. Have to say, this time I didn't win (which is kind of a relief as well; I'm not an IT pro after all). Apparently the judges liked the PowerShell-based PPT thing, but I went way over time (12 minutes instead of only 5) due to some technical issues with the Windows Live OneCare firewall that turned on all of a sudden (didn't have that issue last week) and, yes I have to admit, due to the little last-minute preparation. Maybe I shouldn't have blamed the product but that's I guess a developer's frustration when security turns against him. No further excuses :-). I want to thank the Belgian audience for the support and want to congratulate Andy Malone with his great presentation on Speech Recognition in Windows Vista. To all participants who'll be next: good luck and beware of the judges :-).

Oh, before I do forget, below you can find the "slides" I used. Just copy-paste into Windows PowerShell and press enter twice and there you go:

$ppt = { cls; write-host Windows PowerShell in 5 minutes; write-host *******************************; Read-Host; write-host * Next-generation command-line shell; write-host * Object-oriented; write-host write-host ??? = DIFFICULT ???; Read-Host; cls; write-host Discoverability; write-host ***************; Read-Host; write-host * Getting started; Read-Host; write-host * Demo - Scream for HELP: get-help; Read-Host; powershell; cls; write-host Commandlets and aliases; write-host ***********************; read-host; write-host * Cmdlet = Verb + Noun; read-host; write-host * get-process, get-childitem, clear-host, ...?; read-host; write-host * Enter aliases; Read-Host; write-host * Demo - get-command and get-alias; Read-Host; powershell; cls; write-host Object-orientation; write-host ******************; read-host; write-host * Developers, developers, developers?; read-host; write-host * NO!; read-host; write-host * What is an object?; write-host - Data = properties; write-host - Operations = methods; read-host; write-host * Discoverability: get-member read-host; write-host * Demo - The old way; write-host; cmd; cls; write-host Object-orientation; write-host ******************; write-host; write-host * Developers, developers, developers?; write-host; write-host * NO!; write-host; write-host * What is an object?; write-host - Data = properties; write-host - Operations = methods; write-host; write-host * Discoverability: get-member write-host; write-host * Demo - The old way; write-host; write-host * Demo - File information and file operations; Read-Host; powershell; cls; write-host Drives all the way; write-host ******************; read-host; write-host * What is a drive? [a-Z]:; read-host; write-host * Much more; write-host - HKLM:; write-host - Cert:; write-host - Variable:; write-host - Alias:; read-host; write-host * get-psdrive read-host; write-host * Demo - Dive into the registry; Read-Host; powershell; cls; write-host Conclusion; write-host **********; read-host; write-host * 5 commandlets to remember:; write-host - get-help; write-host - get-command; write-host - get-alias; write-host - get-member; write-host - get-psdrive; read-host; cls; write-host Q "&" A; write-host *****; write-host; write-host * info@bartdesmet.net; write-host * http://blogs.bartdesmet.net/bart; read-host; $ie = new-object -Com internetexplorer.application; $ie.Navigate2("http://blogs.bartdesmet.net/bart"); $ie.visible = 1; exit; }; &$ppt;
Enjoy! On to my ATE dutie right now, where I'll be the rest of the week at the indicated times (see previous post) to help you out with all possible PowerShell issues.

Cheers (Bart still in recovery mode)!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Right, here we are again, up and running (well, sort of) at TechEd 2006 IT Forum. I'm actually feeling pretty sick today, guess the tourist bus roundtrip last weekend has been a bit too drafty. That's kind of the dark side of "Sunny Barcelona". Luckily, Bob Muglia was able to keep me alert during the keynote, which I found pretty interesting, even from a developer's perspective. Needless to say, Jeffrey Snover did a great job with his PowerShell baby once more, which just RTM'ed. Download now!

Later today I'll be trying to reload my batteries to get on stage this evening at 7PM for Speaker Idol once more. So if I do not run out of juice, come and see me. Guess a talk on Windows Vista Application Recovery would be right choice in my situation, but I'll tackle some IT related topic.

For my ATE duties, here's the schedule for the week:

  • Tuesday 14 Nov - 18:45 till 21:00 (I'll be at Speaker Idol between 19:00 and 20:00 however, but I'll try to compensate later this week).
  • Thursday 16 Nov - 10:00 till 13:00 (hopefully I can close the country party on Wednesday on time).
  • Friday 17 Nov - 10:00 till 13:00.

So, feel free to come and see me at desk 28 (Longhorn).

Cheers (Bart in recovery mode)!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Introduction

End of October 2006, the PowerShell release is coming closer day after day. Time for a series of lightning posts with small yet useful tips for developers targeting PowerShell with custom providers, cmdlets, etc. After reading this series, there are no excuses left for not taking on the PowerShell challenge :-).

In this first post, you'll learn how to parameterize cmdlets in a structed way.

Code

Assume you have a cmdlet called Bar that processes Foo objects:

class Foo
{
   private int
id;

   public int
Id
   {
      get { return
id; }
      set { id = value
; }
   }

   private string
name;

   public string
Name
   {
      get { return
name; }
      set { name = value
; }
   }

   //
   // Other properties and class members omitted.
   //
}

Assume our cmdlet is declared as follows:

[Cmdlet("Process", "Foo", DefaultParameterSetName = "FooId")]
public class StopProcCommand :
PSCmdlet
{
}

The DefaultParameterSetName property will be explained in a minute.

There are many ways to provide Foo objects to the cmdlet. One way is to pass the objects (e.g. obtained through some get-foo cmdlet) directly via the pipeline:

private Foo[] foos;

[
Parameter(
   ParameterSetName =
"InputObject"
,
   Mandatory =
true
,
   ValueFromPipeline =
true

)]
public
Foo[] InputObject
{
   get { return
processNames; }
   set { processNames = value
; }
}

Another way is to allow users to specify the names of the Foo objects that have to be processed:

private string[] names;

[
Parameter(
   ParameterSetName =
"FooName"
,
   Mandatory =
true
,
   ValueFromPipelineByPropertyName =
true
,
   ValueFromPipeline =
true
)]
public string
[] Name
{
   get { return
names; }
   set { names = value
; }
}

Or what about the identifiers:

private int[] ids;

[Parameter
(
   ParameterSetName =
"FooId"
,
   Mandatory =
true
,
   ValueFromPipelineByPropertyName =
true
,
   ValueFromPipeline =
true
)]
public int
[] Id
{
   get { return
ids; }
   set { ids = value
; }
}

All these parameters have one thing in common: the use of a ParameterSetName property. The idea is simple: each "parameter set" represents a different way to pass parameters to the cmdlet. On invocation, such a parameter set is chosen (e.g. by invoking process-foo -Name ... or implicitly by using the pipeline). Afterwards, you can find out about the parameter set being used by calling the ParameterSetName property of the cmdlet itself:

protected override void ProcessRecord()
{
   switch
(this.ParameterSetName)
   {
      case "InputObject":
         // Use foos
         break
;
      case "FooName"
:
          
// Use names
         break;
      case "FooId"
:
         // Use ids
         break
;
      default
:
         throw new ArgumentException("Invalid parameter set."
);
   }
}

This way you can allow different types of invocations of a cmdlet, clearly distinguishing different parameter sets. The DefaultParameterSet is used whenever the PowerShell shell cannot disambiguate a parameter that is set based on the command-line input (a fall-back mechanism that is).

Enjoy!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Introduction

I've been posting about Windows PowerShell programmability quite a bit lately. A few interesting posts you might want to check out before continuing include:

Especially the last one got attention from the community, even our colleagues and friends of the IT Pro world :-). I can imagine they never thought of some kind of GUI popping up out of a dark shell environment. One of my blog readers, Karl Prosser, had the good idea to wrap the MessageBox functionality in a cmdlet, so that's what I've done in this post.

Ow yes, to attract Visual Basic folks too, I've written this one in VB 2005.

The code

Let's start by looking at the cmdlet itself. The basics are easy: inherit from PSCmdlet and annotate with Cmdlet, all from the System.Management.Automation namespace. Furthermore, add some parameters and override ProcessRecord:

Imports System.ComponentModel
Imports
System.Management.Automation
Imports
System.Management.Automation.Runspaces
Imports
System.Windows.Forms

<Cmdlet(
"show", "confirm")> Public Class
ShowConfirmCmdlet
    Inherits
PSCmdlet

    Private _confirmationScript As
ScriptBlock
    Private _inputObject As
PSObject
    Private _title As String =
""

    <Parameter(Mandatory:=True, Position:=1)> Property ConfirmationScript() As
ScriptBlock
        Get
            Return
_confirmationScript
        End
Get

        Set(ByVal value As
ScriptBlock)
            _confirmationScript = value
        End
Set
    End
Property

    <Parameter(Mandatory:=True, Position:=2, ValueFromPipeline:=True)> Property InputObject() As
PSObject
        Get
            Return
_inputObject
        End
Get

        Set(ByVal value As
PSObject)
            _inputObject = value
        End
Set
    End
Property

    <Parameter(Mandatory:=False)> Property Title() As
String
        Get
            Return
_title
        End
Get

        Set(ByVal value As String
)
            _title = value
        End
Set
    End
Property

    Protected Overrides Sub
ProcessRecord()
        Dim o As Object
= _confirmationScript.InvokeReturnAsIs(_inputObject)
        Dim res As String =
""
        If LanguagePrimitives.TryConvertTo(o, res)
Then
            If MessageBox.Show(res, _title, MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.Yes
Then
                WriteObject(_inputObject)
            End
If
        End
If
    End
Sub
End
Class

I won't repeat all of the basis over here once more. Just check out other PowerShell posts for more information.

Next, we need to build a snap-in wrapper to make it easy to use the cmdlet. Here it is:

<RunInstaller(True)> Public Class FindStringPSSnapIn
    Inherits
CustomPSSnapIn

    Private _cmdlets As New System.Collections.ObjectModel.Collection(Of
CmdletConfigurationEntry)
    Private _providers As New System.Collections.ObjectModel.Collection(Of
ProviderConfigurationEntry)
    Private _types As New System.Collections.ObjectModel.Collection(Of
TypeConfigurationEntry)
    Private _formats As New System.Collections.ObjectModel.Collection(Of
FormatConfigurationEntry)

    Public Sub New
()
        MyBase
.New()
        Cmdlets.Add(
New CmdletConfigurationEntry("show-confirm", GetType(ShowConfirmCmdlet), Nothing
))
    End
Sub

    Public Overrides ReadOnly Property Name() As
String
        Get
            Return
"DemoPSSnapIn"
        End
Get
    End
Property

    Public Overrides ReadOnly Property Vendor() As
String
        Get
            Return
"BdsSoft"
        End
Get
    End
Property

    Public Overrides ReadOnly Property Description() As
String
        Get
            Return
"This snap-in contains a set of demonstration cmdlets."
        End
Get
    End
Property

   
Public Overrides ReadOnly Property Cmdlets() As System.Collections.ObjectModel.Collection(Of
CmdletConfigurationEntry)
        Get
            Return
_cmdlets
        End
Get
    End
Property

    Public Overrides ReadOnly Property Providers() As System.Collections.ObjectModel.Collection(Of
ProviderConfigurationEntry)
        Get
            Return
_providers
        End
Get
    End
Property

    Public Overrides ReadOnly Property Types() As System.Collections.ObjectModel.Collection(Of
TypeConfigurationEntry)
        Get
            Return
_types
       
End
Get
   
End
Property

    Public Overrides ReadOnly Property Formats() As System.Collections.ObjectModel.Collection(Of
FormatConfigurationEntry)
        Get
            Return
_formats
        End
Get
    End
Property
End
Class

This principle was explained in a previous post. So, compile the whole thing and install it as explained below.

Install it

For the IT pros I've uploaded the compiled library to my site over here. Extract the file (results in a single .dll file) and install it as shown below:

installutil ShowConfirmCmdlet.dll

Installutil.exe can be found in the %windir%\Microsoft.NET\Framework\v2.0.50727 folder, so change the PATH environment variable accordingly (set PATH=%PATH%;%windir%\Microsoft.NET\Framework\v2.0.50727).

Run it

Now open Windows PowerShell. The first thing to do is to register the snap-in we made:

add-pssnapin demopssnapin

Next, we'll try it out with a classic example: get-process (gps). First notice I've three instances of Notepad up and running:

PS C:\Users\Bart> gps n*

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     59       3     1496       6708    52     0,13   2916 notepad
     59       3     1496       6736    52     0,13   4392 notepad
     59       3     1544       6916    52     0,48   4932 notepad

Now, we'll ask for confirmation as follows:

gps n* | show-confirm { "Kill " + $args[0].ProcessName + "?" } -title "Please confirm" | stop-process -whatif

Assuming you choose Yes twice and No once, the output should look like this:

PS C:\Users\Bart> gps n* | show-confirm { "Kill " + $args[0].ProcessName + "?" } -title "Please confirm" | stop-process -whatif
What if: Performing operation "Stop-Process" on Target "notepad (2916)".
What if: Performing operation "Stop-Process" on Target "notepad (4392)".
PS C:\Users\Bart>

I've used whatif just to simulate what would happen; of course you can omit it and kill the processes right away.

Conclusion

There is overlap with -confirm, I know, and the pipeline order is different:

gather | confirm | perform

instead of

gather | perform -confirm

It's just a matter of taste. Personally the first one - that we've been building in this post - reads more user-friendly to me. One caveat however: no "Yes to all" or "No to all" option yet. This would require the use of BeginProcess and EndProcess overrides as Lee pointed out but isn't covered in this post (notice we'd need a more complex custom dialog other than MessageBox.Show to do this - or Vista might help out with this).

Anyway, enjoy.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

You can find it over here.Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Introduction

Everyone who has started with Windows PowerShell should know the where-object (aliased as where) cmdlet by now. A typical example

get-process | where-object { $_.ProcessName.StartsWith("n") }

or abbreviated using aliases

gps | where { $_.ProcessName.StartsWith("n") }

Assuming you have a single notepad process running on your machine, the output will be very similar as the one displayed below:

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     55       3     1416       6764    52     0,08   3436 notepad

The part indicated in red is a so-called script block. Readers of my blog learned about creating cmdlets themselves already (see all my PowerShell posts too). Today, I'm showing you a possible way to consume a scriptblock in your own cmdlet too, by re-creating the where-object cmdlet.

Dissection of Where-Object

So, what does Where-Object do? The answer is really simple: every object that flows through the Where-Object cmdlet is evaluated against the specified script block. If the script block returns true, it's passed on in the pipeline. If not, the object is dropped.

Considering this, what are the parameters? There are clearly two: one to take the script block and one to take the current object that has to be processed by the cmdlet. Let's concretize this using get-help where-object:

PARAMETERS
    -FilterScript <System.Management.Automation.ScriptBlock>
        The script block that is being applied to the object from the pipeline.

        Parameter required?           true
        Parameter position?           1
        Parameter type                System.Management.Automation.ScriptBlock
        Default value
        Accept multiple values?       false
        Accepts pipeline input?       false
        Accepts wildcard characters?  false

    -InputObject <System.Management.Automation.PSObject>
        The pipelined object that is being evaluated by the scriptBlock.

        Parameter required?           false
        Parameter position?           named
        Parameter type                System.Management.Automation.PSObject
        Default value
        Accept multiple values?       false
        Accepts pipeline input?       true (ByValue)
        Accepts wildcard characters?  false

The first parameter is the script block; the second one (which can be accepted from the pipeline) contains the input object.

Coding time

We know enough to create a similar cmdlet right now. Here it is:

[Cmdlet("where", "object2")]
public class WhereObjectCmdlet :
PSCmdlet
{
  
private ScriptBlock
filterScript;

   [
Parameter(Mandatory = true
, Position = 1)]
   public ScriptBlock
FilterScript
   {
      get { return
filterScript; }
      set { filterScript = value
; }
   }

   private PSObject
inputObject;

   [
Parameter(Mandatory = true, Position = 2, ValueFromPipeline = true
)]
   public PSObject
InputObject
   {
      get { return
inputObject; }
      set { inputObject = value
; }
   }

   protected override void
ProcessRecord()
   {
      object
o = filterScript.InvokeReturnAsIs(inputObject);
      if (LanguagePrimitives
.IsTrue(o))
         WriteObject(inputObject);
   }
}

The two properties need no explanation. The ProcessRecord method isn't too difficult as well. First, the inputObject is passed to the script that's called using the InvokeReturnAsIs method. There's another Invoke method too that returns an array of PSObjects as return objects. You can use this one too, but we just need a scalar value indicating the true/false evaluation state of the script block.

Using LanguagePrimitives we check for a true value and in that case, the object is passed on via the pipeline using WriteObject.

Next, wrap the cmdlet in a snap-in as explained in a previous post. I'm not going to duplicate this code over here; I'll assume you "publish" the cmdlet as "where-object2". Don't forget to installutil the snap-in too.

Testing it

Open Windows PowerShell and add your snap-in using add-pssnapin:

PS C:\Users\Bart> add-pssnapin <snapinname>

Next, run the where-object2 cmdlet on the get-process (gps) output as shown below:

PS C:\Users\Bart> gps | where-object2 { $args[0].ProcessName.StartsWith("n") }

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     55       3     1416       6756    52     0,09   3436 notepad

Works fine isn't it? The only caveat is that we have to use the $args script block parameter collection instead of $_. A little workaround can help:

PS C:\Users\Bart> gps | where-object2 { $_ = $args[0]; $_.ProcessName.StartsWith("n") }

However, the goal of this post is not to nag about the $_ assignment, but rather to show you how to create a cmdlet that invokes a scriptblock. And that's what we did :-).

Happy scripting!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Introduction

I think the Encrypting File System is familiar to a lot of you. If not, check out this page. In a few simple words, EFS is an NTFS feature that allows you to encrypt files using a symmetric key. That symmetric key (the FEK) is secured by DPAPI on a per-user basis. Therefore, an EFS encrypted file is only accessible by the user that encrypted it. In this post, I'll show you how to perform EFS encryption on a set of files using Windows PowerShell. No rocket sciene if you know you PS best friend get-member, but still worth to blog about.

Discover the PS world - get-member all the time

Without words...

PS C:\temp\PS demo> get-childitem | get-member -m Methods | format-table Definition

Definition
----------
System.IO.StreamWriter AppendText()
System.IO.FileInfo CopyTo(String destFileName), System.IO.FileInfo CopyTo(St...
System.IO.FileStream Create()
System.Runtime.Remoting.ObjRef CreateObjRef(Type requestedType)
System.IO.StreamWriter CreateText()
System.Void Decrypt()
System.Void Delete()
System.Void Encrypt()
System.Boolean Equals(Object obj)
System.IO.FileAttributes get_Attributes()
System.DateTime get_CreationTime()
System.DateTime get_CreationTimeUtc()
System.IO.DirectoryInfo get_Directory()
System.String get_DirectoryName()
System.Boolean get_Exists()
System.String get_Extension()
System.String get_FullName()
System.Boolean get_IsReadOnly()
System.DateTime get_LastAccessTime()
System.DateTime get_LastAccessTimeUtc()
System.DateTime get_LastWriteTime()
System.DateTime get_LastWriteTimeUtc()
System.Int64 get_Length()
System.String get_Name()
System.Security.AccessControl.FileSecurity GetAccessControl(), System.Securi...
System.Int32 GetHashCode()
System.Object GetLifetimeService()
System.Void GetObjectData(SerializationInfo info, StreamingContext context)
System.Type GetType()
System.Object InitializeLifetimeService()
System.Void MoveTo(String destFileName)
System.IO.FileStream Open(FileMode mode), System.IO.FileStream Open(FileMode...
System.IO.FileStream OpenRead()
System.IO.StreamReader OpenText()
System.IO.FileStream OpenWrite()
System.Void Refresh()
System.IO.FileInfo Replace(String destinationFileName, String destinationBac...
System.Void set_Attributes(FileAttributes value)
System.Void set_CreationTime(DateTime value)
System.Void set_CreationTimeUtc(DateTime value)
System.Void set_IsReadOnly(Boolean value)
System.Void set_LastAccessTime(DateTime value)
System.Void set_LastAccessTimeUtc(DateTime value)
System.Void set_LastWriteTime(DateTime value)
System.Void set_LastWriteTimeUtc(DateTime value)
System.Void SetAccessControl(FileSecurity fileSecurity)
System.String ToString()

Putting EFS to work

So, you can do the following:

PS> dir *.txt | foreach { Write-Host $_.Name; $_.Encrypt() }

There's one little caveat: the EFS encryption takes place in the context of the user running the PowerShell instance of course. So, make sure you're running in the right security context, e.g. by runas-ing the Windows PowerShell.

Btw, on Windows Vista you'll see a notification popping up from the notification area in the taskbar which recommends you to backup your encryption keys:

Clicking it displays the following dialog:

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

PowerShell fans will have seen numerous examples using the get-process and stop-process cmdlets. Combine it with the power of -confirm and every normal self-respecting IT admin should feel a good amount of adrenaline flowing through their veins.

Consider the following:

gps [a-c]* | where something } | stop-process

The part indicated in red is just a plain script block that has to return a boolean value, telling PowerShell whether or not to pass the object that flows through the pipeline from the left to right. That is, if gps [a-c]* returns:

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    125       4    13864      10920    47            1240 audiodg
     30       2      780       3392    34     0,06   4124 conime
    551       5     1636       4444    85             564 csrss
    518       7     2052      11480   156             628 csrss

all of these four System.Diagnostic.Process instances will flow through the pipeline one-by-one on to the where cmdlet, that's asking just one simple question "Pass this System.Diagnostics.Process object through or not?" by evaluation the script block. Inside the script block one can use the variable $_ that represents the current object flowing through the pipeline:

where { $false } won't pass through anything
where { $true  } will pass through anything
where { $_.ProcessName -eq "notepad" } will evaluate the condition
where { $s = $_.ProcessName; $s -eq "notepad" } will evaluate the condition using some temporary variable

Get the point? Now, what about asking the end-user using - how do you guess - a MessageBox? Let's start by trying to display a simple MessageBox on the screen, outside the where-stuff:

PS C:\Users\Bart> [System.Windows.Forms.MessageBox]::Show("Hello")
Unable to find type [System.Windows.Forms.MessageBox]: make sure that the assembly containing this type is loaded.
At line:1 char:34
+ [System.Windows.Forms.MessageBox]: <<<< :Show("Hello")

PS C:\Users\Bart>

Whoops! Don't worry. As developers have to add a reference to an assembly to their projects, you have to do a similar thing in PowerShell. A straightforward way to get around this is this:

PS C:\Users\Bart> [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

GAC    Version        Location
---    -------        --------
True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Windows.Forms\2.0....

Now try to display the MessageBox again. This is what you get to see now:

Now revisit the scene of the where cmdlet (i.e. Where-Object) and consider the following piece of code:

PS C:\Users\Bart> gps [a-c]* | where {
>> $s = "Select " + $_.ProcessName + "?";
>> $d = [Windows.Forms.MessageBox]::Show($s, "PowerShell rocks", [Windows.Forms.
MessageBoxButtons]::YesNo, [Windows.Forms.MessageBoxIcon]::Question)
>> $d -eq [Windows.Forms.DialogResult]::Yes
>> }
>>

Don't worry, this code isn't difficult at all. The first two lines are straightforward if you have some basic knowledge of System.Windows.Forms and the use of variables in PowerShell. Notice the use of square brackets to specify a class name and the :: operator to call a static method. The last one is the one that evaluates the boolean condition based on a comparison of the dialog result with the DialogResult.Yes enumerator value: $d -eq [Windows.Forms.DialogResult]::Yes

The system will now present a MessageBox dialog for audiodg, conime, csrss and csrss, as shown below. Depending on the answer you choose, you'll see the list of passed objects grow in the background in the PowerShell window:

Useful or not? Maybe in some scenarios where the black-n-white command line environment doesn't feel comfortable enough for system "operators". It's just a matter of taste I guess. Nevertheless, this little sample shows you some key concepts:

  • How the Windows PowerShell pipeline works.
  • How to reference an assembly in Windows PowerShell.
  • How the Where-Object cmdlet (aliased as "where") works.
  • The omnipresence of .NET in the world of Windows PowerShell.

I'd say: "Dive deeper in the blue PS ocean". Cheers!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

In this personal webcast episode called "Creating your first Windows PowerShell cmdlet", I'm showing you how to create a wordcount cmdlet (equivalent to the wc command in UNIX) for Windows PowerShell using Visual Studio 2005 and C# (20 minutes in length).

Note: This webcast uses the WordCount cmdlet code that's used in the Windows PowerShell article I wrote for the Dutch .NET Magazine #13. With this webcast you can see this in practice, also a little extended with the snap-in approach. And most importantly, the sample becomes available for non-Dutch-speaking folks as well.

You can download the code for this webcast over here.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

More Posts « Previous page - Next page »