locked
Get-ChildItem Issue With Variable RRS feed

  • Question

  • I hope some very clever person out there can resolve this without even thinking, however my brain seems to be blocked.

    $FileVersion = (Get-ChildItem C:\Windows\System32\drivers\somedriver.SYS).VersionInfo | Select-Object FileVersion

    If ($FileVersion -eq "2, 0, 0, 1") {Copy-Item -Force "C:\local\somedriver.SYS","C:\Windows\System32\drivers\somedriver.SYS"

    I have verified this and $FileVersion does in fact equal 2, 0, 0, 1 however starts as a formatted table.

    FileVersion

    -----------------

    2, 0, 0, 1

    I think it must be passing the variable as the whole table, I also tried adding in my if statement the value @{FileVersion=2, 0, 0, 1 however that also failed.

    Any ideas what I'm missing here?

    Many Thanks

    Thursday, December 12, 2013 5:03 PM

Answers

  • Fastest way maybe?

    $fileversion=([IO.FileInfo]'C:\Windows\System32\drivers\usbprint.sys').VersionInfo.FileVersion


    ¯\_(ツ)_/¯

    • Marked as answer by Tarrley Monday, January 6, 2014 3:33 PM
    Thursday, December 12, 2013 6:13 PM

All replies

  • What is your purpose?

    I submitted a file version information script to the repository:

    PowerShell - Output file version information

    Bill

    Thursday, December 12, 2013 5:09 PM
  • If you're going to use Select-Object that way, you'll want the -ExpandProperty parameter:

    $FileVersion = (Get-ChildItem C:\Windows\System32\drivers\somedriver.SYS).VersionInfo | Select-Object -ExpandProperty FileVersion

    Though in this case, I wouldn't bother with that at all.  Just use dot notation, the same way you did to get the VersionInfo property.  Also, since you're grabbbing a single file, Get-Item is probably more appropriate than Get-ChildItem:

    $FileVersion = (Get-Item C:\Windows\System32\drivers\somedriver.SYS).VersionInfo.FileVersion

    Thursday, December 12, 2013 5:11 PM
  • Hi,

    Try something along these lines:

    PS C:\Scripts\PowerShell Scripts\Misc Testing\12-12-2013> $verInfo = Get-Item C:\Windows\System32\drivers\RimSerial_AMD64.sys
    
    PS C:\Scripts\PowerShell Scripts\Misc Testing\12-12-2013> $verInfo.VersionInfo.FileVersion
    2.3.0.9
    
    PS C:\Scripts\PowerShell Scripts\Misc Testing\12-12-2013> If ($verInfo.VersionInfo.FileVersion -eq '2.3.0.9') { Write-Host 'Do something here, the version matches' } Else { Write-Host 'Does not match' }
    Do something here, the version matches

    EDIT: Wow, way behind today. See above. =]


    Don't retire TechNet! - (Don't give up yet - 12,420+ strong and growing)


    Thursday, December 12, 2013 5:16 PM
  • Fastest way maybe?

    $fileversion=([IO.FileInfo]'C:\Windows\System32\drivers\usbprint.sys').VersionInfo.FileVersion


    ¯\_(ツ)_/¯

    • Marked as answer by Tarrley Monday, January 6, 2014 3:33 PM
    Thursday, December 12, 2013 6:13 PM
  • Perfect, thanks.

    Final version, this will work right?

    $filesource = "\\server\share\drivers"
    $checkfilepath = Test-Path C:\Windows\System32\drivers\somedriver.sys
    if ($checkfilepath) {
    $FileVersion = (Get-Item C:\Windows\System32\drivers\somedriver.SYS).VersionInfo.FileVersion
    If ($FileVersion -eq "2, 0, 0, 1") {Copy-Item -Force "$filesource\somedriver.sys" -Destination "C:\Windows\System32\drivers\somedriver.SYS"} Else {Quit}}
    Else {Quit}

    Many Thanks

    Thursday, December 12, 2013 10:09 PM
  • That will not work. Quit is not a valid command.

    Why don't you explain what you're trying to do, rather than how you're trying to do it?

    Bill

    Thursday, December 12, 2013 10:28 PM
  • One other thing: the FileVersion property isn't actually the file version that Explorer shows when you go to the details tab. That is actually found by gathering the FileMajorPart, FileMinorPart, FileBuildPart, and FilePrivatePart properties of the VersionInfo object:

    $VersionInfo = (Get-Item C:\Windows\System32\drivers\acpi.sys).VersionInfo
    [version] $FileVersion = "{0}.{1}.{2}.{3}" -f $VersionInfo.FileMajorPart, 
        $VersionInfo.FileMinorPart, 
        $VersionInfo.FileBuildPart, 
        $VersionInfo.FilePrivatePart
    

    Sometimes the FileVersion property is the same as calculating the version yourself, but a lot of times it isn't.

    Also, by casting it to a [version] type, you can do some comparison operations besides a simple "-eq":

    $FileVersion -ge "6.3"
    $FileVersion -ge "6.3.9600.16423"
    


    Thursday, December 12, 2013 11:54 PM
  • Be careful, the results might not be what you expect:


    PS C:\> ([Version] "6.1.7600") -eq ([Version] "6.1.7600.0")
    False

    IMO, if the least significant bits are not set, they should be evaluated as zero. (This is why I prefer to cast to a UInt64 and compare that way.)

    Bill

    Saturday, December 14, 2013 6:13 AM
  • I personally wouldn't expect those to be equal, but that's just my opinion. I would, however, expect the other comparison operators (ge, gt, le, lt) to still work, and they do:

    PS> [version] "3.0.2" -gt "3.0.1.199"
    True
    
    PS> [version] "3.0.1" -lt "3.0.1.199"
    True
    
    PS> [version] "3.0.1" -gt "3.0.1.199"
    False


    Is there a cast available to take a [version] to an Int64, or are you having to run the version through a custom function to convert it? If you have to run it through a function, it seems that the cast to [version] would still be easier. 

    I think getting the version the way I showed above will always give a version that treats nulls how you prefer because PowerShell would treat a null as an [int], and that would mean it would be 0 (according to Get-Member, all 4 of the version properties are of type [int]).

    That is an assumption, though, so you could explicitly tell PowerShell to behave that way:

    $VersionInfo = (Get-Item C:\Windows\System32\drivers\acpi.sys).VersionInfo
    [version] $FileVersion = "{0}.{1}.{2}.{3}" -f [int] $VersionInfo.FileMajorPart,
         [int] $VersionInfo.FileMinorPart,
         [int] $VersionInfo.FileBuildPart,
         [int] $VersionInfo.FilePrivatePart

    As long as you always put any hand typed versions in the 'x.x.x.x' format, there should be no problem with the -eq operator.

    Saturday, December 14, 2013 4:47 PM
  • Ok then explain this:
    PS C:\scripts> "3.0.1.1000" -gt "3.0.1.199"
    False

    $1000 is clearly greater then $199 when I go to the bank.

    Text cannot compare number correctly.  This is a fundamental of programming (Programming 101). It is also one of the most frequent mistakes that programmers make.


    ¯\_(ツ)_/¯

    Saturday, December 14, 2013 4:58 PM
  • You are correct, you can't compare versions if they are strings. You can cast the strings to a [version] type, though, which is able to properly compare them:

    PS> [version] "3.0.1.1000" -gt "3.0.1.199"
    True

    Casting the second string wouldn't hurt, but it's not necessary b/c of the way PS does comparisons. Since the first object is of type [version], it will automatically attempt to cast the second object to that type.

    Saturday, December 14, 2013 5:10 PM
  • To me, the below is counterintuitive:


    PS C:\> ([Version] "6.1.7600") -eq ([Version] "6.1.7600.0")
    False
    PS C:\> ([Version] "6.1.7600") -lt ([Version] "6.1.7600.0")
    True

    IMO, the least significant bits of the version number should be considered as zeros if they're not specified, at least for purposes of comparison. I avoid the problem by casting to an unsigned 64-bit integer.

    Bill
    Saturday, December 14, 2013 5:24 PM
  • 6.1.7600 does not have a revision number. 6.1.7600.0 does (even if it's 0). To me, they're not the same thing.

    Until I see how you're changing from a version to a 64-bit integer, I still think casting to the [version] type is simpler. The snippet I showed always puts the version in a format that has a revision number. If you compare two file versions that were generated with the snipped, your scenario will never come up. If you compare the file version to a string that you are responsible for creating and you make sure it's in the 'x.x.x.x' format, there is no problem, either.

    I'm not aware of a cast, and if there's not one, you'd have to shift bits or use the BitConverter class. That seems more complicated than just ensuring you use versions of the format 'x.x.x.x'.

    The beauty of PowerShell is that there are countless ways to do the same thing. There is no one right way. So, if you prefer converting to 64-bit integers and then comparing, then there's nothing wrong with that.
    Saturday, December 14, 2013 5:47 PM
  • The same is true for IPAddesses  [ipaddress] helps to manage but does not allow them to be compared.  Thisis better because it does not allow you to be fooled.

    PS C:\scripts> $a=[ipaddress]'192.168.1.2'
    PS C:\scripts> $b=[ipaddress]'192.168.1.10'
    PS C:\scripts> $a -gt $b
    Cannot compare "192.168.1.2" because it is not IComparable.

    I was going to point out [version] next although, as Bill points out, it has some warts.

    What both version and ipaddress do is enforce rules for these well known  objects.

    Both validate the format of the strings and both let you inspect all aspects of the type.

    PS C:\scripts> $a.Major
    192
    PS C:\scripts> $a.MajorRevision
    0
    PS C:\scripts> $a.Minor
    168
    PS C:\scripts> $a.Revision
    2

    PS C:\scripts> [version]'1.1.1.1.1'
    Cannot convert value "1.1.1.1.1" to type "System.Version". Error: "Version string portion was too short or too long."


    ¯\_(ツ)_/¯

    Saturday, December 14, 2013 5:47 PM
  • The biggest issue is that [IO.FileInfo] doe not use [version].  It uses strings and there is no rule that says the strings have to be version type strings.  Because of that they will not always cast.

    [version]'1'

    There seems to be no absolute method as always with systems.  We have to know all of the variations to survive. 


    ¯\_(ツ)_/¯

    Saturday, December 14, 2013 5:52 PM
  • 6.1.7600 does not have a revision number. 6.1.7600.0 does (even if it's 0). To me, they're not the same thing.

    To each his own, I guess. IMO "does not have" should be compared as a zero.

    Bill

    Saturday, December 14, 2013 6:01 PM
  • jrv,

    That's why my original post said not to use the $FileInfoObject.VersionInfo.FileVersion. Sometimes there's text mixed in there. Sometimes the file version doesn't match what explorer shows. The accurate way to get what Explorer shows is to use the following VersionInfo properties: FileMajorPart, FileMinorPart, FileBuildPart, FilePrivatePart.

    Please look over my code snippet above. It will give you an object that is both human readable and machine comparable. As I also said, there's not a problem comparing file versions as long as you keep their formats the same.

    Bill,

    I agree, to each his own. Just out of curiosity, how are you converting to a 64-bit integer? 

    Saturday, December 14, 2013 6:07 PM
  • jrv,

    That's why my original post said not to use the $FileInfoObject.VersionInfo.FileVersion. Sometimes there's text mixed in there. Sometimes the file version doesn't match what explorer shows. The accurate way to get what Explorer shows is to use the following VersionInfo properties: FileMajorPart, FileMinorPart, FileBuildPart, FilePrivatePart.

    Please look over my code snippet above. It will give you an object that is both human readable and machine comparable. As I also said, there's not a problem comparing file versions as long as you keep their formats the same.

    Bill,

    I agree, to each his own. Just out of curiosity, how are you converting to a 64-bit integer? 

    Problem is with those - they can be blank or some can be blank.  Yes - I agree that that is one way and a method can be devised to handle most, if not all, conditions.

    ¯\_(ツ)_/¯

    Saturday, December 14, 2013 6:21 PM
  • Just out of curiosity, how are you converting to a 64-bit integer?

    I put a sample script in the repository that shows one way:

    PowerShell - Output file version information

    It shifts the bits in a PowerShell v2-compatible matter.

    Bill

    Saturday, December 14, 2013 6:27 PM
  • I'm really not trying to be difficult, jrv, but that's just not correct. If they were blank, PowerShell would take care of making them 0 (see where I cast each of the properties to [int] above). They're not blank, though. Check this out:

    [System.Diagnostics.FileVersionInfo]::GetVersionInfo("$env:temp\test.txt") | select file*part

    Point that to a file that doesn't have a version. You should see all zeros come back.

    That snippet should be able to handle close to anything you can throw at it. That's without making a special function. It does require a lot of typing, though, so I would recommend wrapping it in a function (or even modifying the type data for the FileInfo type).

    Saturday, December 14, 2013 6:32 PM
  • I think that is Bill's point.  If a file doesn't have a version then its version is NOT 0.

    Yes casting to int takes care of the blanks but a version of 1 is not the same as a version of 1.0.0.0.  The lack of other zeros can and does mean is some versioning systems that there is not change and a zero is the first of that level.

    If what you are doing works for what you need then it is good.  I am only saying that one solution may not fit all circumstances.  I suspect that may be why MS dod not make FileVersionInfo a System.Version object.

    I suspect the the n.n.n.n pattern will become the defacto or accepted industry standard before long.  By then we will have formalities for this.


    ¯\_(ツ)_/¯

    Saturday, December 14, 2013 6:46 PM
  • No, that's not Bill's point. You are making the argument for why '6.1.7600' is not the same as '6.1.7600.0', which is the position I took in the previous posts when I was taking up for the [version] comparison operators. Please read above for more information.

    Bill wants nulls to be treated as zeros (which my snippet that I started with does), and I really don't have a problem with that (that's how PowerShell treats them). The issue is that the comparison operation wasn't built to behave the way Bill wants. Again, there's no problem with that. Part of what makes PowerShell so awesome is that you can make something that will massage the inputs to a .NET method like that so that .NET still does the heavy lifting.

    My point is that casting to a [version] is simpler than relying on a separate function to convert it to a 64-bit integer and then do the comparison. The comparisons always work in a deterministic manner using a [version]. It will work exactly how Bill wants it to if you just ensure that the versions you're comparing are in the 'x.x.x.x' format.

    Saturday, December 14, 2013 8:00 PM
  • You are assuming a definition for version.


    ¯\_(ツ)_/¯

    Saturday, December 14, 2013 8:05 PM
  • Yes, as I said above, my snippet treats nulls as zeros. So if there is no version, you will get a version of '0.0.0.0'. That is not technically correct.

    That is a very acceptable situation to me while using PowerShell interactively. If I was creating a function or modifying the type data to add a FileVersion property to every file, I might put a quick check for the existence of .VersionInfo.FileVersion. When displaying several files, all sorted by version, at the same time, I would agree that it would be helpful to be able to tell at a glance files with no version at all.

    Just out of curiosity, what do you think the problem is with comparing a file that has no version against an actual file version? Let's say you want to ensure that a file with  no version is at least version '1.2.0.0'. To me, and I think Bill would agree, the comparison should return false (no version is not at least '1.2.0.0'). My code would show that result, and since the original post was asking about comparing file versions, I don't see the issue.

    Saturday, December 14, 2013 8:21 PM
  • This is, in part, what I am adding.  Files in Windows NTFS do not have versions.  NTFS is not a versioning system like MVS or VMS or many other file storage systems.

    The FileVersionInfo is an extraction from the subsystem that the file belongs to.  For Net isz is always n.n.n.n where n is a UINT16.  For files that are legacy Windows files thisis ususally n.n.b where b is the build number.  For third party binaries the version can be anything and may or may not be supported.

    The version is stored inside of the file.  It is extracted, when available, from the binary using an extension call that the OS uses to attempt to read the version.  For the new PE format this structure is required but some vendors do not use it.


    ¯\_(ツ)_/¯

    Saturday, December 14, 2013 8:34 PM
  • Here is the old (1999) version of the version definition from MS/Intel.  It has evolved some.  The text is discussing PE file header info. In VS this is set by the version resource which is now mandatory in VS for Windows PE files.

    The next 2 16-bit-words are the binary's version, ('MajorImageVersion' and
    'MinorImageVersion'). Many linkers don't set this information correctly
    and many programmers don't bother to supply it, so it is better to rely
    on the version-resource if one exists.

    The next 2 16-bit-words are the expected subsystem version
    ('MajorSubsystemVersion' and 'MinorSubsystemVersion'). This should be
    the Win32 version or the POSIX version, because 16-bit-programs or
    OS/2-programs won't be in PE-format, obviously.
    This subsystem version should be supplied correctly, because it *is*
    checked and used:
    If the application is a Win32-GUI-application and runs on NT4, and the
    subsystem version is *not* 4.0, the dialogs won't be 3D-style and
    certain other features will also work "old-style" because the
    application expects to run on NT 3.51, which had the program manager
    instead of explorer and so on, and NT 4.0 will mimic that behaviour as
    faithfully as possible.

    Then we have a 'Win32VersionValue' of 32 bits. I don't know what it is
    good for. It has been 0 in all the PE files that I inspected.


    ¯\_(ツ)_/¯

    Saturday, December 14, 2013 8:39 PM
  • I didn't express myself very well before (that's not the first time).

    What I meant to say is that I can understand a file not having an embedded version.

    But if a file does have an embedded version (i.e., a VS_FIXEDFILEINFO), then it's a 64-bit value composed of two DWORDs (dwFileVersionMS and dwFileVersionLS, or dwProductVersionMS and dwProductVersionLS, depending on which version you want, file or product version number). The API documentation even says that the two DWORD parts form a 64-bit number for comparison purposes.

    Less significant bits of a number can't be "missing" if more significant bits are already set. This is why it doesn't make sense for "6.1.0" to compare as different from "6.1".

    Bill

    Sunday, December 15, 2013 7:20 AM
  • Why are you guys talking about whether or not a file has an embedded version? All that matters is whether or not .NET presents one to us. As I said in a previous post, my solution would always show 0.0.0.0 on a file without a version, but I said that it would be very easy to make it present a null value in that case as well.

    How about what's below. Now, as this is written, it will only work for PSv3 or higher because of the parameters to Update-TypeData. This trick can still be used for version 2, but the steps to set it up are a little bit different (but the ScriptBlock should be the same):

    Update-TypeData -TypeName System.IO.FileInfo -MemberType ScriptProperty -MemberName PSFileVersion -Value {
        if ($this.VersionInfo.FileVersion) {
            [version] ("{0}.{1}.{2}.{3}" -f $this.VersionInfo.FileMajorPart,
                 $this.VersionInfo.FileMinorPart,
                 $this.VersionInfo.FileBuildPart,
                 $this.VersionInfo.FilePrivatePart)
        }
    }


    If you run that, you'll now have an extra property on all files called PSFileVersion (and it will be null if there is no file version). Try this:

    dir c:\windows\system32 | select name, psfileversion


    It looks pretty, it's accurate, and you can do comparisons against the versions as easily as you compare numbers. This will work:

    (Get-Item C:\Windows\System32\ntoskrnl.exe).PSFileVersion -gt "6.3.0.0"

    Notice that the value I'm comparing to is a string. No 64-bit integer conversion necessary. .NET does all of the hard work, so I don't have to worry about custom code to do it for me.

    I understand the complaints against the comparison between version formats that aren't the same (the behavior is on purpose--read the remarks), but I can't understand why you would want to convert the versions to 64-bit integers. As I've said before, as long as you keep the versions in the same format, your issue will never come up.

    Edited to add:

    The comparison example above with the string only works because the first object is a [version]. If you reverse it and put the string first, the comparison will not work properly. It's a good idea to always cast the string as a [version] if you want to do this, but I was showing that it isn't necessary as long as the first object is of type [version].

    Sunday, December 15, 2013 2:55 PM
  • Regarding keeping the versions in the same format: This is not always possible as you might want to use user input, and files "in the wild" have a habit of not following the standards.

    I understand the .NET implementation is by design. I just don't agree that it's correct. It doesn't make sense for "6.1.0" to compare as greater than "6.1".

    Bill

    Sunday, December 15, 2013 3:51 PM
  • Bill, how would your script handle files "in the wild"? Your script puts the version in the "x.x.x.x" format (besides, this thread already covered what happens when the version parts are null). How would your script handle comparing a user input version?

    Also, how often do you think someone would compare "6.1.0" to "6.1"? And if/when they do, what would you say is simpler: teaching them to convert the version to a 64-bit integer, or teaching them to put their version in a different format?

    Look, the only thing I wanted to add to this thread was not to use .VersionInfo.FileVersion. In my experience, it's not accurate (even if you get rid of any strings contained, the actual version number contained in it is often not accurate). If you want an accurate file version (as accurate as I've found using native PowerShell, and I've never found a reason to doubt it), you have to manually combine the four version parts.

    It looks like we're getting off topic, so I'm going to to just leave this as a disagreement over which way to do the comparisons is simpler. You have your preference, and I have mine. I did appreciate the discussion, though :)

    Sunday, December 15, 2013 4:11 PM
  • Agree, we've gotten off-topic. I think at this point we can say the OP's question has been answered in either case.

    Bill

    Sunday, December 15, 2013 4:46 PM
  • My simple point is some files have no version, Some have an alphabetic version and some a verision that looks like 1.1  You cannot change what yu get because you would be inventing misleading information.

    See "information theory" for guidance.

    In most cases It is unscientific to alter reality for convenience. It canbe done if you already know the range and type of you files but that seldom is true.

    It is just a point to keep in mind and not a moral or political principal to be argued.

    Good luck and happy versioning.


    ¯\_(ツ)_/¯

    Sunday, December 15, 2013 5:56 PM
  • No disagreement jrv. My only point is that I think .NET is not doing the right thing based on what the Win32 API spec says, so I prefer not to use the [Version] class to compare version numbers. But as noted, this thread has wandered off-topic, and I think the OP's question should be answered at this point.

    Bill

    Sunday, December 15, 2013 7:17 PM
  • Yes – off topic.

    Two final points of argument:

    If comparing two versions of the same file then all but pure string will likely work correctly except for files that do not use correct PS format (There are many of these).

    For reporting file version I suggest always using the string value as is.

    Why would you compare different files. I would say check string first.  If it is identical then you are ok.  If not then the files are different.  Then try to decide which one is newer if that is your task.


    ¯\_(ツ)_/¯

    Sunday, December 15, 2013 8:13 PM
  • I'm going to chime in one more time, since this does actually pertain to accurate file versions:

    If you're talking about checking the .VersionInfo.FileVersion string, jrv, I'm going to have to disagree with you again. That property is simply not dependable.

    I believe this is because the strings are pulled from the MUI resource files (if one exists for the file), and those files aren't always updated with patches. The actual version parts are pulled directly from the binary file.

    For an example of what I'm talking about, you can run the following command:

    dir C:\Windows\system32 -Filter *.dll | 
        select name, @{N="Ver1";E={$_.VersionInfo.FileVersion}}, 
                     @{N="Ver2";E={"{0}.{1}.{2}.{3}" -f $_.VersionInfo.FileMajorPart, $_.VersionInfo.FileMinorPart, $_.VersionInfo.FileBuildPart, $_.VersionInfo.FilePrivatePart}} |
        ? { $_.ver1 -notlike "$($_.ver2)*" }
    

    Some of the results you get back will be for superficial things (like commas instead of decimal points, extra zeros, etc), but a lot will be because the actual string versions are different from the versions pulled from the version parts.

    This is an example for a specific file:

    PS> dir C:\Windows\system32\mshtml.dll | 
        select name, @{N="Ver1";E={$_.VersionInfo.FileVersion}}, 
                     @{N="Ver2";E={"{0}.{1}.{2}.{3}" -f $_.VersionInfo.FileMajorPart, $_.VersionInfo.FileMinorPart, $_.VersionInfo.FileBuildPart, $_.VersionInfo.FilePrivatePart}} |
        fl
    
    Name : mshtml.dll
    Ver1 : 11.00.9600.16410 (winblue_gdr.130923-1706)
    Ver2 : 11.0.9600.16476

    11.0.9600.16476 is definitely not the same as 11.0.9600.16410.

    Sunday, December 15, 2013 8:49 PM
  • If two strings match exactly then they are equal.  What you cannot do is determine which version is newer with string comparison.

    Go back and read what I wrote very carefully.  I posted that if they are equal then the versions are equal.  If they are not equal then you must use a different method to further compare.

    Assume I want to find all files that are the same.  String comparison will work.  I want to know if a DLL is version 1.1.1.1.  If the strings compare then it is the version I want.  If they don't compare then it is the wrong version.  If we know the file we can make that assumption.  If we don't care about which is newer but only if they are the same.


    ¯\_(ツ)_/¯

    Sunday, December 15, 2013 8:57 PM
  • jrv, I thought we covered the comparison thing. I've said this already, but I'll say it again: if the strings are in the 'x.x.x.x' format, yes, you most certainly can compare them. You do it by casting them to [version] types. The [version] object knows how to do the comparison. Two strings are not being compared at that point--two versions are. 

    If you're using the FileVersion property, then, no, you can't do version comparison with those strings since text can be mixed in there. In my last post, I was trying to explain why you don't want to use that property, though. 

    Now, I'm still not sure what you're talking about using, but I'll go ahead and assume you're talking about using that property, since going through the trouble of combining the version parts into a string means you've done 99% of the work of converting it into a [version] object that can be compared. If you're talking about getting the version a different way, though, please feel free to let me know what it is.

    If you will "[g]o back and read what I wrote very carefully", I showed you an example, using the 'mshtml.dll' file, that shows why you should never trust the FileVersion property. If you want to check that file's version (at least on anything higher than Windows 7), you cannot trust what's in the FileVersion property, and you must compute the version by combining the four file version parts. 

    On my computer, the FileVersion is coming back as '11.00.9600.16410 (winblue_gdr.130923-1706)' on that file, even though the true version is higher (ver1 is the 'FileVersion'; ver2 is the version computed from the four version parts). As far as I know, it will continue to return that, regardless of the true version of the file, until the 'c:\windows\system32\en-US\mshtml.dll.mui' file is updated (I think that's where the FileVersion property is coming from on my computer). For that reason, I cannot trust the version on that file if I use FileVersion.

    If the only thing you care to use file versions for is determining whether or not a file is the same, I think you should look into computing a hash.

    Almost any time I've needed to compare a file version, checking for equality was not what I was looking for. More times than not, I need to know that a file version is at least a certain version, and sometimes I need to make sure it's within a range. [version] comparisons handle that completely. Converting the versions to numbers, as Bill does, will also handle that.
    Sunday, December 15, 2013 9:44 PM
  • If the strings are not in the same format then the files are not the same.  Simple logic.  IF they are in the same format and compare exactly then the strings are identical.  Who cares what is in them?

    Comparing to equal will always work.  If they do not compare as equal you know the files are different.

    That is all. That is all I said.  No need to make more of it.


    ¯\_(ツ)_/¯

    Sunday, December 15, 2013 10:03 PM
  • If the strings are not in the same format then the files are not the same.  Simple logic.  IF they are in the same format and compare exactly then the strings are identical.  Who cares what is in them?

    Comparing to equal will always work.  If they do not compare as equal you know the files are different.

    That is all. That is all I said.  No need to make more of it.


    ¯\_(ツ)_/¯

    You never answered how you're getting this string. Again, as I've said in more than one post now, if you're using .VersionInfo.FileVersion, that string is not accurate for a significant number of files (especially Microsoft files).

    You are correct when you say that if the strings don't match they're not the same. But you're forgetting that the strings may be the same and yet the file isn't the same. To me, that's a big deal. If you're cataloging file versions, your information could be incorrect.

    Here's a scenario:

    1. I check the 'mshtml.dll' VersionInfo.FileVersion today. It reports back that it is '11.00.9600.16410 (winblue_gdr.130923-1706)'. It is actually at version '11.0.9600.16476' (which can be confirmed by using Explorer or by combining the File*Part properties as in previous posts).

    2. An Internet Explorer patch comes along that updates the file to '11.0.9600.16482' (something higher than 16476)

    3. I check the version of the file again using the VersionInfo.FileVersion property. It reports back '11.00.9600.16410 (winblue_gdr.130923-1706)', which is the same thing it showed before the patch. The file is obviously different, but the FileVersion property is reporting that it hasn't changed.

    So, if you're using that property, you will miss file changes on a significant number of files. 'mshtml.dll' is just one example. Go back and read my example to get a list of DLLs from system32 that are affected by this (remember, you will get some false positives).

    If you're combining the File*Part properties (which is what both Bill and I do), then the version will always be in a known format, and then all of the stuff you said about not being able to compare them is simply not correct.

    So, how are you proposing that someone gets a file's version from PowerShell?

    Sunday, December 15, 2013 10:58 PM
  • Doesn't matter. For the same file it is either equal or not equal.  No other choices.  If we only care about equal then we are done.

    SAME FILE.  SAME STRING.

    Assume file myfile.dll - you have two copies and know they are PE files  Compare the fileversion and it either compares or doesn't.  If it compares they are the same fileversion.  The numbers have to be the same.


    ¯\_(ツ)_/¯

    Sunday, December 15, 2013 11:05 PM
  • Try this;
    (dir c:\windows\system32\mshtml.dll).VersionInfo.FilePrivatePart


    ¯\_(ツ)_/¯

    Sunday, December 15, 2013 11:12 PM
  • Doesn't matter. For the same file it is either equal or not equal.  No other choices.  If we only care about equal then we are done.

    SAME FILE.  SAME STRING.

    Assume file myfile.dll - you have two copies and know they are PE files  Compare the fileversion and it either compares or doesn't.  If it compares they are the same fileversion.  The numbers have to be the same.


    ¯\_(ツ)_/¯

    You're kidding, right? Are you even reading my posts? What part of that scenario did you not understand?

    If you're using the .VersionInfo.FileVersion property and the file has a MUI resource file, the FileVersion is not coming from the binary file you think it is. The File*Part properties are, though.

    So, your statements are only true in some cases. They are wrong for a lot of files, though (especially Microsoft). Please work through the examples I've presented above and let me know at which point you're having trouble.

    I'll ask again, how are you proposing that someone gets a file's version from PowerShell?

    Sunday, December 15, 2013 11:12 PM
  • Try this;
    (dir c:\windows\system32\mshtml.dll).VersionInfo.FilePrivatePart


    ¯\_(ツ)_/¯

    I agree, using that is part of the best solution (there may be something better, but I haven't found it yet). You also need to use the other three, though: FileMajorPart, FileMinorPart, FileBuildPart. Those are what I've been referring to as the File*Part properties, or the four version parts.

    And, like I've said I don't know how many times in this thread, putting those four properties together gives you a version in a known, controlled, format every time from PowerShell. And, with that known, controlled string, version comparisons are now possible every single time :)

    See what I mean?
    Sunday, December 15, 2013 11:23 PM
  • In one of the original posts I said "if you know the file".  I also said that there is no one way to compare all files.  You have just proven that contention.   You must know the rules which is why Microsoft always publishes that information for patches.  Even if all version info matches you still have to use file date to test the files.

    There is no one way to do this.  It is product and subsystem dependent. 

    Bills issue is that the internal 64 bit number is always accurate to a point except for driver and system patches.  Microsoft is trying to upgrade that..  They through out the biggest headache which was the Office people who had ther own way of patching OS files.  Now no one is allowed to update the "Windows" folder exept system psople although vendors still insist on ignoring that.  It would be really nice inversion checking actually worked everywhere the same.


    ¯\_(ツ)_/¯

    Sunday, December 15, 2013 11:23 PM
  • Try this;
    (dir c:\windows\system32\mshtml.dll).VersionInfo.FilePrivatePart


    ¯\_(ツ)_/¯

    I agree, using that is part of the best solution (there may be something better, but I haven't found it yet). You also need to use the other three, though: FileMajorPart, FileMinorPart, FileBuildPart. Those are what I've been referring to as the File*Part properties, or the four version parts.

    And, like I've said I don't know how many times in this thread, putting those four properties together gives you a version in a known, controlled, format every time from PowerShell. And, with that known, controlled string, version comparisons are now possible every single time :)

    See what I mean?

    Except that with patches we need to use file date too.


    ¯\_(ツ)_/¯

    Sunday, December 15, 2013 11:24 PM
  • In one of the original posts I said "if you know the file".  I also said that there is no one way to compare all files.  You have just proven that contention.   You must know the rules which is why Microsoft always publishes that information for patches.  Even if all version info matches you still have to use file date to test the files.

    There is no one way to do this.  It is product and subsystem dependent. 

    Bills issue is that the internal 64 bit number is always accurate to a point except for driver and system patches.  Microsoft is trying to upgrade that..  They through out the biggest headache which was the Office people who had ther own way of patching OS files.  Now no one is allowed to update the "Windows" folder exept system psople although vendors still insist on ignoring that.  It would be really nice inversion checking actually worked everywhere the same.


    ¯\_(ツ)_/¯

    Nope, wrong again. It does work the same way everywhere. Combine the four File*Part properties. That's what Explorer does. That's what Bill does. That's how you do it every single time you're trying to get the file version from a binary file. It is absolutely not product dependent.

    The FileVersion string property is what doesn't always work because the version info call isn't necessarily checking the file you think it's checking. This is probably only the case for Vista and higher...

    You are incorrect about Bill's issue. Bill's 64-bit numbers are perfectly accurate for drivers and systems patches. Just know that if you use the VersionInfo.FileVersion property, you're not guaranteed to see the what the file thinks its true version is. If you combine the four File*Part properties, you will see exactly what the file thinks its version is. You can safely use those properties to get a listing of a file version from any file. If the file comes back with an incorrect version, it's the file's fault.

    And, one more time, if you go through the work of combining the four properties, you can safely compare them with other versions in the same format 100% of the time.

    I don't know how else to explain this, so I'm done with this thread unless someone else has something to add.

    Sunday, December 15, 2013 11:44 PM
  • We're talking about a couple of different concepts here...

    1. Does VersionInfo.FileVersion contain accurate data? This depends on the file. Sometimes VersionInfo.FileVersion matches Major/Minor/Release/Build, but sometimes it does not. Printer driver DLLs, for example, will have VersionInfo.FileVersion set to the printer driver file version, and Major/Minor will be (for example) 0.3 (to indicate it's a version 3 driver). Some sample output on my machine:


    PS > $verInfo = (get-item $ENV:SystemRoot\system32\spool\DRIVERS\x64\3\PSCRIPT5.DLL).VersionInfo
    PS > $verInfo.FileVersion
    6.1.7600.16385 (win7_rtm.090713-1255)
    PS > "{0}.{1}.{2}.{3}" -f $verInfo.FileMajorPart,$verInfo.FileMinorPart,
    >>  $verInfo.FileBuildPart,$verInfo.FilePrivatePart
    0.3.7601.17514

    In other words, the convention is that printer driver files use Major/Minor to store different metadata than the "actual" file version. The only choice here is to compare/parse the FileVersion string.

    2. Casting to [Version] works fine if you are comparing versions with all four parts of the version number in place (Major/Minor/Release/Build). If you don't put all four parts in place, casting to [Version] can lead to some interesting results. For example:


    PS > ([Version] "6.1.0") -eq ([Version] "6.1")
    False

    (Although this is by design, I contend that the design is not correct, but that's a separate discussion.) To avoid ambiguity, use all four parts of the version number. Alternatively, you can convert the four parts to a UInt64 and then compare.

    I believe there was some confusion between these two issues and I hope this clarifies. In general, Major/Minor/Release/Build should work to compare file versions, but this can depend on what kind of file you're comparing.

    Bill


    • Edited by Bill_Stewart Tuesday, December 17, 2013 10:01 PM Fixed typo
    Monday, December 16, 2013 3:51 PM
  • You can work around that behavior with a pretty small function:

    function Get-Version
    {
        [CmdletBinding()]
        param (
            [Version]$Version
        )
    
        $major = [Math]::Max($Version.Major, 0)
        $minor = [Math]::Max($Version.Minor, 0)
        $build = [Math]::Max($Version.Build, 0)
        $revision = [Math]::Max($Version.Revision, 0)
    
        return New-Object Version($major, $minor, $build, $revision)
    }
    
    ([Version] "6.1.0") -eq ([Version] "6.1")
    
    False
    
    (Get-Version "6.1.0") -eq (Get-Version "6.1")
    
    True




    • Edited by David Wyatt Monday, December 16, 2013 5:00 PM
    Monday, December 16, 2013 5:00 PM
  • My point was simply that .NET is simply not doing the right thing in that case. Your function is fine as a workaround (that we shouldn't have to use, but it is what it is).

    Bill

    Monday, December 16, 2013 5:10 PM