Archive for May 2006

PowerShell and file version information   5 comments

I often want to get the version information about the files on my system.  Version information is provided as part of the System.Diagnostics.Process object, but I often want the version information about applications that aren’t running.  This script allows me to get that information.  I’ve written the script so it handles both piped input and command line arguments.  It uses the begin/process/end features of the scripting language, so I can get it to behave almost like a compiled cmdlet. 
 
Here’s what it looks like when I use it:
PS> ls c:\windows\*.exe | get-fileversion

ProductVersion   FileVersion      FileName
--------------   -----------      --------
1.6.0.2          1.6.0.2          C:\windows\Alcmtr.exe
1.1.0.27         1.1.0.27         C:\windows\alcwzrd.exe
6.00.2900.2180   6.00.2900.218... C:\windows\explorer.exe
5.2.3790.2453    5.2.3790.2453... C:\windows\hh.exe
5, 51            5, 51, 138, 0    C:\windows\IsUninst.exe
1.1.0.8          1.1.0.8          C:\windows\MicCal.exe
5.1.2600.2180    5.1.2600.2180... C:\windows\NOTEPAD.EXE
5.1.2600.2180    5.1.2600.2180... C:\windows\regedit.exe
2.0.1.7          2.0.1.7          C:\windows\RTHDCPL.exe
1.0.1.51         1.0.1.51         C:\windows\RTLCPL.exe
2, 5, 0, 5       2, 5, 0, 5       C:\windows\RtlUpd.exe
1, 0, 0, 21      1, 0, 0, 21      C:\windows\SoundMan.exe
5.1.2600.0       5.1.2600.0 (x... C:\windows\TASKMAN.EXE
1,7,0,0          1,7,0,0          C:\windows\twunk_16.exe
1,7,1,0          1,7,1,0          C:\windows\twunk_32.exe
3.10.425         3.10.425         C:\windows\winhelp.exe
5.1.2600.2180    5.1.2600.2180... C:\windows\winhlp32.exe
or
PS> get-fileversion C:\monad\rc1\System.Management.Automation.dll

ProductVersion   FileVersion      FileName
--------------   -----------      --------
1.0.9567.1       1.0.9567.1       C:\monad\rc1\System.Management.Automation.dll
Here’s the script – it’s pretty straight forward. Since I don’t know whether I’m going to have piped input or not, I use the begin script block to declare a couple of functions that will be used by either of the process or end blocks.
The real work is done in the function GetVersionInfo where I simply call the GetVersionInfo static method on the System.Diagnostics.FileVersionInfo type. Note that most of the code is error correction and ensuring that I get a proper path when I call the GetVersionInfo method.
param ( [string[]]$paths )
begin {
    # I want to do some stuff with relative paths.   
    # create a variable that I can use later
    $P = [string](get-location)

    # the workhorse of the script
    function GetVersionInfo
    {
        param ( [string]$path )
        # resolve the path, we're going to need a fully qualified path to hand
        # to the method, so go get it.  I may not have that depending on how
        # was called
        $rpath = resolve-path $path 2>$null
        # the thing we hand to the method is the path string, so we'll tuck that away
        $path = $rpath.path
        # check to be sure that we're in the filesystem
        if ( $rpath.provider.name -ne "FileSystem" ) 
        { 
            "$path is not in the filesystem"
            return $null
        }
        # now that I've determined that I'm in the filesystem, go get the fileversion
        $o = [system.diagnostics.fileversioninfo]::GetVersionInfo($path)
        # this little dance adds a new property to the versioninfo object
        # I add the relative path to the versioninfo so I can inspect that in the output object
        # the way that add-member works is to not emit the object, so I need to 
        # use the -passthrough parameter
        $o|add-member noteproperty RelativePath ($path.replace($P,".")) -pass
    }
    # whoops! something bad happened
    function ShowFileError
    {
        param ( [string]$path )
        if ( test-path $path -pathtype container )
        {
            "$path is a container"
        }
        else
        {
            "$path not found"
        }
    }
}

# data could have been piped - check $_ to see if this cmdlet had data
# piped to it
process {
    if ( $_ )
    {
        # make sure that I'm not trying to get a versioninfo of a directory
        if ( test-path $_ -pathtype leaf )
        {
            GetVersionInfo $_
        }
        else
        {
            ShowFileError $_
        }
    }
}

# we could have also gotten arguments on the command line
end {
    if ( $paths )
    {
        # by calling resolve path first, I can deal with wildcards on the command line
        foreach ( $path in resolve-path $paths )
        {
            # make sure it's a file, not a directory
            if ( test-path $path -pathtype leaf )
            {
                GetVersionInfo $path
            }
            else
            {
                ShowFileError $path
            }
        }
    }
}
 
 
 
 

Posted May 14, 2006 by jtruher3 in PowerShell

PowerShell: Hashtables and Objects   Leave a comment

I use hash tables all the time – they’re dead useful and when we were building PowerShell I used them as surrogate objects.  However, they don’t quite behave like an object – display is problematic and it’s tougher to add methods, etc, so, how do I get what I want?  There are a couple of ways to add functionality to the PowerShell environment. Scripts and functions are the usual way to add to a shell environment, but one of the things I really like about PowerShell is the dynamic nature of the snapin model. This allows me to add cmdlets and providers to my environment – and since one of the things I use all the time is a hashtable as an object, I figured that I would create a hashtable converterto do the trick.   The converter itself could be done as a script (and is left as an excercise for the reader)
using System;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace System.Management.Automation.Demos
{
    // This class defines the properties of a snapin
    [RunInstaller(true)]
    public class ConversionCmdlets : PSSnapIn
    {
        /// <summary>Creates an instance of DemoSnapin class.</summary>
        public ConversionCmdlets() : base()
        {
        }
        ///<summary>The snapin name which is used for registration</summary>
        public override string Name
        { get
            { return "ConversionCmdlets";
            }
        }
        /// <summary>Gets vendor of the snapin.</summary>
        public override string Vendor
        { get
            { return "Vendor String - JWT";
            }
        }
        /// <summary>Gets description of the snapin. </summary>
        public override string Description
        { get
            { return "This snapin contains demo conversion cmdlets";
            }
        }
        private string[] formats = null;

        public override string[] Formats
        { get 
            { return formats;
            }
        }
    }
}

That’s the code that allows me to create a snapin, now I need the actual cmdlet code
using System;
using System.Management.Automation;
using System.Collections;
using System.Collections.ObjectModel;

namespace JWT.Conversion.Cmdlets
{
    [Cmdlet("Convert","HashToObject")]
    public class ConvertHashToObjectCommand : PSCmdlet
    {
        private Hashtable hash;
        [Parameter(Mandatory=true,Position=0,ValueFromPipeline=true)]
        public Hashtable Hash
        {
            get { return hash; }
            set { hash = value; }
        }
        private String[] name = { "ConvertedHashTable" };
        [Parameter(Position=1)]
        public String[] Names
        {
            get { return name; }
            set { name = value; }
        }
        protected override void ProcessRecord()
        {
            PSObject obj = new PSObject();
            // Go through each key and create a property and
            // assign the value to the new property.
            // In the case that the value is a script block, create
            // a ScriptMethod instead.
            foreach ( string key in Hash.Keys )
            {
                if ( Hash[key].GetType().Name == "ScriptBlock" )
                    obj.Methods.Add(
                        new PSScriptMethod(key,(ScriptBlock)Hash[key])
                        );
                else
                    obj.Properties.Add(
                        new PSNoteProperty(key, Hash[key])
                        );
            }
            // update the type names for the new object
            Collection<string> tnames = new Collection<string>();
            obj.TypeNames.Clear();
            foreach ( string tname in Names )
            {
                obj.TypeNames.Add(tname);
            }
            WriteObject(obj);
        }
    }
}

Then I built a script to build and install the snapin
# make.ps1
# build my ConversionCmdlets PowerShell
$out          = "Conversion-Cmdlets.dll"
$src          = "convert-HashToObject.cs","convert-snapin.cs"
$ref          = "${pshome}/system.management.automation.dll"
$frameworkDir = "${env:windir}/Microsoft.Net/Framework/v2.0.50727"
$csc          = "${frameworkDir}/csc.exe"
$installutil  = "${frameworkDir}/installutil.exe"
&$csc /target:library /out:$out /r:$ref $src
if ( ! (test-path $out) )
{
    Write-Host -red "$out could not be built"
    exit
}
&$installutil $out > install.out 2> install.err
$snapin = get-pssnapin -reg | where {$_.modulename -match $out}
if ( ! $snapin )
{
    Write-Host -red "$out could not be installed"
    exit
}
else
{
    "Snapin created and installed"
}

Here’s what it looks like when you use it!
PS> add-pssnapin ConversionCmdlets
PS> get-command convert-hashtoobject

CommandType     Name                            Definition
-----------     ----                            ----------
Cmdlet          Convert-HashToObject            Convert-HashToObject [-Hash]...

PS> $a = @{A=1;B=2;C={$this.A};D={$this.A=$args[0]}}|convert-hashtoobject
PS> $a|format-table -auto
A B
- -
1 2
PS> $a.C()
1
PS> $a.D(10)
PS> $a|format-table -auto
 A B
 - -
10 2
PS> $A.C()
10
PS>

Posted May 5, 2006 by jtruher3 in PowerShell

All this rushing about   Leave a comment

 
This was originally started life as a blog I made for the Microsoft Orchestra
 
I’ve been thinking about tempo and what it is that makes people rush – here are my thoughts on the topic.
Many times, we rush when it is hard. I think that the reason is that you get nervous about the hard bits and then you think ok – this goes really, really fast, if I play it as fast as I can, I should be able to keep up. There are a few problems with this:

  1. It may be that you can play it faster than you think
  2. Rushing can be contagious; you’re playing with your standmate, and they might go a little fast, and you want to catch up
  3. Not enough time is given to the spaces between the notes
  4. (and most importantly) you lose the connection with the rest of the whole group in concentrating on your little corner of the world. So, in the microcosm of your standmates, you’re together, but rushing ahead of the entire orchestra.

Then, of course, there’s the rushing where it’s too easy, I think that this is associated with music that has a lot of silence in the individual parts. A good example of this is probably the Donizetti "Daughter of the Regiment". Lots of the parts have little "oom-pah" or "dit-dit" parts – I think these have a tendency to rush because the overall tempo is not internalized (not by tapping the foot but getting the right subdivision of the overall tempo).  It’s incredibly important that when you play these accompaniment parts that you tune into the player that’s got the melody – be sure that you are supporting the melody rather than trying to fill in the silences – the melody takes care of filling in the silence, the accompaning parts just need to accompany.

 

What can we do about this?  I think that the most important thing to do is to take the time to listen to the sounds around you.  Playing together does not mean that you play your part, the guy next to you plays his part and the whole thing comes together like magic.  Playing together means listening to everything, especially the players that don’t happen to play the part you play.  Also, remember that music making is a social process, not a technical exercise – concentrate on making music, not the technique(1) – anyway, that’s what I try to do. 

 

jim

 

1
I’m not suggesting that technique is unimportant, it’s just not the most important.  I’ve attended too many performances that were technically excellent but left me unmoved because of the lack of musicality.  I have more to say on that topic, but later.

Posted May 5, 2006 by jtruher3 in Music

Music, Hark!   1 comment

While I make my living working with and planning software, I consider myself a musician rather than a "software guy". My background is all music, my education was in music, then as a teacher and performer (both singer and instrumentalist). I’ve been very lucky to have the opportunity to perform with some really great musicians.
 
My latest musical endeavor finds me the conductor of the Microsoft Orchestra – when the post opened up a few years ago, I volunteered.  I never thought that I would be an orchestra conductor; I studied choral music and was a choir director and singer for 20 years and have conducted orchestras, but mostly in combination with choirs (like the Brahms Requiem, Dona Nobis Pacem, etc), but never thought I would regularly conduct an orchestra.
 
I really love it – it’s a chance for me to learn, to teach, and to make music that I never thought I would get the chance to do.  It is so much fun to build rapport with the players and to bring music out that they didn’t think they had in them.  It’s also been a great learning experience for me, and has made me a better musician by trying to keep ahead of these folks; making sure that I have something to say that inspires, cajoles or amuses these wonderful people into playing better.
 
The MS orchestra is made up of MS employees, family and friends and has been around since the early 90s.  We’re getting better and better with each concert and it is so much fun.  (if you’re interested, you can see more at http://www.msorchestra.org/)
 
j
 

Posted May 3, 2006 by jtruher3 in Music

First PowerShell Blog   1 comment

I keep getting hounded to blog so here you go.  I’m a PM on the PowerShell team and i’m largely responsible for the PowerShell language (Bruce Payette shares the blame with me in equal portion).  I thought that one of the things that I could do here would be to keep track of all the scripts that I use on a somewhat daily basis.  They’ve been posted in other venues before, but I can post them here too. I have more on my mind than just PowerShell, but I’ll post that under a different category
 
This first script is a script that I use all the time. I only remember the parameters of cmdlets that I use all the time, and while I can get the parameter information via (get-command cmdlet).definition, or from help I don’t care for the formatting. I wrote this script to help me with that, it provides me the information in a way that I like. You might like it too.
# The Command to retrieve information, Verbose output, show Ubiquitous Parameter info
# get-paraminfo
param ( [string]$cmd, [bool]$raw = $false, [bool]$ubp = $false )

# Formatting directives used in out-special
$type = @{ label = "Type";      e = { $_.Parametertype.name }}
$man  = @{ label = "Mandatory"; e = { $_.IsMandatory}}
$pos  = @{ label = "Pos";       e = { $_.Positional}; Align = "right" }
$vp   = @{ label = "PipeValue"; e = { $_.ValueFromPipeline } ; width = 10}
$vppn = @{ label = "PipeName";  e = { $_.ValueFromPipelineByPropertyName } ; 
           width = 10 }
$parms = "name",$type,$man,$pos,$vp,$vppn

# Use the special formatting built to output the results in a 
# palatable way
function out-special ()
{
    if ( $raw ) { $input }
    else { $input | format-table $parms -auto }
}

#
# Start Here
#
if ( ! $cmd -or (get-command $cmd ).count )
{
    get-command $cmd 
    Throw "
    -cmd -raw -ubp
    Need single Cmdlet
    "
}

# show the name of the command
$cmd
# and the DLL
get-command $cmd|format-list dll,helpfile | out-host

# here are the ubiquitous parameters
$ub = "ErrorVariable","ErrorAction","OutBuffer","Verbose","OutBuffer","Debug"
# Get the parameter sets and display the interesting info
foreach ( $pset in (get-command $cmd ).parametersets )
{
    "Parameter Set: " + $pset.name 
    # create a custom "object" so we can print it nicely
    $pset.parameters | foreach-object { 
        # optionally toss the ubiquitous parameters
        if ( $_.Position -lt 0 ) { $p = "named" } else { $p = $_.Position } 
        if ( !($ub -contains $_.name) -or $ubp )
        {
            $_ | add-member NoteProperty Positional $p -pass |
                 add-member NoteProperty ParameterSetName $pset.name -pass  
        }
    } | sort-object Positional | out-special
}
I think that the interesting bits are the additional properties that I add, which extends the object where I want. Also, this script emits either objects or text, depending on whether you use the -raw parameter. You can also get the ubiquitious parameters (-ubp), which I usually suppress. Most of the time, I just use the text output. Here’s how it looks against the get-command cmdlet.
PS> get-paraminfo get-command
get-command

DLL      : C:\WINDOWS\assembly\GAC_MSIL\System.Management.Automation\1.0.9567.0
           __31bf3856ad364e35\System.Management.Automation.dll
HelpFile : System.Management.Automation.dll-Help.xml

Parameter Set: CmdletSet

Name         Type            Mandatory   Pos PipeValue PipeName
----         ----            ---------   --- --------- --------
ArgumentList Object[]            False     1     False    False
Usage        SwitchParameter     False named     False     True
OutVariable  String              False named     False    False
TotalCount   Int32               False named     False     True
Verb         String[]            False named     False     True
Noun         String[]            False named     False     True
PSSnapin     String[]            False named     False     True


Parameter Set: AllCommandSet

Name         Type            Mandatory   Pos PipeValue PipeName
----         ----            ---------   --- --------- --------
Name         String[]            False     0      True     True
ArgumentList Object[]            False     1     False    False
OutVariable  String              False named     False    False
Usage        SwitchParameter     False named     False     True
CommandType  CommandTypes        False named     False     True
TotalCount   Int32               False named     False     True
So this tells me where the positional parameters are and whether they take pipeline input which is what I want, most of the time.
jim

Posted May 1, 2006 by jtruher3 in PowerShell