James w's profileTime is an illusion. Lun...PhotosBlogListsMore Tools Help

Blog


    5/14/2006

    PowerShell and file version information

    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
                }
            }
        }
    }
    
     
     
     
     
    5/5/2006

    PowerShell: Hashtables and Objects

    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>
    

    All this rushing about

     
    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.

    5/3/2006

    Music, Hark!

    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
     
    5/1/2006

    First PowerShell Blog

    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