Using CoreWLAN on MacOS 10.5

When Apple’s OS X version 10.6 shipped, they broke compatibility will all 3rd party geolocation applications.  This wasn’t entirely unexpected as we all were using a private framework which wasn’t documented.  So, Firefox 3.5 and Gears, and probably Skyhook, are busted right now on OS X 10.6.

Apple has provided, with limited documentation, a new public framework called CoreWLAN.  It looks pretty easy to use.  To do a scan of the WIFI access points, you can simply do:

#import <Cocoa/Cocoa.h>
#import <CoreWLAN/CoreWLAN.h>

NSError *err = nil;
NSDictionary *params = nil;

NSArray* scan = [NSMutableArray arrayWithArray:[[CWInterface interface] scanForNetworksWithParameters:params error:&err]];

This is all fine and dandy.  However, Firefox uses 10.5, and so using the above code will not compile using the old SDK.  Unfortunately, Apple didn’t provide any C apis – only exposing this objective-C API.  So, what I had to do was some objective-c magic.  Check out the reference (http://developer.apple.com/mac/library/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html)

Basically, we needed to be able to dynamically load the CoreWLAN library, that is simple enough:

void *corewlan_library = dlopen(“/System/Library/Frameworks/CoreWLAN.framework/CoreWLAN”,  RTLD_LOCAL);

Now that this library has been loaded, we want to simulate the objective-c call “[CWInterface interface]“.

// get the class
Class CWI_class = objc_getClass(“CWInterface”);
// register the selector name
SEL interfaceSel = sel_registerName(“interface”);
// make the call
id interface = objc_msgSend(CWI_class, interfaceSel);

This mess above is the same as:

id interface = [CWInterface interface];

As you probably can see, it isn’t that hard to do this, just it is syntactically nasty.  Here is the source to the little application I was testing with:

#include <mach-o/dyld.h>
#include <dlfcn.h>
#include <unistd.h>

#include <objc/objc.h>
#include <objc/objc-runtime.h>

Class CWI_class = objc_getClass(“CWInterface”);
printf(“cwi class %x\n”, CWI_class);

SEL interfaceSel = sel_registerName(“interface”);
printf(“sel: %x\n”, interfaceSel);

SEL scanSel = sel_registerName(“scanForNetworksWithParameters:error:”);
printf(“scanSel: %x\n”, interfaceSel);

id interface = objc_msgSend(CWI_class, interfaceSel);
printf(“interface: %x\n”, interface);

id scanResult = objc_msgSend(interface, scanSel, 0, 0);
printf(“scanResult: %x\n”, scanResult);

Hope this helps.  Please let me know of an easier way, if one exists.

This entry was posted in mozilla and tagged , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

5 Comments

  1. Tom
    Posted September 16, 2009 at 5:20 pm | Permalink

    NSBundle handles all the dynamic loading details for you (and encapsulates the details of where the actual library is in the framework).

    NSBundle * bundle = [[NSBundle alloc] initWIthPath:@”/System/Library/Frameworks/CoreWLAN.framework”];

    Class CWI_class = [bundle classNamed:@(”CWInterface”];

    Then you should be able to use the normal Obj-C message passing syntax with the Class object instead of using objc_msgSend. (Although Xcode will give you warnings for not having prototypes, you can still send the messages using the standard syntax.)

    Also, in your printf statements, you may want to call [[object description] UTF8String] to get a C-string with details on the object.

  2. dgatwood
    Posted October 31, 2009 at 1:47 pm | Permalink

    You’re both making this way too hard.

    1. Add the framework to the project. Build against the 10.6 SDK.
    2. Change the compiler settings to target earlier versions of the OS.
    3. In “Other Linker Flags”, add “-weak_framework CoreWLAN” (without the quotes).
    4. In your code, before you try to use any class methods on CWInterface or whatever, make sure the class exists:

    id cwi = NSClassFromString(@”CWInterface”);
    if (cwi) {
    /* Optionally use cwi instead of CWInterface */
    interfaces = [cwi supportedInterfaces];
    }

    You can even store the class reference as an ivar so that you never have to do more than:

    if (_cwi) {
    /* 10.6 code */
    } else {
    /* original 10.5 code */
    }

    or whatever.

  3. Joth
    Posted March 5, 2010 at 3:26 am | Permalink

    Thanks for sharing the info. This set me in the right direction for implementing geolocation in Chromium on OSX 10.6 (patch pending: http://codereview.chromium.org/669083)

    • Posted March 5, 2010 at 9:20 am | Permalink

      Joth, very cool. Can’t wait to check it out!

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>