Archive for April, 2009

Here it is. According to my observations, this is the list of places where I recommend you seek help for PowerShell related quandaries, in order of the speed of response. The first two are practically a tie, but I have put them in this order because the first is just a much better place to get questions answered, with no limits on length of posts. In almost every case, you’ll get faster responses during US daytime, due to sheer volume of users…

  1. IRC #PowerShell on IRC.Freenode.net.
  2. Twitter. Use a the word “PowerShell” in a tweet with a question.
  3. Usenet. Microsoft.public.Windows.PowerShell (or via Google Groups).
  1. Web Forum. On PowerShellCommunity.org

The funny thing is, Twitter can be fast, but it’s not a very effective way of getting answers to anything but trivial questions. Usenet can be fast, but it can also ignore you completely. IRC can be astonishingly fast … if you happen to come in while several high level geeks are having a conversation and thus, paying attention … but at other times (especially in the middle of the USA sleep hours) you might not get an answer at all, depending on who’s paying attention that day. Hypothetically, the WebForum would be the best place to ask — since unanswered questions there still stay in an “unanswered” list that MVPs and others can check — but for some reason it’s never built up the critical mass of users that would allow it to be fast around the clock, or even to answer every single question…

I’m secretly hoping someone will develop a piece of web software which will function as a web forum with an option to email subscribe (like CodePlex’s forums). But in addition, will also function as a web interface to Usenet a-la Google Groups, so that we could set up a web forum which posted questions to an existing Usenet group. It would also need a web-service feed … so we could write a bot that could post new questions to IRC (or a Jabber conference room) and Twitter. It wouldn’t necessarily have to accept answers via IRC/Jabber/Twitter, since those users are usually willing to click through if they think they can help answer a question … this would be the ultimate web forum software. Why can’t I find it already written?

Well, it’s been awhile since I posted anything, and this causes me angst. I’ve been spending all of my time lately on PoshCode.org — not writing content, but working on a new Asp.Net MVC-based back-end (on codeplex) — but in the meantime, this month PoshCode.org finally passed this site’s raw pageview count, and it made me realized I hadn’t posted anything in a long time.

I have a whole stack of PowerBoots demos that I’ve been promising various people that I would post, so I figure I really ought to start putting them up, even if I don’t feel like I have time to properly explain them.

A few in particular don’t need much explaining, so lets start with a couple of those. A while back I wrote a kitchen timer function in response to Hal Rottenberg’s one liner and recently I had cause to pull that out to help my kids take turns playing computer games on Up to Ten … but I needed something a little more visible for their sake.

So I converted my old script to a new PowerBoots-based version with large numbers and a gradient brush that slides across and turns the whole thing to red text as the end of the time get’s closer and closer:

One of the consistent questions about PowerShell is: what’s the best way to write a script or a function to process pipeline objects and be able to take it’s parameters as a normal function?

Of scripts and functions

The first thing to know is that in PowerShell, there’s really no difference between a script (just a file with a .ps1 ending) and a function as you’ll see written below. If you take a function Get-Square, and remove the first and last lines ( Function Get-Square { } ) you could put them in a file called “Get-Square.ps1” in your PATH, you use them exactly the way you would the function that was pre-loaded into memory by dot-sourcing or pasting it on the command line. Of course, doing that easily requires writing the function parameters on their own line using PARAM(...) syntax, which is why I recommend doing that.

Of functions and the pipeline

When your script or function is used on the pipeline its begin block is called once when the pipeline starts up, and then the process block is called repeatedly: once to process each pipeline object, and finally, the end block is called after all the objects have been processed through the whole pipeline. If you don’t understand that, you should play with this function, try calling it by passing a series of numbers through multiple instances of it:


function Test-SquarePipe {
   PARAM( $label, $color="White" )
   BEGIN   { Write-Host "Begin $Label" -Foreground $color }
   PROCESS {
      Write-Host "$Label `t $_" -Foreground $color
      $_ * $_
   }
   END     { Write-Host "End $Label"   -Foreground $color }
}

# for example ...
1..5 | Test-SquarePipe one cyan | Test-SquarePipe two yellow | Test-SquarePipe three green
 

