# Friday, June 06, 2008

In the spirit of "tidying things up," and pushing out nearly-there projects, I turned my attention to http://www.codeplex.com/PSMobile. I have my fingers in way too many pies I think and I'm just not finding enough time to get things finished off - but enough is enough. I am trying to clear my plate, and this is fruit number #2 of that attempt at self-correction. I have some other important out of band work that I need to complete, but I find I cannot concentrate on that until these personal projects that have been niggling me for ages are dealt with. Anyway, lets dispel a couple of questions with a screenshot:

Requirements

  • ActiveSync 4.2 or higher (or Windows Mobile Device Centre 6.0+ on Vista) Download
  • A Windows Mobile device (PocketPC/SmartPhone 2002, 2003, 2003SE, Windows Mobile 5, 6 or 6.1)
  • Windows PowerShell 1.0 or 2.0 (CTP) Download

Features

wm61-device

  • Copy, Move, Delete items between folders on your device (including Storage Card) with standard PowerShell Cmdlets
  • Move/Copy files to/from your device and your desktop with ConvertTo-WMFile and ConvertFrom-WMFile
  • Get device information and manipulate and explore the registry with a rich device object returned from Get-WMDevice
  • Invoke-Item against remote items to or execute or trigger their associated applications
  • Invoke-Item with -Local switch to attempt to execute a remote file in the context of your local desktop (e.g. office docs or images/videos)
  • New "Mode" attributes specific to Windows Mobile file attributes: (I)nRom, Rom(M)odule
  • File/Folder objects' attributes can be modified with .Attributes properties just like FileInfos etc.
  • Tab completion with MoW's PowerTab Download

File Manipulation

A picture's worth a thousand words.

images-screenshot

Cmdlets and Definitions

Here's a table of the syntax for the included Cmdlets.

Cmdlet Definition WhatIf / Confirm
ConvertFrom-WMFile * [-Path] [-Destination] [-Force] [-Verbose] Yes
  [-LiteralPath] [-Destination] [-Force] [-Verbose] Yes
ConvertTo-WMFile * [-Path] [-Destination] [-Force] [-Verbose] Yes
  [-LiteralPath] [-Destination] [-Force] [-Verbose] Yes
Get-WMDeviceInfo [-Verbose]  
Get-WMMemoryInfo [-Verbose]  
Get-WMStoreInfo [-Verbose]  
Start-WMActiveSync [-Verbose]  
Stop-WMActiveSync [-Verbose]  
Start-WMProcess [-LiteralPath] [[-Arguments] ] [-Verbose]  
Get-WMDevice [-Verbose]  

* These Cmdlets that accept a path will bind to pipeline input via PSPath property name.

If you've got any problems, suggestions or ideas, please post into the discussions board on the web site. Have fun!

posted on Friday, June 06, 2008 7:00:15 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [5] Trackback
# Thursday, June 05, 2008

I took a few hours yesterday to "tidy up my room" so to speak, so I built a nice MSI installer, updated the help, CodePlex Wiki and examples and closed all bugs. This is probably the final release now that PowerShell 2.0 CTP2 has introduced support for eventing, so thanks for all the support.

New Features

  • Multiple named queue support and default queue with -QueueName parameter
  • Better COM support, window message pumping etc.
  • NoFlush / Peek parameter support for queue reading
  • Get-EventQueue command added for viewing queues and their message counts.

Cmdlet Name Changes

  • Get-Event -> Read-Event
  • Connect-EventListener -> Connect-Event
  • Disconnect-EventListener -> Disconnect-Event

Additionally, several niggling bugs closed (including the one where read-event -wait would return immediately with no events).

http://www.codeplex.com/pseventing

For an advanced example: Foreground / Background Swappable Downloads In PowerShell.

posted on Thursday, June 05, 2008 9:20:10 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Friday, May 23, 2008

UPDATE May 26th: You must run PowerShell v2.0 CTP in STA mode for this to work. Start the shell, then run "powershell -sta" from the command line to start a new version of the shell in "single thread apartment" mode (STA). This is required for WPF to work correctly.

That is a bit of a mouthful of a title for this post but it's the best I could come up with. This post takes some of James' scripty bits and Jaykul's scripty bits and shows you how to create a countdown timer written in PowerShell script that runs in the background without blocking input. Just like Jaykul's original clock, you can drag it around and right-clicking it will close it. His version was the current time and it also showed some system resources. I changed it into a countdown and removed the other nested graphs. When it hits 00:00:00 it turns red. Here's what it looks like:

