MSH: Get Extended Properties of a File

If you right click on an image file in Windows explorer and then go to the Summary tab you will see some handy properties listed there like the image’s width, height and bit depth.  Wouldn’t it be handy to be able to access these properties from MSH.  Jim Truher posted the basic code snippet for doing this in MSH on the newsgroup microsoft.public.windows.server.scripting.  I modified it to accept pipeline input.  With this script copied into a file named get-metainfo.msh and placed in your path, you can do cool stuff like the following: search for any picture taken on May 14, 2003.
 
MSH:212 # dir *.jpg *.png | get-metainfo | where {$_.DatePictureTaken} |
where {([DateTime]$_.DatePictureTaken).Date -eq [DateTime]"5/14/2003"}
Path             : C:\Documents and Settings\hillr\My Documents\My Pictures\Eds
                   2003KTM450EXC.JPG
DateCreated      : 4/30/2004 9:12 AM
CameraModel      : GC-X1
DateModified     : 5/14/2003 4:24 PM
Attributes       : A
Type             : JPEG Image
DatePictureTaken : 5/14/2003 5:24 PM
<snip>
 
Note that the first "where" filter filters out objects that either don’t have a DatePictureTaken property or the property is null.  BTW, this is good example of a cmdlet written in MSH that accepts both pipeline input and arguments.  Here’s the script (place in an MSH file named something like get-metainfo.msh):
 
begin {
    $props = @{
        Name = 0;
        Size = 1;
        Type = 2;
        DateModified = 3;
        DateCreated = 4;
        DateAccessed = 5;
        Attributes = 6;
        Status = 7;
        Owner = 8;
        Author = 9;
        Title = 10;
        Subject = 11;
        Category = 12;
        Pages = 13;
        Comments = 14;
        Copyright = 15;
        Artist = 16;
        AlbumTitle = 17;
        Year = 18;
        TrackNumber = 19;
        Genre = 20;
        Duration = 21;
        BitRate = 22;
        Protected = 23;
        CameraModel = 24;
        DatePictureTaken = 25;
        Dimensions = 26;
        Company = 30;
        Description = 31;
        FileVersion = 32;
        ProductName = 33;
        ProductVersion = 34
    }
   
    function add-member {
        param($type, $name, $value, $input)
        $note = "system.management.automation.psnoteproperty"
        $member = new-object $note $name,$value
        $metaInfoObj.psobject.members.add($member)
        return $metaInfoObj
    }
   
    function emitMetaInfoObject($path) {
        [string]$path = (resolve-path $path).path
        [string]$dir  = split-path $path
        [string]$file = split-path $path -leaf
        $shellApp = new-object -com shell.application
        $myFolder = $shellApp.Namespace($dir)
        $fileobj = $myFolder.Items().Item($file)
       
        $metaInfoObj = new-object system.management.automation.psobject
        $metaInfoObj.psobject.typenames[0] = "Custom.IO.File.Metadata"
        $metaInfoObj = add-member noteproperty Path $path -input $metaInfoObj
        foreach ($key in $props.keys) {
            $v = $myFolder.GetDetailsOf($fileobj,$props.$key)
            if ($v) {
                $metaInfoObj = add-member noteproperty $key $v -input $metaInfoObj
            }
        }
        write-output $metaInfoObj
    }
}

process {
    if ($_) {
        emitMetaInfoObject $_
    }
}

end {
    if ($args) {
        $paths
        foreach ($path in $args) {
            if (!(test-path $path)) {
                write-error "$path is not a valid path"
            }
            $paths += resolve-path $path
        }
   
        foreach ($path in $paths) {
            emitMetaInfoObject $path
        }
    }
}

This entry was posted in Monad. Bookmark the permalink.

6 Responses to MSH: Get Extended Properties of a File

  1. rick says:

    Keith,
     
    I updated this for PowerShell RC1 as a learning exercise. 🙂
     
    The thing I really love about this example is the extra effort out into making this CMDLET pipeable using custom.io
     
    .\\get-metainfo | get-members
     
    this is cool !!!
     
     
     
    BTW: I have not fixed the bug where the filename contains [xxxx] – like many W3C file seem to have
     
     
    begin {    $props = @{         Name = 0;         Size = 1;         Type = 2;         DateModified = 3;         DateCreated = 4;        DateAccessed = 5;         Attributes = 6;         Status = 7;         Owner = 8;         Author = 9;        Title = 10;         Subject = 11;         Category = 12;         Pages = 13;         Comments = 14;        Copyright = 15;         Artist = 16;         AlbumTitle = 17;         Year = 18;         TrackNumber = 19;        Genre = 20;         Duration = 21;         BitRate = 22;         Protected = 23;         CameraModel = 24;        DatePictureTaken = 25;         Dimensions = 26;        Company = 30;         Description = 31;         FileVersion = 32;        ProductName = 33;         ProductVersion = 34    }        function add-member {        param($type, $name, $value, $input)        $note = "system.management.automation.psnoteproperty"        $member = new-object $note $name,$value        $metaInfoObj.psobject.members.add($member)        return $metaInfoObj    }        function emitMetaInfoObject($path) {        [string]$path = (resolve-path $path).path        [string]$dir  = split-path $path        [string]$file = split-path $path -leaf        $shellApp = new-object -com shell.application        $myFolder = $shellApp.Namespace($dir)        $fileobj = $myFolder.Items().Item($file)                $metaInfoObj = new-object system.management.automation.psobject        $metaInfoObj.psobject.typenames[0] = "Custom.IO.File.Metadata"        $metaInfoObj = add-member noteproperty Path $path -input $metaInfoObj        foreach ($key in $props.keys) {            $v = $myFolder.GetDetailsOf($fileobj,$props.$key)            if ($v) {                 $metaInfoObj = add-member noteproperty $key $v -input $metaInfoObj             }        }        write-output $metaInfoObj    }}process {    if ($_) {        emitMetaInfoObject $_    }}end {    if ($args) {        $paths        foreach ($path in $args) {            if (!(test-path $path)) {                write-error "$path is not a valid path"            }            $paths += resolve-path $path        }            foreach ($path in $paths) {            emitMetaInfoObject $path        }    }}

  2. Keith says:

    Hey Rick, thanks for the update!  I have updated this entry based on your port.

  3. coleman says:

    Is it possible to do something similar with folders as well as files?  I would like to add data to the "Description" field on some folders, but this functionality is not directly built into XP or Server 03….

  4. Keith says:

    Yeah I think the example could be modified to work on $myFolder (don\’t split the path assuming the path is to a folder).

  5. I get an error: ” The term ‘get-metainfo’ is not recognized as the name of a cmdlet”

    • rkeithhill says:

      Since I wrote this (a long, long time ago) the default extension for PowerShell scripts has changed from .msh (beta days) to .ps1. So this instruction “Here’s the script (place in an MSH file named something like get-metainfo.msh)” should read “Here’s the script (place in an PS1 file named something like get-metainfo.ps1)”. Once you’ve done that keep in mind that PowerShell will not automatically run scripts out of the current directory for security unless you tell it you want to do that (same as on *nix). To run the script from the current directory, execute ./get-metainfo.ps1

Leave a comment