Incidentally, if you don’t specify the begin, process, or end blocks, the body of your function is treated as the end block. This is so that you can use the special $Input variable, which collects all the things passed in on the pipeline, and thus only works in the end block. That would allow the following function to behave the same way regardless of whether it was invoked on the pipeline or by passing an $InputObject. Notice, however, the difference between this, and the function above.


Function Test-SquareEnd {
PARAM( $label, $color="White", $InputObject )
   BEGIN   { Write-Host "Begin $Label" -Foreground $color }
   PROCESS { Write-Host "$Label `t $_" -Foreground $color }
   END     {
      ## because one of $Input or $InputObject must be null:
      Foreach($item in $Input + $InputObject) {
         Write-Host "$Label `t $item" -Foreground $color
         $item * $item
      }
   }
}

# and test it like this
1..5 | Test-SquareEnd one cyan | Test-SquareEnd two yellow | Test-SquareEnd three green

# Or like this
1..5 | Test-SquarePipe one cyan | Test-SquareEnd two yellow | Test-SquareEnd three green
 

Our challenge

The basic idea here is to rewrite that function such that it can be used to process a set of numbers from either the pipeline or an argument, without interfering with the processing of other parameters. We require that the function process items as they come in, rather than waiting until it’s received all input before processing them the way Test-SquareEnd does.

In PowerShell 2.0 this would be easy

In the current CTP 3 of PowerShell 2, you just specify ValueFromPipeline=$true for the parameter you want to set from the pipeline, and the function will work the same way whether you pass the numbers as a parameter or along the pipeline — you can even control which attribute of the objects on the pipeline will be used, but that’s a whole other article.


Function Get-Square {
PARAM(
   $label
,  [ConsoleColor]$color = "White"
,  [Parameter(ValueFromPipeline=$true)]
   [Int[]]$InputObject
)
   BEGIN   { Write-Host "Begin $Label" -Foreground $color }
   END     { Write-Host "End $Label"   -Foreground $color }
   PROCESS {
      ForEach($i in $InputObject) {
         Write-Host "$Label $i" -Foreground $color
         $i * $i
      }
   }
}

## Either way we call these, they have the same output
## Unlike what Get-SquarePipe or Get-SquareEnd
1,2,3,4 | Get-Square one green | Get-Square two cyan
Get-Square one green 1,2,3,4   | Get-Square two cyan
 

Of course, PowerShell 2.0 is still in beta status, and even after it’s released you may need to write scripts that are backwards compatible to PowerShell 1.0, and the excercise of doing so may help you to understand more about how PowerShell functions work, and particularly how they behave in the pipeline.

Our solution

Since in a PowerShell 1.0 function it’s not really supported directly, we need to do some extra work:


function Get-Square {
   PARAM(
      $label = ""
   ,  [ConsoleColor]$color = "White"
   ,  [Int[]]$InputObject = $null
   )
   BEGIN {
      if ($InputObject) {
         ## If you accepted additional params, you'd need to pass those in
         Write-Output $InputObject | &($MyInvocation.InvocationName) -Label $label -Color $color
         ## break
      } else {
         Write-Host "Begin $Label" -Foreground $color
      }
   }
   PROCESS {
      ## If you specify a type for $InputObject, test for that here
      if($_ -is [Int]) {
         Write-Host "$Label $_" -Foreground $color
         $_ * $_
      } elseif($_) {
         throw "$_ is not a System.Int32"
      }
   }
   END {
      if(!$InputObject) {
         Write-Host "End $Label"   -Foreground $color
      }
   }
}

 

An explanation

Of course, this is just an example method, squaring things isn’t that exciting — but what’s special about it is that the output is almost exactly the same whether you call it with parameters Get-Square 1,2,3,4 or on the pipeline: 1,2,3,4 | Get-Square.

The trick is that it actually executes the same way in either case:

  1. If you call it by passing the int (or array of ints) as an argument ($InputObject), it calls itself and passes those values on the pipeline.
  2. When the integers are passed on the pipeline, the special pipeline iterator variable $_ is set, and the process block is executed.
  1. When it has to (re)invoke itself, it passes any other parameters as parameters, which means you need to have default values for them.

