Minimum System Version Check - With Reliable Notification

  • List:

      Over the years, the standard mechanisms for requiring a minimum
    system version and displaying a kind message to the user have proven
    unreliable. I recently had the need to revisit this issue but (as far
    as I can tell) searching the list archives and all the other usual
    sources don't quite reveal a complete solution, merely suggestions.

      I've seen a few people suggest creating a 'miniature' run loop or
    using Carbon, but in my mind, the cleanest, most straightforward
    solution was just to put the responsibility back into the hands of the
    OS (where it was supposed to be in the first damn place). AppleScript
    to the rescue.

      For the archives, following is a quick and dirty - but complete -
    solution to this issue. It assumes 10.5 as the minimum system (I trust
    it's obvious what to change and where for other versions). Perhaps not
    so obvious to newbies, this belongs in your project's main.m file.

    int main(int argc, char *argv[])
    {
    // Obtain system version
    static SInt32 systemVersion;
        Gestalt(gestaltSystemVersion, &systemVersion);

    // Only run if we're on 10.5 or above ...
    if (systemVersion >= 0x1050)
    {

      // We're fine - start the app instance
      return NSApplicationMain(argc,  (const char **) argv);

    } else {

      // Fails minimum requirement - we'll use AppleScript to
      // display a warning message to bypass the OS X
      // system version checking mess ...

      // Set up an autorelease pool (NSAppleScript needs it)
      NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

      // Create then execute the AppleScript, ignoring any errors
      // (Gasp! Ignore errors? Yes - if the script doesn't compile
      // or it fails, more is wrong than we can do anything about ...)
      NSString * source = @"beep\ndisplay alert \"This awesome application
    requires Mac OS X 10.5 or above and cannot run on your computer. Why
    on God's great creation are you not up to date?! Bad user, BAD USER!\"
    as warning";
      NSAppleScript * script = [[[NSAppleScript alloc]
    initWithSource:source] autorelease];
      [script executeAndReturnError:nil];

      // Release the pool. It probably doesn't matter, but what kind
      // of Cocoa developer would I be if I didn't? What will people SAY?!
      [pool release];

      // Goodbye, cruel world!
      return -1

    }

    // To be honest, I'm not sure what's best here, but since
    // NSApplicationMain() isn't supposed to come back, errors
    // are handled, and main() requires a return, a clean
    // return seems the right thing to do ... YMMV.
    return 0;
    }

      AppleScript will beep (with the user's preferred standard error
    sound) display the caution icon with your app's icon grafted atop. You
    may or may not want to lose the beep. Not sure why caution / error
    dialogs don't automatically sound the error beep anyway, but that's
    another philosophical debate entirely.

      I'd recommend completely foregoing the use of the
    LSMinimumSystemVersion key approach (search the docs and the list
    archives if you're unfamiliar) since the user will get two "this won't
    work" messages when the LSMinimumSystemVersion is actually honored.
    Two such messages may seem like you're just rubbing it in. ;-)

      Alternatively, you could include the already-compiled script in the
    app's resources to save a millisecond or two during runtime, but I
    don't feel it's necessary and I would think it'd be easier just to
    change the message in the app's source.

      Also, regarding the return, I didn't bother looking up exactly what
    the implications are but I'd imagine there are a few other ways it can
    be done, all of which are probably more or less fine. I'd be
    interested in opinions on this.

      Experts are welcome to pick the whole thing apart and point out its
    wrongness as desired. :-)

    --
    I.S.
  • Hi,
    I think the primary issue with this approach is that the app may not get as
    far as main.

    If the app uses a symbol from the frameworks that is not available at
    runtime, dyld will fail to load the binary.

    Aaron Hillegass gives a solution at
    http://weblog.bignerdranch.com/?p=13that involves using an auxiliary
    executable.  A shell process that has very
    good compatibility loads, and if it determines that the OS is new enough, it
    loads the real application.

    -Ken

    On Sun, Feb 1, 2009 at 9:26 AM, I. Savant <idiotsavant2005...> wrote:

    > List:
    >
    > Over the years, the standard mechanisms for requiring a minimum system
    > version and displaying a kind message to the user have proven unreliable. I
    > recently had the need to revisit this issue but (as far as I can tell)
    > searching the list archives and all the other usual sources don't quite
    > reveal a complete solution, merely suggestions.
    >
    > I've seen a few people suggest creating a 'miniature' run loop or using
    > Carbon, but in my mind, the cleanest, most straightforward solution was just
    > to put the responsibility back into the hands of the OS (where it was
    > supposed to be in the first damn place). AppleScript to the rescue.
    >
    > For the archives, following is a quick and dirty - but complete - solution
    > to this issue. It assumes 10.5 as the minimum system (I trust it's obvious
    > what to change and where for other versions). Perhaps not so obvious to
    > newbies, this belongs in your project's main.m file.
    >
    >
    >
    > int main(int argc, char *argv[])
    > {
    > // Obtain system version
    > static SInt32 systemVersion;
    > Gestalt(gestaltSystemVersion, &systemVersion);
    >
    > // Only run if we're on 10.5 or above ...
    > if (systemVersion >= 0x1050)
    > {
    >
    > // We're fine - start the app instance
    > return NSApplicationMain(argc,  (const char **) argv);
    >
    > } else {
    >
    > // Fails minimum requirement - we'll use AppleScript to
    > // display a warning message to bypass the OS X
    > // system version checking mess ...
    >
    > // Set up an autorelease pool (NSAppleScript needs it)
    > NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    >
    > // Create then execute the AppleScript, ignoring any errors
    > // (Gasp! Ignore errors? Yes - if the script doesn't compile
    > // or it fails, more is wrong than we can do anything about
    > ...)
    > NSString * source = @"beep\ndisplay alert \"This awesome
    > application requires Mac OS X 10.5 or above and cannot run on your computer.
    > Why on God's great creation are you not up to date?! Bad user, BAD USER!\"
    > as warning";
    > NSAppleScript * script = [[[NSAppleScript alloc]
    > initWithSource:source] autorelease];
    > [script executeAndReturnError:nil];
    >
    > // Release the pool. It probably doesn't matter, but what
    > kind
    > // of Cocoa developer would I be if I didn't? What will
    > people SAY?!
    > [pool release];
    >
    > // Goodbye, cruel world!
    > return -1
    >
    > }
    >
    > // To be honest, I'm not sure what's best here, but since
    > // NSApplicationMain() isn't supposed to come back, errors
    > // are handled, and main() requires a return, a clean
    > // return seems the right thing to do ... YMMV.
    > return 0;
    > }
    >
    > AppleScript will beep (with the user's preferred standard error sound)
    > display the caution icon with your app's icon grafted atop. You may or may
    > not want to lose the beep. Not sure why caution / error dialogs don't
    > automatically sound the error beep anyway, but that's another philosophical
    > debate entirely.
    >
    > I'd recommend completely foregoing the use of the LSMinimumSystemVersion
    > key approach (search the docs and the list archives if you're unfamiliar)
    > since the user will get two "this won't work" messages when the
    > LSMinimumSystemVersion is actually honored. Two such messages may seem like
    > you're just rubbing it in. ;-)
    >
    > Alternatively, you could include the already-compiled script in the app's
    > resources to save a millisecond or two during runtime, but I don't feel it's
    > necessary and I would think it'd be easier just to change the message in the
    > app's source.
    >
    > Also, regarding the return, I didn't bother looking up exactly what the
    > implications are but I'd imagine there are a few other ways it can be done,
    > all of which are probably more or less fine. I'd be interested in opinions
    > on this.
    >
    > Experts are welcome to pick the whole thing apart and point out its
    > wrongness as desired. :-)
    >
    > --
    > I.S.
    >
  • On Feb 1, 2009, at 3:54 PM, Ken Ferry wrote:

    > I think the primary issue with this approach is that the app may not
    > get as far as main.
    >
    > If the app uses a symbol from the frameworks that is not available
    > at runtime, dyld will fail to load the binary.
    >

      Ah *ha* ... you are of course absolutely right.

      I confess I hadn't even thought this issue. Alas it seems Aaron's
    suggestion is probably the only real solution until we're so far past
    10.5 (where the issue is supposed to be "good and fixed") that we
    react with shock when we learn the user is still using 10.5.

      That sucks.

      Thanks for the shot of common sense, though - that was pretty lame
    of me. ;-) I completely failed to think the problem all the way
    through. I guess it's obvious that the app I tested this on uses some
    pretty plain (old-school) Cocoa. :-)

    --
    I.S.
previous month february 2009 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28  
Go to today
MindNode
MindNode offered a free license !