countdown

Here's the source of invoke-background.ps1:

  1. param([string]$scriptName)  
  2.  
  3. # original script James Brundage (blogs.msdn.com/powershell)  
  4.  
  5. $rs = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()  
  6. $rs.ApartmentState, $rs.ThreadOptions = “STA”, “ReuseThread”  
  7. $rs.Open()  
  8.  
  9. # Reference the WPF assemblies  
  10. $psCmd = {Add-Type}.GetPowerShell()  
  11. $psCmd.SetRunspace($rs)  
  12. $psCmd.AddParameter("AssemblyName", "PresentationCore").Invoke()  
  13. $psCmd.Command.Clear()  
  14.  
  15. $psCmd = $psCmd.AddCommand("Add-Type")  
  16. $psCmd.AddParameter("AssemblyName", "PresentationFramework").Invoke()  
  17. $psCmd.Command.Clear()  
  18.  
  19. $psCmd = $psCmd.AddCommand("Add-Type")  
  20. $psCmd.AddParameter("AssemblyName", "WindowsBase").Invoke()  
  21.  
  22. $sb = $executionContext.InvokeCommand.NewScriptBlock(  
  23.     (Join-Path $pwd $scriptname)  
  24. )  
  25.  
  26. $psCmd = $sb.GetPowerShell()  
  27. $psCmd.SetRunspace($rs)  
  28. $null = $psCmd.BeginInvoke()  

Next, here's the modified clock script:

  1. param (  
  2.     [timespan]$period = (New-Object system.TimeSpan(0,5,0)),  
  3.     $clockxaml="<path to xaml file>\clock.xaml" 
  4. )  
  5.  
  6. ### Import the WPF assemblies  
  7. Add-Type -Assembly PresentationFramework  
  8. Add-Type -Assembly PresentationCore  
  9.  
  10. $clock = [Windows.Markup.XamlReader]::Load(   
  11.          (New-Object System.Xml.XmlNodeReader (  
  12.             [Xml](Get-Content $clockxaml) ) ) )  
  13.  
  14. $then = [datetime]::Now  
  15.  
  16. $red = [System.Windows.Media.Color]::FromRgb(255,0,0)  
  17. $redbrush = new-object system.windows.media.solidcolorbrush $red 
  18. $label = $clock.FindName("ClockLabel")  
  19. $done = $false 
  20.  
  21. # Create a script block which will update the UI  
  22. $updateBlock = {     
  23.    if (!$done) {  
  24.         # update the clock  
  25.         $elapsed = ([datetime]::Now - $then)  
  26.         $remaining = $null;  
  27.           
  28.         if ($elapsed -lt $period) {  
  29.             $remaining = ($period - $elapsed).ToString().substring(0,8)  
  30.         } else {  
  31.             $label.Foreground = $redbrush         
  32.             $remaining = "00:00:00" 
  33.             $done = $true 
  34.         }         
  35.         $clock.Resources["Time"] = $remaining 
  36.    }  
  37. }  
  38.  
  39. ## Hook up some event handlers   
  40. $clock.Add_SourceInitialized( {  
  41.    ## Before the window's even displayed ...  
  42.    ## We'll create a timer  
  43.    $timer = new-object System.Windows.Threading.DispatcherTimer  
  44.    ## Which will fire 2 times every second  
  45.    $timer.Interval = [TimeSpan]"0:0:0.50" 
  46.    ## And will invoke the $updateBlock  
  47.    $timer.Add_Tick( $updateBlock )  
  48.    ## Now start the timer running  
  49.    $timer.Start()  
  50.    if(! $timer.IsEnabled ) {  
  51.       $clock.Close()  
  52.    }  
  53. } )  
  54.  
  55. $clock.Add_MouseLeftButtonDown( {   
  56.    $_.Handled = $true 
  57.    $clock.DragMove() # WPF Magic!  
  58. } )  
  59.  
  60. $clock.Add_MouseRightButtonDown( {   
  61.    $_.Handled = $true 
  62.    $timer.Stop()  # we'd like to stop that timer now, thanks.  
  63.    $clock.Close() # and close the windows  
  64. } )  
  65.  
  66. ## Lets go ahead and invoke that update block   
  67. &$updateBlock 
  68. ## And then show the window  
  69. $clock.ShowDialog()  

