Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

Checking file versions is surprisingly hard.

Checking file versions is surprisingly hard.

  • Comments 28

I was wandering around the web the other day and ran into this post.  In general I don’t have many issues with the post, until you get to the bottom of the article.  The author mentions that his code only runs on Win7 or newer so he helpfully included a check to make sure that his code only runs on WIn7:

// Example in C#.

internal bool SupportsTaskProgress() {
    if (System.Environment.OSVersion.Version.Major >= 6) {
        if (System.Environment.OSVersion.Version.Minor >= 1) {
            return true;
        }
    }
    return false;
}

This is a great example of why it’s so hard to write code that checks for versions.  The problem here is that this code is highly likely to fail to work on the next version of Windows (or whenever Windows 7.0 is released).  In that case SupportsTaskProgress will incorrectly return false.

 

Personally I wouldn’t even bother writing the SupportsTaskProgress function this way.  Instead I’d check for the “new TaskbarLib.TaskbarList()” call to return NULL and assume that if it returned NULL the API call wasn’t supported (the non COM interop equivalent would be to check for a failure on the call to CoCreateInstance).  That way the code would work even if (for some obscure reason) the taskbar logic was ported to a previous OS version.

 

If I simply HAD to keep the SupportsTaskProgress function, I’d rewrite it as:

// Example in C#.

internal bool SupportsTaskProgress() {
    if (System.Environment.OSVersion.Version.Major >= 6) {
        if (System.Environment.OSVersion.Version.Major == 6) {
if (System.Environment.OSVersion.Version.Minor >= 1) { return true;
}
return false; }
return true; } return false; }

That way it would only check for minor version being greater than 1 if the major version is 6.  I suspect that this code could be tightened up further as well.

 

This is a part of the reason that picking a version number for the OS is so complicated.

  • OK, I've seen people code like this and have never figured out why. Given that the && operator will stop evaluating as soon as one condition returns false, why not use that to combine the IF statements?

  • SlackMaster K: I wanted to replicate the original style.  See my "I suspect that this code could be tightened up further as well" comment.

  • You could do:

    internal bool SupportsTaskProgress() {

      return System.Environment.OSVersion.Version >= new Version(6, 1)

    }

    That should be equivalent.

  • In the general case for Versions V, you have M.m.r.p (Major, minor, revision and patch—or whatever they're called).  To compare two, you have to start with the major one and cascade:

    if (M1 > M2 ||

        (M1 == M2 &&

          (m1 > m2 ||

            (m1 == m2 &&

              (r1 > r2 ||

                (r1 == r2 &&

                   (p1 > p2))))))) {

       // V1 > v2

    }

    I've not tried to optimize it beyond that, as it doesn't need to happen that often.

  • Hey all - I'm the "author" mentioned above.

    I won't go into the reasons of the brain-fart with the code as the original post that contains it has been updated to explain why, but one thing I'd like to mention is (which is directly above the actual code in the original posting):

    "The comparisons haven’t been short-circuited due to reasons of clarity."

    The visitors of my site tend to be end users as opposed to programmers (although some are beginners); I've learnt in the past to keep code examples very simple otherwise I get "emails".

    I've now updated the code and also written it so it's less readable for the visitors to my site. *cough*

  • We shouldn't blame the task as being hard.  The concept here is easy.  It's that the design of the API makes it hard.  So someone should fix the design and add a Win32 function that checks the version like Paul's code shows.

    In general, APIs should make easy things easy and complex things hard, not the other way around.

  • BootBlock: Read your email please.

  • Slackmaster: Not exactly like this, but I've broken up conditionals like this just to make debugging easier.

    As I'm stepping through something particularly complicated in a debugger, it's nice to know why I'm in (or not in) this code without having to count parens, consider precedence, and deal with the odd assignment/ternary embedded in an overworked conditional all the while doing whatever it takes to get the values of the variables IN the statement.

  • Like Paul says. This is trivial in .net since Version implements a greater than operator and icomparable

  • Let .net do the "heavy lifting" for you:

    Version win7 = new Version("7.1");

    Version osVersion = new Version(System.Environment.OSVersion.Version.Major, System.Environment.OSVersion.Version.Minor)

    if(osVersion >= win7)

    ...

  • Part of the problem is the rather daft Major/Minor version numbering scheme itself. If each new release was a x.0 release, then version detection code would be a cinch since you'd just do a straight comparison of the major version numbers.

    I've never entirely understood the rational behind overcomplicating this with an additonal Minor version. Was there really a worry we might run out of integers?

  • If I was doing it in C I'd be tempted to put the major version in the top bits of an integer and the minor version in the bottom bits and compare.

    E.g.

    #define PACK_VERSION(major, minor) ((major<<16)|minor)

    Then you can just compare integers. That said it feels wrong to do it this way rather than checking which functions are available. Actually, I'd rather just code to a subset of Win32 that has been around for ages because I've got less chance of breaking stuff on one OS.

  • A clearer way to check by version number, using a concept easily portable to any other OS or app:

    internal bool SupportsTaskProgress() {

       if (System.Environment.OSVersion.Version.Major > 6) return true;

       if (System.Environment.OSVersion.Version.Major < 6) return false;

       return System.Environment.OSVersion.Version.Minor >= 1;

    }

    If you prefer, change the last line to:

       if (seov.minor >= 1) return true;

       else return false;

    You can extend this general idea as needed to account for revision, patchlevel, etc.

    (Sorry if this isn't kosher/canonical C#; I don't actually know that, being mainly a plain old C-monkey.)

  • Anon, the idea of packing major and minor into an int actually doesn't work that well - we tried it in DOS years and years ago.

    I actually love the .Net solution (make Version an icomparable and then move the logic somewhere lower).  The cool thing is that it's essentially available in unmanaged code via theVerifyVersionInfo API: http://msdn.microsoft.com/en-us/library/ms725492(VS.85).aspx

    But it doesn't change my base comment: Don't ever check windows versions.  Instead check for functionality being present or not.  

  • I somehow find it ironical that someone has trouble with comparing versions and can get COM right.. :|

Page 1 of 2 (28 items) 12