There is one tiny difference in the processing, which in real-world use is practically never noticeable (you can see it in our example if you call it like this):


Get-Square one green 1,2,3 | Get-Square two cyan
1,2,3 | Get-Square one green | Get-Square two cyan
 

You’ll see that unlike the PowerShell 2.0 pipeline function, when you pass the numbers as a parameter, the first one of them actually gets passed through the process block before the begin block of the second function on the pipeline is called. This usually doesn’t have any effect, but it’s something to keep in the back of your head.

A few precautions:

  • You have to default values for parameters, because you’ll be passing them all as named parameters, in the re-invoke step, and $null can cause problems.
  • If you need to do additional processing in the begin block, you should only do so in an ELSE case: when $InputObject is null. That way, the code will only execute once each time you call the function.
  • The same goes for the end block: you have to keep your code in an If(!$InputObject) block to avoid executing it twice (when you pass the values as an argument, and it re-invokes itself).
  • The test cases in the process block must wrap all of your process block code, so that you don’t process the arguments twice, and you shouldn’t refer to $InputObject, but instead should use the automatic $_ variable which is the value passed when the function is (re)invoked via the pipeline.

Here’s some sample output, in case you’re wondering:


PS> Get-Square test cyan 2,3,4
Begin test
test 2
4
test 3
9
test 4
16
End test

PS> 2,3,4|Get-Square test cyan
Begin test
test 2
4
test 3
9
test 4
16
End test
 

[new] Updated to PowerBoots 0.1

An introduction to PowerBoots

Please excuse me if I start by just copying the basic ideas of the Shoes Tutorial, but I figured that since PowerBoots is inspired by Shoes, that was as good a place as any to start. PowerBoots (or just “Boots”) is a PowerShell 2.0 module with functions for writing Windows Presentation Framework (WPF) applications in the PowerShell scripting language. You should get the latest version of PowerBoots before continuing, and install it by putting the “PowerBoots” folder in one of your “Modules” folders (list them by typing $Env:PSMODULEPATH in PowerShell v2).

Don’t forget to start PowerShell.exe with the STA parameter (This is no longer required in PowerBoots 0.1).

Did I hear someone ask what is WPF? It was introduced as part of .Net 3.0 (and vastly improved in .Net 3.5), so you can expect to find it preinstalled on computers from Vista on, and of course you can download and install it on XP if it’s not already installed. The only thing you really need to know about WPF for the purposes of this tutorial is that it is the new GUI toolkit for .Net, and that it is container based — you put elements into other elements to control the layout, rather like HTML and Java Swing… you can pick up the rest as we go along.

A simple Boots program


New-BootsWindow -SizeToContent WidthAndHeight -Content (
   Button -Content "Push Me"
)

 

The parenthesis ( and ) are a container, so the button is “in” the Window. You can also pass a ScriptBlock instead, which works basically the same way. Of course, this is a bit uglier than the Shoes syntax, so lets see if we can’t clean it up some. The -Content parameter is positional, so the first non-named argument you pass will be used for that. The same is true for the -Children parameter of panels, and in fact, each of the other similar parameters: Items, Blocks, and Inlines.

We have used a function New-BootsWindow which has an alias Boots. Boots takes all the same parameters as the Window function mentioned previously, but it uses slightly more useful defaults, and has a few other major benefits as well, the first of which is that it automatically “shows” the window, and the second is that it supports an -Async parameter which allows the window to come out in a new thread so that you can continue using PowerShell while the window remains alive and responsive. There is one catch: New-BootsWindow cannot take it’s content on the pipeline (the old function, now renamed “Out-BootsWindow” can take pipeline content, but is a script function, and requires -STA mode) — you have to specify it as a ScriptBlock. So now that we know this, we can rewrite our first example like this:


Boots { Button -Content "Push Me" }
 

Just for the record, the simplest Boots program would just be a simple popup dialog to put some text in a Window, like: Boots { $msg }

We can put controls in a stack