...and finally the modified clock.xaml file:

  1. <Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' 
  2.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  3.         xmlns:system="clr-namespace:System;assembly=mscorlib" 
  4.         WindowStyle='None' AllowsTransparency='True' 
  5.         Topmost='True' Background="Transparent"  ShowInTaskbar='False' 
  6.         SizeToContent='WidthAndHeight' WindowStartupLocation='CenterOwner' > 
  7.    <Window.Resources> 
  8.       <system:String x:Key="Time">12:34.56</system:String> 
  9.    </Window.Resources> 
  10.  
  11.    <Grid Height="2.2in"> 
  12.       <Grid.ColumnDefinitions> 
  13.          <ColumnDefinition/> 
  14.       </Grid.ColumnDefinitions> 
  15.       <Label Name="ClockLabel" Grid.Column="2" Opacity="0.7" Content="{DynamicResource Time}" FontFamily="Impact, Arial" FontWeight="800" FontSize="2in" > 
  16.          <Label.Foreground> 
  17.             <LinearGradientBrush> 
  18.                <GradientStop Color="#CC064A82" Offset="1"/> 
  19.                <GradientStop Color="#FF6797BF" Offset="0.8"/> 
  20.                <GradientStop Color="#FF6797BF" Offset="0.4"/> 
  21.                <GradientStop Color="#FFD4DBE1" Offset="0"/> 
  22.             </LinearGradientBrush> 
  23.          </Label.Foreground> 
  24.       </Label> 
  25.    </Grid> 
  26. </Window> 

Important: you'll need to save all files into the same directory and fix up the path to the clock.xaml file in the start-countdown.ps1 script.

Have fun!

posted on Friday, May 23, 2008 5:04:18 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Wednesday, May 21, 2008

This question has been asked in various ways over the last few years and I don't believe an answer that suits everyone has been proffered yet. I think this is part of a broader problem space that needs to be solved, one that I (and many others) have spent a bit thinking about -- for me personally, it's been mostly in the pub strangely enough, usually with a pint in hand -- and while I don't profess to have the answer, I do spent most of my powershell time tinkering with providers and have some views on this ;-)

Firstly, if you haven't seen this suggestion I've raised on connect (Allow providers other than the filesystem provider to surface commands) then take a gander at it now. It suggests allowing providers other than the FileSystemProvider to surface commands by using a new ProviderCapabilities flag. For those not able to read this suggestion, the bottom line is that currently one can execute a command on the filesystem by using the following syntax:

ps c:\> .\test.bat
hello, world

However, if you had another provider that linked into a mobile device, the amazon s3 service or MSL skydrive (when are they going to release an API?) for example, you would allow execution of commands with the same syntax, e.g.

ps skydrive:\> cd ppts
ps skydrive:\ppts> .\mydemo.ppt
The term '.\mydemo.ppt' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
At line:1 char:8
+ .\mydemo.ppt <<<<

As you can see, this doesn't work.

What's important is having the same experience with similarly capable providers; e.g. those that can host executable content. Yes, you can implement support for invoke-item, but it's a bit discordant. One of the nicest features of powershell (and sometimes the most confusing) is that all providers - variable, function, environment, filesystem etc all hook into the same framework. There are some philosophically irksome differences like the fact that the  variable drive is the "default" provider, since dollar-qualified expressions are assumed to point there if not qualified with a drive name:

ps c:\> $host
Name             : ConsoleHost
Version          : 1.0.0.0
InstanceId       : 9d8a29bf-3d84-4ce6-8651-e0c72afb404b
UI               : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture   : en-CA
CurrentUICulture : en-US
PrivateData      : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy

so let's give a drive name this time:

ps c:\> ${c:test.bat}
@echo hello, world

This is a lot easier to understand once you realise that the '$' prefix is a grammatical shortcut into the IContentReader/IContentWriter interfaces on every provider; Much easier than just blindly committing to memory the method to read a variable and the method to read a file (imho). Once you introduce this capability into other providers, you then have to address ye olde $env:path variable. Currently this variable is imported from the system environment. The system, aka Windows, knows nothing about powershell and its drives. When using Get-Command, the search order for discovering commands is:

Aliases, Functions, Cmdlets, Scripts, Commands located in the directories specified by the Path environment variable, External scripts.

