PowerShell Cookbook

Search

Categories

 

On this page

Workaround: The OS handle's position is not what FileStream expected
PowerShell Execution Policies in Standard Images
First Solo – and a Ripped Shirt to Prove It
Importing and Exporting Credentials in PowerShell
Flight Training, Episode 2
PowerShell's Noble Blue
Generating Code Coverage from PowerShell Scripts
First Steps in Flight
PowerShell Crash - Your Help Needed
BgShell – Background Shell

Archive

Blogroll

Disclaimer
I work for Microsoft.

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 220
This Year: 20
This Month: 0
This Week: 0
Comments: 533

Sign In

 Wednesday, July 30, 2008
Wednesday, July 30, 2008 7:15:16 PM (Pacific Daylight Time, UTC-07:00) ( )

If you have a PowerShell script that you are calling from cmd.exe, you might run into the following error:

Write-Host : The OS handle's position is not what FileStream expected. Do not use a handle simultaneously in one FileStream and in Win32 code or another FileStream. This may cause data loss.

This is bug in PowerShell, and happens when:

  • a PowerShell command generates both regular and error output
  • you have used cmd.exe to redirect the output to a file
  • you have used cmd.exe to merge the output and error streams

For example:

PowerShell -Command '"start"'; Write-Error "Foo"; '"end"' > c:\temp\redirect.log 2>&1

One workaround is to use Start-Transcript for file logging (rather than cmd.exe) or have PowerShell do the error stream redirection.

However, if you don't have control over your logging, you can add the following snippet to any scripts that get launched this way.

Note: this is an unsupported workaround. It will almost definitely break as future versions of PowerShell are released.

 

V1

$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
$consoleHost = $host.GetType().GetField("externalHost", $bindingFlags).GetValue($host)
[void] $consoleHost.GetType().GetProperty("IsStandardOutputRedirected", $bindingFlags).GetValue($consoleHost, @())
$field = $consoleHost.GetType().GetField("standardOutputWriter", $bindingFlags)
$field.SetValue($consoleHost, [Console]::Out)

V2 CTP2

$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
$objectRef = $host.GetType().GetField("externalHostRef", $bindingFlags).GetValue($host)

$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetProperty"
$consoleHost = $objectRef.GetType().GetProperty("Value", $bindingFlags).GetValue($objectRef, @())

[void] $consoleHost.GetType().GetProperty("IsStandardOutputRedirected", $bindingFlags).GetValue($consoleHost, @())
$bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
$field = $consoleHost.GetType().GetField("standardOutputWriter", $bindingFlags)
$field.SetValue($consoleHost, [Console]::Out)

 

How does this work, and why does this happen in the first place? When PowerShell sends output to its output stream the first time, it keeps a reference to the output stream for future use. However, this output stream is really a wrapper around a lower-level stream. When cmd.exe writes to the output stream, it writes to the lower-level stream. This makes the .NET wrapper complain that the underlying stream has changed from beneath it.

This workaround modifies some private engine state to not keep a reference to the output stream -- but instead to re-examine the output stream on every use.

Comments [0] | | # 
 Thursday, July 24, 2008
Thursday, July 24, 2008 5:11:22 PM (Pacific Daylight Time, UTC-07:00) ( )

Once in awhile, we get questions about the best practice for PowerShell's execution policy when it is included as part of a standard desktop image.

This is one of the main reasons that PowerShell ships with a "Restricted" execution policy. Since you have to explicitly enable or install PowerShell, we've frequently been asked why we aren't more permissive by default. After all, you did just install / enable it! The reason is that separating the execution policy decision from the installation decision gives you a lot more freedom on installing PowerShell. It lets you push PowerShell to your whole enterprise via SMS (or include on a standard image,) and then later selectively configure the Execution Policy via Group Policy or other mechanisms.

When it comes to configuring these systems, the answer to this is largely determined by who will be using PowerShell. Is it primarily the end users, or as a target for logon scripts? The 80% rule is key here. You want to make a well-reasoned decision that applies to as many of your systems as possible, rather than have everybody make an un-informed decision on their own.

If it’s mostly for logon scripts, and you have a certificate you can sign these scripts with:

  • Set the execution policy to AllSigned
  • Add your signing certificate to the TrustedPublisher store

If scripting is likely to be extremely common (as with Exchange,) or you can’t obtain a code signing certificate, set the execution policy to RemoteSigned.

See here for some more guidance on this: http://www.leeholmes.com/blog/3rdPartiesAndPowerShellExecutionPolicies.aspx

As an brief caveat for logon scripts, some machines are configured to treat UNC paths as the same security zone as the internet (as opposed to the intranet.) This is Internet Explorer's "Enhanced Security Configuration." In this case, PowerShell responds the same as the Explorer Shell when it runs scripts from a UNC path: “While scripts from the internet can be useful, this script can potentially harm your computer. Do you want to run <script>?”

One way to fix this is by adding the source server to Internet Explorer’s Trusted Sites, or changing the “UncAsIntranet” configuration property (http://technet.microsoft.com/en-us/library/bb457150.aspx). This is also covered on page 341 of the (my) PowerShell Cookbook.

As another caveat, it is currently not possible to directly assign a .ps1 script as a logon script, since the architecture that enables this implies that you must be able to double-click on a PowerShell script to run it. We don’t want double-clicking to run a script by default for security reasons, so you can write a VBS script or batch file to call PowerShell with the correct arguments. The team blog goes into a bit more detail on this.

Comments [0] | | # 
 Tuesday, June 24, 2008
Wednesday, June 25, 2008 4:17:45 AM (Pacific Daylight Time, UTC-07:00) ( )

Last night, I flew my first half-hour as Pilot in Command of N8756C, a 1977 Piper Warrior. In flying, a pilot’s First Solo is a huge milestone. After hours of training and ground study, you finally take to the air on your own.

FirstSolo

The most difficult part of flying is getting back down, so the First Solo is three takeoffs and landings -- about half-an-hour in the traffic pattern. It seemed to be the flight that would never come, though.

We first planned our pre-solo flight for Thursday, followed by the solo on Sunday. The pre-solo flight is a comprehensive review, so we booked the airplane from 5 to 7 PM. When we went through our pre-flight checklist, we realized that none of the avionics were working: I could hear myself, but couldn’t speak with my instructor. We tried everything we could to resolve the problem (including accidentally enabling the Emergency Locator Transmitter / Distress Beacon,) but couldn’t fix it. Dejected, we re-parked the plane and returned to base. I later learned that the squelch on the radio was wrongly adjusted. The squelch control determines the signal strength required for transmission. It is normally used to suppress low-level static, but in this case was set to exclude almost everything.

With that flight aside, we re-booked for Sunday – this time for both the pre-solo and solo flight.

Sunday came around bright and sunny, as did our 6 PM flight time. We patiently waited for our plane to arrive back from a previous lesson, filling time with some ground work in the meantime. We planned a flight to Thun Field, with detours for maneuver practice and instrument flying. Then, we waited some more. And some more. The plane finally got back 75 minutes late, but we went out hoping to get at least part of the lesson done.

When we got to the plane for our pre-flight checklist, we of course found it bone-dry. The gas truck took seemed to take its sweet time, but the real kicker came as we prepared to taxi out of the parking area. There was traffic to our left: a brand spanking new Boeing 737 being towed SLOWLY back toward the Boeing hangar in all of its unpainted glory. By slowly, I mean walking pace. This taxi would have taken 45 or 50 minutes, so we had to call off that flight, as well. We re-booked for Monday: 5 to 9 PM.

Boeing 737

Monday (yesterday) finally came around, and my study work for Sunday night was to read up on how to use the VOR system for navigation, rather than use the primarily visual navigation we were previously planning. VORs are directional beacons that let a flight instrument determine the direction from the VOR ground station to your airplane. To find a specific point, you generally use two VORs at the same time to triangulate your position.

The flight was intense. It was 2.8 hours in the air and reviewed:

  • Slow flight, where you fly and maneuver at near-stall speeds
  • Power-On and Power-Off stalls, and stall recovery
  • Ground reference maneuvers (Turns around a point, S-turns, rectangular courses)
  • Steep turns and standard turns
  • Emergency procedures (We “lost our engine,” so I had to pick and prepare for a landing)
  • Go-Arounds (Calling a mulligan on un-safe or un-certain landings)
  • 5 landings at Thun field, with my instructor having his eyes closed for the final 3 or 4

I was recording this on my GPS, so here are some highlights. First, the Thun Field landings:

Thun Field landings

Then, some ground-reference maneuvres:

image

On the way back to Renton airport, we also did 30 minutes of Instrument Training. For instrument training, you don a mask that prevents you from seeing outside the cockpit, and maneuver the airplane by instruments alone.

When we finally landed back at Renton airport, we practiced another 2 landings, finally coming to a full stop at the tie-down area. My instructor got out, wished me luck, and watched as went through the pre-flight checklist again. I alerted the tower as I prepared to taxi:

Renton Ground, this is Warrior 8756 Charlie at North Tower. Student pilot on first solo, requesting closed traffic with information Alpha.

I was given clearance to taxi to runway 15 (150 degrees, roughly north to south,) and prepared for my first flight. I waited for a minute as a few other planes landed, and then was given clearance for takeoff. I had too much power on my first run around the traffic pattern, so ended up lining up with the runway a little too high. Rather than try to rescue the landing in a potentially nervous state, I let the tower know I was doing a go-around, and climbed back to the traffic pattern. The controller mentioned that the winds were fairly variable and gusty, approaching 8 knots at times. She offered the south to north landing on runway 33, which I declined (as the winds weren’t affecting my landings.) The second attempt was much better, marking my first landing as Pilot in Command.

Now for landing number two.

Take-off is usually a breeze, but the nose appeared unusually high as I gave the plane full power on the ground. I quickly brought the power down, and exited one of the taxiways to determine the cause. After checking everything (flaps, elevator, etc,) I realized it was just a mind trick: I had been in a basically nose-down attitude (for the descents) for the last 10 or 15 minutes, so it was a lot like Velocitization in a car. You get so used to something (speed in a car, or nose angle in an airplane,) that it becomes your mind’s new definition of normal. You combat velocitization once you realize you have it, so I requested clearance to taxi back on the runway and complete my landings.

In the air again, the controller mentioned that they were just about to close, and asked if I wanted her to stay on. I didn’t want to keep her late, so I let her know I’d make my next landing a full-stop. She was awesome, and said she had no problem staying so I could get the solo done, so I continued with my next landing – still OK, but with definite room for improvement.

My final takeoff is where it really kicked in. After lifting into the air, an enormous thrill washed over me as I took a relaxed breath of fresh air. It was physically impossible for me to NOT land a third time. I grinned as I made my final approach, and settled in for the smoothest and straightest landing yet.

As recorded by my GPS:

image 

As I taxied off the runway, the tower controller congratulated me on my landings as I thanked her for her help. The landings were nothing for the highlight reels, of course, but it gave a satisfying end-cap to 30 minutes that I will never forget.

And the ripped shirt to prove it? Well, in-line with tradition, my instructor cut up the back of my shirt. Before modern electronics, in-cockpit communication was mainly through shirt tugging and hollers from the instructor that sat behind you. Cutting off the back of the shirt symbolizes this very important step -- as your instructor won't need to be yanking on it any more.

Comments [5] | | # 
 Wednesday, June 04, 2008
Wednesday, June 04, 2008 3:40:28 PM (Pacific Daylight Time, UTC-07:00) ( )

One question that comes up fairly often when dealing with (or writing!) secure cmdlets is how to properly handle usernames and passwords. The solution there is to use (or make) the -Credential parameter of type PSCredential. A PSCredential object helps ensure that your password stays protected in memory, unlike cmdlets that accept a straight username / password combination.

If a parameter is of type PSCredential, PowerShell supports several types of input:

  • empty: If you supply no input to a mandatory -Credential parameter, PowerShell prompts you for the username and password.
  • string: If you supply a string to the -Credential parameter, PowerShell treats it as a username and prompts you for the password.
  • credential: If you supply a credential object to the -Credential parameter, PowerShell accepts it as-is.

This is great for interactive use, but what if you want to write an automated script for a cmdlet that accepts a -Credential parameter? The solution lies in passing a pre-constructed PSCredential object. The solution to this is covered by recipe 16.9 in the PowerShell Cookbook:

 

Securely Store Credentials on Disk

Problem

Your script performs an operation that requires credentials, but you don’t want it to require user interaction when it runs.

Solution

To securely store the credential’s password to disk so that your script can load it automatically, use the ConvertFrom-SecureString and ConvertTo-SecureString cmdlets.

Save the credential’s password to disk

The first step for storing a password on disk is usually a manual one. Given a credential that you’ve stored in the $credential variable, you can safely export its password to password.txt using the following command:

PS >$credential.Password | ConvertFrom-SecureString | Set-Content c:\temp\password.txt

Recreate the credential from the password stored on disk

In the script that you want to run automatically, add the following commands:

$password = Get-Content c:\temp\password.txt | ConvertTo-SecureString

$credential = New-Object System.Management.Automation.PsCredential `

    "CachedUser",$password

These commands create a new credential object (for the CachedUser user) and store that object in the $credential variable.

Discussion

When reading the solution, you might at first be wary of storing a password on disk. While it is natural (and prudent) to be cautious of littering your hard drive with sensitive information, the ConvertFrom-SecureString cmdlet encrypts this data using Windows’ standard Data Protection API. This ensures that only your user account can properly decrypt its contents.

While keeping a password secure is an important security feature, you may sometimes want to store a password (or other sensitive information) on disk so that other accounts have access to it anyway. This is often the case with scripts run by service accounts or scripts designed to be transferred between computers. The ConvertFrom-SecureString and ConvertTo-SecureString cmdlets support this by allowing you to specify an encryption key.

When used with a hard-coded encryption key, this technique no longer acts as a security measure. If a user can access to the content of your automated script, they have access to the encryption key. If the user has access to the encryption key, they have access to the data you were trying to protect.

Note: Due to limitations in Version 1 of PowerShell, passwords encrypted with a specific encryption key can only be successfully decrypted by the same instance of PowerShell.exe process. Trying to decrypt the passwords with a different PowerShell.exe process will not be successful. To encrypt the passwords to disk in a way that can be read by other processes, use the .NET Encryption APIs: http://poshcode.org/116.

Although the solution stores the password in a specific named file, it is more common to store the file in a more generic location—such as the directory that contains the script, or the directory that contains your profile.

To load password.txt from the same location as your profile, use the following command:

$passwordFile = Join-Path (Split-Path $profile) password.txt

$password = Get-Content $passwordFile | ConvertTo-SecureString

To learn how to load it from the same location as your script, see “Find your Script’s Location.”

For more information about the ConvertTo-SecureString and ConvertFrom-SecureString cmdlets, type Get-Help ConvertTo-SecureString or Get-Help ConvertFrom-SecureString.

Comments [0] | | # 
 Tuesday, June 03, 2008
Wednesday, June 04, 2008 6:42:09 AM (Pacific Daylight Time, UTC-07:00) ( )

After what seems like an eternity, I finally got to start my official flight training. As a Canadian (AKA "Alien,") applying for flight school also means getting your fingerprints taken and being put in a special registration system. My training permission cleared late last week, so I booked my first lessons as soon as I could. In fact, for a government agency, the flight registration process was surprisingly efficient. Finding a place to get TSA-approved fingerprints outside of work hours? Well, that's a different story.

Anyways.

One of the things I really missed about my intro flights was a good understanding of exactly where I traveled during the flight. I remembered some landmarks, but didn't really catch on to many streets or things I knew from my ground travels. Recording my tracks through a GPS system seemed to be the best approach, so I finally settled on a Lowrance Airmap 2000C. Its features rival most of the top-end aircraft GPS products (aside from XM weather and traffic, which I'm not in the market for,) at a fraction of the price. For about the same cost, I would have been able to use some computer-based GPS software on my Tablet PC, but messing around with a Tablet PC in the cockpit seems to be a lot more trouble than it's worth.

clip_image001

I took it on a long camping trip during the Memorial Day long weekend, and it was fun watching it trace my path around the Puget Sound. It warned me plenty as I passed through the restricted airspaces (McChord airforce base, SeaTac airport, etc,) so luckily I wasn't actually flying.

On Thursday, I went for my first real lesson, flying AcuWings' Piper Warrior PA28-151. This is the kind of plane I want to do most of my training on -- as the Cirrus SR20 (while newer and nicer) felt like it might prevent me from learning some of the subtle basics -- like rudder control, or use of the nose-wheel while on the ground.

clip_image001[5]

In the introductory flights, the instructor does almost all of the ground work, aside from giving you a brief overview of what they are doing. For your first training flight, though, you start to take over the technical preparations: aircraft examination, engine start procedures, pre-takeoff work, and the post-flight run-down. We spent about 45 minutes on these preparations, learning the how and why behind 80+ items in the pre-flight and post-flight checklists.

In the air, we practiced straight and level flight, ascents, descents, and (level, ascending, descending) turns. Like the flight-preparation work, non-introductory training means that you begin to take over much more of the landing preparations. During this flight, we did 2 landings, one being a touch-and-go. For both, I flew the traffic pattern, lined up the approach, and helped guide us toward the runway. I did a little less of the actual landing than I had done during my introductory flights, but I think that was probably because of higher wind. This flight gave me 0.8 hours of qualified dual-instruction to apply to my flight training.

On Sunday, we put in another 1.1 hours of dual instruction, with the learning curve continuing just as steep. Since I had been taught the checklists, pre-flight this time was all my own – including the inevitable splashing of leaded, high-octane Avgas on my hands while testing for fuel contaminants. As we prepared for takeoff, I was shown how to talk with the tower, request permission to taxi, take-off, and practice landings. That's a massive system of verbal cryptography that will take a while to get used to, but you feel like part of a club when you're doing it.

While in the air, we practiced slow flight (near-stall conditions,) stalls, and recovering from stalls. These are the most common "emergency" situations in flight, and are relatively easy to recover from as long as you know what you are doing. If you haven't practiced them, then you'll just panic and donate some iron to the earth's mantle.

After practicing those, we started working on landings. As a skill in flying, landings are a unique breed. They are a skill within a skill: a technique that gets lavish attention in the context of flying, but completely independent of it. Pilots obsess about landings the same way that cigar aficionados gush about about the tower of ash emanating from their cigar, or how developers delight in "Perl Golf,"

It's a journey that's just beginning -- I can't wait for more.

Comments [1] | | # 
 Saturday, May 31, 2008
Sunday, June 01, 2008 3:20:24 AM (Pacific Daylight Time, UTC-07:00) ( )

Once in awhile, you'll see a PowerShell screenshot that is still black and boring like the traditional cmd.exe prompt. Other times, you'll see the fancy blue window you've come to love. Why the difference? And if you're suffering from black window syndrome, what can you do about it?

image

Before shipping Version 1, our marketing and design teams set to make the PowerShell window a unique and marquee experience. What form did this take?

  • Blue. Specifically, Red: 1, Green: 36, Blue: 86. -- In hex, 012 456. That number is much too cool to be a coincidence, but I haven't done the social archeology to find out why :)
  • Lucida Console. Lucida Console is infinitely more readable than the raster fonts, and ships with all versions of Windows. Consolas is a beautiful alternative, but isn't broadly available.
  • Window Size. Long gone are the days of 640x480 monitors being the most common, so PowerShell's default window gives about 800x600 of usable space.
  • Screen Buffer. Likewise, long gone are the days of 256mb of memory being the most common, so PowerShell's default window gives a dreamy 60 pages of history buffer. This is a shell you want to live in. And when you live in a shell, you  want to be able to see what you've done.
  • Quick Edit. Quick Edit mode lets you copy and paste easily to and from the console window. It is the setting that most console users enable once they realize it exists, and long gone are the days of mouse-driven console applications.

Now, even with all of these improvements, you still see screen shots of PowerShell prompts blinking desolate in the confines of a traditional black console window. Why is this?

PowerShell customizes its window through the shortcut properties in its Start Menu link. When you launch PowerShell through the Start | Run dialog or some other app launcher, these shortcut properties don't apply. Surprisingly, Windows does not support a mechanism to make these console customizations machine-wide. Windows supports per-user console customizations in the the HKCU hive of the registry, but there's usually no "Current User" during a PowerShell install. And even if there were, any customizations would not apply to user accounts created after PowerShell was installed.

So how do you get PowerShell's Noble Blue if you prefer Start | Run? Here's a script from the PowerShell Cookbook that does exactly that -- for your current user account.:

## From Windows PowerShell, The Definitive Guide (O'Reilly)
## by Lee Holmes (
http://www.leeholmes.com/guide)

Push-Location
Set-Location HKCU:\Console
New-Item ".\%SystemRoot%_system32_WindowsPowerShell_v1.0_powershell.exe"
Set-Location ".\%SystemRoot%_system32_WindowsPowerShell_v1.0_powershell.exe"

New-ItemProperty . ColorTable00 -type DWORD -value 0x00562401
New-ItemProperty . ColorTable07 -type DWORD -value 0x00f0edee
New-ItemProperty . FaceName -type STRING -value "Lucida Console"
New-ItemProperty . FontFamily -type DWORD -value 0x00000036
New-ItemProperty . FontSize -type DWORD -value 0x000c0000
New-ItemProperty . FontWeight -type DWORD -value 0x00000190
New-ItemProperty . HistoryNoDup -type DWORD -value 0x00000000
New-ItemProperty . QuickEdit -type DWORD -value 0x00000001
New-ItemProperty . ScreenBufferSize -type DWORD -value 0x0bb80078
New-ItemProperty . WindowSize -type DWORD -value 0x00320078
Pop-Location

Comments [0] | | # 
 Tuesday, May 20, 2008
Tuesday, May 20, 2008 7:47:31 AM (Pacific Daylight Time, UTC-07:00) ( )

If you're testing your PowerShell scripts (manually or automatically,) one of the first questions you'll end up asking yourself is, "did I test enough?"

This is common problem in all of software development. To the rescue is a simple metric known as "Code Coverage" – a measure of how much code you exercised during the testing of that code.

However, the measurement is just the end result – you need a tool to get you there. When it comes to measuring code coverage in PowerShell scripts, though, there simply aren't any tools yet.

Like performance measurement tools, Code Coverage tools are sometimes driven by instrumentation of the source code, and sometimes driven by sampling the code during runtime. Instrumentation gives the highest accuracy, but we can go a long way by runtime analysis alone. To accomplish that, we'll use our favourite feature to abuse – PowerShell script tracing. The last time we pushed it, we got a sampling profiler out of the deal. Let's do it again for code coverage.

Take, for example, the following source code:

trap { "Error handling!"; continue }                                  
                                                                      
"Got here"                                                            
                                                                      
if($args[0] -eq "Test")                                               
{                                                                     
   "Got TEST as an argument"                                          
}                                                                     
elseif($args[0] -eq "Err0r")                                          
{                                                                     
   throw "Catch Me!"                                                  
}                                                                     
else                                                                  
{                                                                     
   "Didn't get TEST as an argument"                                   
}     

We want to run through three parameters that it takes, and make sure we're exercising everything. Notice how we're even being diligent by testing the "Error" case!

PS C:\temp> $tests = @()                                              
PS C:\temp> $tests += { .\Test-CodeCoverage.ps1 Test }                
PS C:\temp> $tests += { .\Test-CodeCoverage.ps1 SomethingElse }       
PS C:\temp> $tests += { .\Test-CodeCoverage.ps1 Error }               
PS C:\temp>                                                           
PS C:\temp> .\Get-ScriptCoverage.ps1 .\Test-CodeCoverage.ps1 $tests 

What does that give us?

trap { "Error handling!"; continue }                                  
                                                                      
"Got here"                                                            
                                                                      
if($args[0] -eq "Test")                                               
{                                                                     
   "Got TEST as an argument"                                          
}                                                                     
elseif($args[0] -eq "Err0r")                                          
{                                                                     
   throw "Catch Me!"                                                  
}                                                                     
else                                                                  
{                                                                     
   "Didn't get TEST as an argument"                                   
}                                                                     
Coverage Statistics: 66.6666666666667%                                
PS C:\temp>   

Ouch! Why is the error handling code (in red) not being hit? Ah, after further investigation, it turns out that we have a typo in our string comparison. We fix it:

...
elseif($args[0] -eq "Err0r")
...

Becomes

...
elseif($args[0] -eq "Error")
...

And run code coverage again:

trap { "Error handling!"; continue }                                  
                                                                      
"Got here"                                                            
                                                                      
if($args[0] -eq "Test")                                               
{                                                                     
   "Got TEST as an argument"                                          
}                                                                     
elseif($args[0] -eq "Error")                                          
{                                                                     
   throw "Catch Me!"                                                  
}                                                                     
else                                                                  
{                                                                     
   "Didn't get TEST as an argument"                                   
}                                                                     
Coverage Statistics: 100%                                             
PS C:\temp>    

Much better.

Here is the script – under 80 lines of (heavily commented) code:

## Get-ScriptCoverage.ps1                                             
## Test the script named by $testScript for code coverage.            
## The command given by $command must exercise this named             
## script.                                                            
param([string] $testScript, [ScriptBlock[]] $command)                 
                                                                      
# Store the content of the script to be tested                        
$fileContent = gc $testScript -ea Stop                                
                                                                      
## Start a transcript, and log it to a file                           
$tempFile = [IO.Path]::GetTempFilename()                              
Start-Transcript $tempFile                                            
                                                                      
## Turn on line-level tracing, run the command(s),                    
## then turn off line-level tracing again.                            
Set-PsDebug -Trace 1                                                  
$command | Foreach-Object { & $_ }                                    
Set-PsDebug -Trace 0                                                  
                                                                      
## Stop the transcript                                                
Stop-Transcript                                                       
                                                                      
## Get the result of the script coverage run                          
$coverageContent = (gc $tempFile) -match "^DEBUG:"                    
Remove-Item -LiteralPath $tempFile                                    
                                                                      
Clear-Host                                                            
                                                                      
## Clean up interference from other scripts                           
$scriptLines = @()                                                    
$processedLines = @{}                                                 
                                                                      
foreach($originalLine in $coverageContent)                            
{                                                                     
    # Make sure we only process unique lines in the                   
    # transcript                                                      
    if($processedLines[$originalLine]) { continue }                   
    $processedLines[$originalLine] = $true                            
                                                                      
    ## Recover as much as possible from the original script line      
    ## without its debugging information                              
    $originalLine = $originalLine -replace " <<<< ",""                
    $line = $originalLine -replace '\D*\d+\+ (.*)','$1'               
                                                                      
    ## Go through each line in the original script, and see if        
    ## this is actually in the script                                 
    foreach($fileLine in $fileContent)                                
    {                                                                 
        ## If it is, add the debug line to the list of lines          
        ## covered by this scenario                                   
        if($fileLine.Contains($line))                                 
        {                                                             
            $scriptLines += $originalLine                             
        }                                                             
    }                                                                 
}                                                                     
                                                                      
## Find out which line numbers were covered                           
$coveredLines = $scriptLines |                                        
    % { $_ -replace '\D*(\d+)\+ .*','$1' } | Sort -Unique             
                                                                      
$coverageCount = 0                                                    
$possibleCoveredLines = 0                                             
for($counter = 1; $counter -le $fileContent.Count; $counter++)        
{                                                                     
    $color = "Red"                                                    
    $line = $fileContent[$counter - 1]                                
                                                                      
    ## Ignore comments, blank lines, curly                            
    ## braces, and fall-through conditional statements                
    ## in coverage computation (as they are never                     
    ## traced in Set-PsDebug tracing                                  
    if(($line -notmatch '^\s*#') -and                                 
       ($line -notmatch '^\s*{\s*$') -and                             
       ($line -notmatch '^\s*}\s*$') -and                             
       ($line -notmatch '^\s*else') -and                              
       ($line -notmatch '^\s*param\(') -and                           
       ($line.Trim()))                                                
    {                                                                 
        $possibleCoveredLines++                                       
    }                                                                 
    else { $color = "Gray" }                                          
                                                                      
    ## If this line was hit in code coverage, colour it               
    ## green                                                          
    if($coveredLines -contains $counter)                              
    {                                                                 
        $color = "Green"                                              
        $coverageCount++                                              
    }                                                                 
                                                                      
    ## Display the line in the appropriate colour                     
    Write-Host -Fore $color $line                                     
}                                                                     
                                                                      
## Output the coverage statistics                                     
Write-Host ("Coverage Statistics: " +                                 
    "$($coverageCount / $possibleCoveredLines * 100)%")               
Comments [0] | | # 
 Tuesday, May 06, 2008
Tuesday, May 06, 2008 5:03:21 PM (Pacific Daylight Time, UTC-07:00) ( )

For a long time, I've been thinking about learning to fly. One of the random long-distance-drive conversations I like to have is, "What job would you do if you were for some reason prevented from doing what you do now?" For me, flying has always been something I would entertain, but only romantically. The downsides to doing it professionally are huge -- the time away from your family and strict seniority-based promotions being the two largest factors. So instead, I wanted to reward myself with flying lessons the next time I thought it was appropriate. A King of the Hill episode, of all things, changed my mind on that.

image

In that episode, Hank (the father) and Peggy (the mother) were going through marital problems, and spoke with a counselor. The counselor asked them what they wanted to do when they retired -- something they had been planning toward for a long time. Their dream was to buy some motorcycles and tour the United States together. The counselor's suggestion was simple -- holding off on buying the bikes doesn't really help anything, so just buy the darn things and start enjoying yourselves! The cross-country tours can wait until you have the time to do them, but little local trips can happen right now.

That started the slow chemical reaction in my brain, which ultimately led me to start investigating flying lessons more seriously.

My typical obsessive online research turned up lots of information about learning to fly. Tom Unger’s “Flying Lessons” journal was indispensable, gave a lot of great background information, and what to expect with the process. However, I found very little about picking a flight school around the Seattle area, and sent a mail to an internal mailing list asking for opinions. Four suggestions came up most frequently:

I ruled out Galvin immediately – their responses to customer complaints on http://www.airnav.com/airport/KBFI/GALVIN were ridiculous and showed complete disregard for their customers.

I ended up looking for operations out of the Renton Municipal Airport (KRNT) as my primary factor. It’s close to my house, and commute time seems to be the most important element in choosing a base. Airport traffic and other factors can be learned at other airports (such as Crest Airpark for its crazy small field, and Boeing Field for its busy airspace,) since most of your practice take-offs and landings come from explicit training exercises (as opposed to being dictated by the base you fly from.)

I first checked out Pro-Flight during a 9:00 AM intro flight, taking up a well-loved Cessna 172. Introductory flights are an amazing deal – even as a leisure activity. They cost under $100 for a scenic tour of the Puget Sound, and you get to do most of the flying yourself.

image        image

The biggest challenge (as compared to driving a car) was adapting to a new mental model, and adapting to a very manual control system. When taxiing, having 4 control surfaces at my feet was pretty complex – seeing as I kept on thinking of it like controlling handles on a bike steering column. I would instinctively push right expecting to turn left, but got the hang of it more when I thought about it as differential braking (even when I was using the nose wheel part of the control.) My only point of minor helmet fire came at this point – when we needed to use differential braking to make a turn, but my feet were only on the nose wheel portion of the control (as suggested by the instructor.)

image


Oh, and ignoring my internal alarm bells is hard enough when starting an automatic transmission car without depressing a clutch – an airplane was much worse.

What surprised me most was how much input you had to give the controls. It felt like there was a good 8 inches of travel in the foot pedals – much more than I was expecting. I was sure I was going to rip the knob off of the dash when I was priming the engine.

After we taxied, takeoff went pretty well. There was no traffic, so we didn’t burn engine hours waiting in line. Actually leaving the pavement was weird, as I again was expecting to need a subtle touch. After pushing the throttle forward slowly like you see them do on big airplanes, the instructor told me to just gun it :) The climb felt surprisingly manual as well, although the instructor did give me a rate of ascent to aim for, which helped a lot. Once I visually leveled off, the instructor pointed out that I was still climbing.  The nose definitely points a lot lower during steady flight than it does during takeoff, but adapting to that came shortly.

We did some minor orientation and directional work, and even got to do some nice steep turns. The ceiling was really indefinite, and we had to avoid some cloud cover. The runway approach went really well as we approached from the North. I guess since there was no traffic, we basically did the 45 degree entry directly into an extended final for the traffic pattern. Lining up felt pretty natural, although I made the approach too low – I was aiming for the numbers instead of the first dash.

image


So all-in-all, it was a great time up – and they seem like a very capable flight school.

The next day, I took a flight with AcuWings, and I can understand why there is so much support for them. Their office was a little hard to find at first (barely any signs on an already nondescript office building,) but they seemed well-run. Their office was off-base, so we had a very short drive across the road to the actual airplanes. They will be getting a building on the field in the near future, so that will be convenient. I followed the owner to the field who, despite his precision flying and deep knowledge of flight rules, failed to use his signal light even once.

For that intro flight, we flew their Cirrus SR20 (since I wanted to try a completely different airplane.) Different it was – an ’07 model, tons of electronics, tons of power, and no traditional yoke! You control the throttle with your right hand, and control the direction with a joystick-like device on your left. The weather was beautiful: in the mid-60s with unlimited ceiling, and 10 miles of visibility. This made for a much busier airport, which was also good to experience.

image     image


My daughter joined us on this ride -- an experience I was thrilled to have been able to share with her.

The aircraft was a lot more responsive than the Cessna, so I initially pitched it up to like 1100 ft/min correcting to the proper 700 ft/min or so during takeoff. We climbed to 3500 feet, and were treated to an awesome view of Mount Rainier. With all of the interactive electronics and gadgets, I had to consciously stop myself from getting too absorbed in the pretty displays, and instead use the cockpit window for what it was intended for :) We did some more level turns, played with the autopilot a bit, and practiced throttle control.

I was amazed to learn that airplanes will “skip” in the atmosphere. If you let them go with wings level and drop the power, they will just settle on a new lower altitude that matches the new power.

After about half an hour in the air, we came back to the Renton base and ran a more typical approach. We flew parallel to the runway (which I was surprised to realize was 30ish degrees off of the North-South axis,) and made a short final from the South due to heavy traffic. The landing went well, probably because it was almost entirely under the control of my instructor :)

After landing, it became very clear to me that this hobby has much more in store for me.

Comments [1] | | # 
 Monday, March 31, 2008
Tuesday, April 01, 2008 5:23:22 AM (Pacific Daylight Time, UTC-07:00) ( )

A few years ago, we experimented with a "Live" version of Get-ChildItem that retrieved elegant textual context-based advertisements from the internet as you used PowerShell.

This has been very successful, but unfortunately, a small subset of our trial user population has experienced crashing behaviour due to these new shell modifications. We have been unable to reproduce the issue in our labs, so the users in question have sent us screen casts of the crash. Even still, we've been looking at the code so long that we still can't confirm the problem.

It would really help the PowerShell team if you could take a look and help us identify the issue.

I've posted the screen cast here: http://www.leeholmes.com/projects/powershell_crash/. Make sure you have sound enabled, as that is supposedly an important factor.

Comments [1] | | # 
 Friday, March 28, 2008
Friday, March 28, 2008 7:33:12 AM (Pacific Daylight Time, UTC-07:00) ( )

[Download: BgShell.zip]

BgShell is a proof of concept PowerShell host that explores the idea of "PowerShell Everywhere."

What if PowerShell was always at your fingertips – making quick calculations easier than launching Windows Calculator? Or, making a “quick ipconfig” with PowerShell just a keystroke away? BgShell offers PowerShell Everywhere, and instant gratification. Simply press Control-Alt-B, and the BgShell interface appears immediately. Press Control-Alt-B again to hide the shell. Closing the window only hides the shell, keeping it instantly available for your next command.

The concept of “PowerShell Everywhere” goes further than that, though. What if you could bind PowerShell script blocks to arbitrary keystrokes? BgShell supports that, too. If you use any keyboard Macro programs, BgShell can likely replace them -- and with a more powerful engine and scripting language, at that.

BgShell lets you define these in a host-specific profile. If you tend to write the same thing over and over, just define a hotkey for it:

## Boy, I type this a lot.
$keyMapping['Control,Alt,Q'] = @{ 
    Action = { SendString 'PowerShell Rocks!'
}

 

Or, rather than batting the mouse cursor out of the way when it obscures your typing, just press a hotkey:

## Move the annoying mouse pointer out of your way
$keyMapping['Control,Alt,Z'] = @{ 
    Action = {
        [Windows.Forms.Cursor]::Position = (New-Object System.Drawing.Point 0,0
    }
}

 

Or even bring some of your old Unix habits to the Win32 console:

$keyMapping['Control,L'] = @{
    KeypressHandled = { IsClassActive 'ConsoleWindowClass' };

    ## Console clear
    Action =  { SendKeys '{ESC}cls{ENTER}' }
}


 

At the same time, you’ve been building new habits in PowerShell. Why not cater to them?

$keyMapping['Control,F'] = @{
    KeypressHandled = { IsClassActive 'ConsoleWindowClass' };

    ## Console foreach-object
    Action =  { SendString '| foreach { $_. }'; SendKeys '{LEFT}{LEFT}' }
}

 

Keystroke automation of other programs opens up entire new realms of interactive management potential:

## Control,Alt,C -- Convert selected text in an Outlook Message
## into a code sample
$keyMapping['Control,Alt,C'] = @{
    KeypressHandled = { IsClassActive '_WwG' };
    Action =  { 
        Start-Sleep -m 100
        SendKeys "%o"
        Start-Sleep -m 500
        SendString "ff"
        Start-Sleep -m 500
        SendString "Courier New"
        SendKeys "{ENTER}"
        Start-Sleep -m 500
        SendKeys "%o"
        Start-Sleep -m 500
        SendString "fs"
        Start-Sleep -m 500
        SendString "9"
        SendKeys "{ENTER}"
        Start-Sleep -m 500
    }
}

 

Of course, your brain starts to hurt with so many hotkeys. In that case, maybe a memorable phrase is better?

## Get the current date

$stringMapping[ "**date" ] = { SendString (Get-Date) }

 

How about saving yourself from the tedium of template code?

## Get some input from the user
function Get-Input 
{ 
  param ($message = "Input : ", 
         $title = "Inputbox")

  $vbs = New-Object -com MSScriptControl.ScriptControl 
  $vbs.language = 'vbscript' 
  $vbs.addcode("function getInput() getInput = inputbox(`"$message`",`"$title`") end function") 
  $result = $vbs.Eval('getInput') 
  $result 
}

## Generate a C# property
$stringMapping[ "**prop " ] = 
{
    $template = @"
        /// <summary>
        /// Summary of what this property does
        /// </summary>
        public __TYPE__ __NAME__
        {
            get
            {
                return __NAMELOWER__;
            }
            set
            {
                __NAMELOWER__ = value;
            }
        }
        private __TYPE__ __NAMELOWER__;
"@

    $type,$name = (Get-Input -Message "Property type and name, such as 'String Foo': ") -split " "
    
    $template = $template.Replace("__TYPE__", $type)
    $template = $template.Replace("__NAME__", $name)
    $template = $template.Replace("__NAMELOWER__", ($name.Substring(0,1).ToLower() + $name.Substring(1)))

    $autoIt.ClipPut($template)
}

 

The sky really is the limit.

For more examples, see the example profile. Place it in the same directory as your regular PowerShell profile. BgShell surfaces this profile location through the standard $profile variable.

 

Note

·         Vista introduces security enhancements that prevent low-privilege applications from being notified of keystrokes in (or sending keystrokes to) high-privilege applications. BgShell is marked to run as administrator so that it can respond in elevated windows.

Comments [5] | | #