Boots {
   StackPanel {
      Button "A bed of clams"
      Button "A coalition of cheetas"
      Button "A gulp of swallows"
   }
}
 

StackPanels are awesome. So are WrapPanels. Try that code with a WrapPanel instead of a StackPanel and see what the difference is. This brings up another point: those positional parameters we mentioned earlier: Content, Children, Items, Blocks, and Inlines, are also set to accept the value from the pipeline. Not only that, but they are intelligent about whether or not the content model accepts multiple items! So we can actually rewrite that script like this, and get the same results:


Boots { "A bed of clams", "A coalition of cheetas", "A gulp of swallows" | Button | StackPanel }
 

Now we’re really onto something! Read the rest of this entry »

Harmless

1..100|%{"`a"}

Awesome, and mostly harmless (2.0 only)

$SWA = Add-Type -M '
[DllImport("user32",EntryPoint="ShowWindowAsync")]
public static extern bool S(IntPtr h, int i);'
-name SWA -pass
ps | ?{ $pid -eq $_.Id } | % { $SWA::S($_.MainWindowHandle,0) }

while ($true) {
   $handles = ps | %{ if([int]$_.MainWindowHandle){$_.MainWindowHandle} }
   $handles | %{ $SWA::S($_,0) }  # hide all windows
   Sleep 5                        # for five seconds
   $handles | %{ $SWA::S($_,5) }  # and unhide them
   Sleep (60 * 5)                 # every five minutes
}
 

 

Very Annoying

while($true){ps | ?{$_.ID -ne $PID}|%{$_.CloseMainWindow()}}

Cute. Harmless. Requires PoshWpf (PowerBoots 0.1)

## Preload PowerBoots:
#imo PowerBoots
## OR
#Add-PsSnapin PoshWpf
## And THEN:
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
New-BootsWindow {
   param($Window)
   $W = $Window.ActualWidth  /2
   $H = $Window.ActualHeight /2
   $tickover = {
      $X = $this.Tag[0].Left - $this.Tag[1].ActualWidth  /2 #$W
      $Y = $this.Tag[0].Top  - $this.Tag[1].ActualHeight /2 #$H
      $dX = ([System.Windows.Forms.Cursor]::Position.X - $X) / 2
      $dY = ([System.Windows.Forms.Cursor]::Position.Y - $Y) / 2
      $this.Tag[0].Left = $this.Tag[0].Left + $dX - $this.Tag[1].ActualWidth  /2
      $this.Tag[0].Top  = $this.Tag[0].Top  + $dY - $this.Tag[1].ActualHeight /2
   }                                          
   # Make a timer
   $timer          = New-Object System.Windows.Threading.DispatcherTimer
   $timer.Interval = "00:00:00.1"
   $timer.tag = @($Window,$image)
   # Stick the timer into the window....
   $window.Tag = @($timer, $tickover)
   $timer.Add_Tick( $tickover )
   # Make an image
   $image          = New-Object System.Windows.Controls.Image
   $image.Source   = "http://www.clker.com/cliparts/c/1/b/6/1216306562167833124lemmling_Cartoon_ghost.svg.med.png"
   $image.Width    = 75
   $timer.tag = @($Window,$image)
   $timer.Start()
   return $image
} -On_ContentRendered {
   Write-Debug "Content Rendered. Tag: $($this.tag[0])"
   $this.tag[0].Start()
} -On_Closing {
   Write-Debug "Window Closing. Tag: $($this.tag[0])"
   $this.tag[0].Remove_Tick($this.tag[1])
   $this.tag[0].Stop()
} -WindowStyle "none" -AllowsTransparency $true -background Transparent -Async -TopMost $True

Harmless. Awesome. Requires PowerBoots and HttpRest

563..1|%{boots{image{invoke-http get http://xkcd.com/$_ |receive-http text //*[@title]/@src} -MaxWidth 800} -async}

Dig that. I even put the xkcd comics in order for you. So as you go through and close all 563 WPF windows, you get to read all the xckd comics, and you can tell your boss it was someone’s idea of an April Fool’s prank.

Surprisingly Deceptive

@Jaykul Wahoo! I made MVP!

Search My Content