As we can see, Commands (5) are discovered via the $env:path variable. The other items (apart from 3 - cmdlets) all live in a flat namespace, so there's no path involved there.  I'd love if somehow it were possible to add any powershell path to this variable, even if they were limited to drive-qualified paths:

ps c:\> $env:path
c:\windows; c:\windows\system32; ...; s3:\utilities; "mobile:\storage card"; ...

Perhaps this information could be persisted inside PowerShell only so when the shell is exited, the path environment variable remains unchanged when viewed from the external windows system. This might mean that PowerShell paths would have to be appended in your profile at each load, but this isn't a bad thing either, IMO. So finally, on to the crux of the matter: disambiguation of identically named Cmdlets. Ultimately I don't believe there is a magic answer. This is solved in Windows by using the path variable, and so I believe it isn't such a bad idea to solve it with a path variable in powershell too. Behold $env:cmdletpath

ps c:\> $env:cmdletpath
Microsoft.PowerShell.Core; Microsoft.PowerShell.Host; Microsoft.PowerShell.Management; Microsoft.PowerShell.Security; Microsoft.PowerShell.Utility; VMWare.Commands.Utility

It's simple. It's optional. Snap-in qualified commands would continue to work, overriding the cmdlet search path. Instead of having to alias multiple commands when using a snapin that replaces a suite of built-in cmdlets, you can just re-jig the search path. Done. It's not the answer to everything, but it sure would make life a bit easier, no?

posted on Wednesday, May 21, 2008 8:36:39 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Tuesday, May 20, 2008

I have two machines at home here, one is a Vista SP1 laptop, the other an XP SP3 Desktop. The latter was recently patched with SP3 in a desperate attempt to prolong its dwindling desire to function in a reasonable fashion. It's what I call from a development box point of view, "Encrusted." Encrustation is the point at which you no longer have any idea what beta, CTP, evaluation or otherwise not recommended software is installed. It defies its digital nature by throwing up different errors each time its booted. Anyway, the SP1 Beta would not install on this machine, specifically the .NET 3.5 beta bits. I may investigate further, but frankly I think it's time for fenestrecide and a corresponding rebirth.

This led me to try updating my Vista laptop instead. If you download the VS2008 patch you'll notice it's only about 450KB. It's a stub which detects what's missing from your environment and downloads only what it needs. I found the installer to be extremely useless in terms of giving feedback over what it was doing. It just says "installing" and you have to watch a progress bar slowly creeping across with many stalls where your machine doesn't seem to be doing anything at all. This time I downloaded the separate .NET 3.5  SP1 beta bits which size about 220MB. I installed this first and it seemed to go OK. Next, I downloaded the VS patch and let it go ahead. Again many stalls where you're wondering if it's actually going to work at all. Eventually, it failed. After some examination of the logs, I discovered that it didn't like the post-RTM patches for supporting the Reference Source server ( Shawn Burke's Blog - Configuring Visual Studio to Debug .NET ). After removing these updates, I was able to progress to past this prior point of failure but by this time it was midnight, having started the process in earnest at around 8pm. I decided to leave it to run overnight.

In the morning I had a stalled install process and a dialog notifying me that files were in use and that I should shutdown "Machine Debug Manager," "Windows Sidebar Component" and "Windows Sidebar" - Vista GUI cruft - if I wanted to avoid a reboot. I stopped the MDM service and shutdown Windows Sidebar and chose "Retry" from the options of "Retry," "Cancel" and "Ignore." Even though I hit "Retry," the bland dialog displaying "installing" now switched to "Installation failed.. rolling back" - but thankfully, it was NOT rolling back. The status bar appeared stalled for about 10 minutes then popped up the same dialog, this time with only "Windows Sidebar" listed. This time I chose "Ignore" implicitly accepting the penalty of a reboot to allow the status bar to continue its inexorable journey to the right, all the time the text telling me that the installation had failed and it was rolling back. Ten more minutes and the install succeeded. To summarise, VS seems snappier, and everything seems to work fine. I'll post more if I discover anything of interest.

The usual suspects have more information:

Beta of .NET 3.5 and VS2008 SP1 is out (Scott Guthrie)

VS2008 and .Net 3.5 SP1 Beta - Should You Fear This Release- (Scott Hanselman)

.NET 3.5 SP1 Beta- Changes Overview (Patrick Smacchia)

posted on Tuesday, May 20, 2008 10:30:14 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback