Where’s this class from?

February 14th, 2008 by Joe Ranieri

Occasionally I want to know where a specific class is implemented. Today I wanted to know where iCal’s main calendar view was implemented, in hopes of stealing it for my own uses.

I don’t feel much like typing the description, so here’s a video “tutorial.”


Hijacking JavaScript Functions

February 11th, 2008 by Ryan Govostes

I spent several hours last week tracking down an obnoxious bug in WebKit JavaScriptCore’s Function.toString() implementation where multiple var declarations on a single line would get grouped with parentheses (Radar, Trac, Bugzilla). The incredible WebKit team made a working patch within 23 hours of my report, kudos to them.

Joe and I very much enjoy hijacking code. While this may become a recurring theme on this blog, right now I’m just going to share how to do it in JavaScript. Let’s say one of the .js files included from your page declares the function foo() as

function foo() {
    var x = "spinach";
    alert(x);
}

If you run foo(), it will obviously display a dialog reading “spinach”. But for some reason or other, you need it to say “eggplant” instead, but you can’t alter the original code. Furthermore, pretend we’re in a situation where it’s inconvenient to reimplement the function. No problem — we can just hijack it and modify the code:

// First, get the code to the function
// Here we strip off the "function foo() { ... }"
var c_foo = foo.toString().replace(/^[^{]*{/|>, '').replace(/}$/|>, '');
 
// Now perform whatever modifications we need
c_foo = c_foo.replace('spinach', 'eggplant');
 
// Now overwrite the function with a new Function object using our code
foo = new Function(c_foo);

Now whenever foo() is called, it will use our modified code. Of course, in this simple example we could have just redeclared foo(), but this becomes far more feasible with more complex code.

As an interesting aside, when you call toString() on a function, it’s not actually printing out the function as it was declared (hence, the bug I mentioned at the top of the post). In the case of JavaScriptCore (used by Safari), a function is broken into a graph of various nodes representing keywords, loops, strings constants, and so on. When you convert the function to a string, it traverses the graph and constructs a more familiar representation of it. As such, it’s kind of a handy way to reformat obfuscated (or just hard to read) code, but you’ll lose commenting.


An RB feature you didn’t know about

February 5th, 2008 by Joe Ranieri

Some of you might know that REALbasic will highlight URLs in comments and allow you to click them. But, how many of you knew that has a special “rb-feedback” url type to make linking to feedback reports even easier?

// working around rb-feedback://miokjjuu

Neat eh?


Window.FloaterProcess Fix for Mac OS X

February 1st, 2008 by Jonathan Johnson

Recently on the NUG, a question came up about making Window.FloaterProcess work correctly on Mac OS X. This property is designed to make it easy to have one of your global floating windows show up only “inside” of another application. After a lack of volunteers, I coded up a generic drop-in fix.

Module FloaterProcessFix
  Private Const kEventAppFrontSwitched = 7
  Private Const kEventClassApplication = 'appl'
 
  Protected Sub CheckOpenWindows()
    declare function GetFrontProcess lib "Carbon" (psn as Ptr) as Integer
    dim psn as new MemoryBlock(8)
    dim err as Integer = GetFrontProcess(psn)
    if err = 0 then
      declare function GetProcessInformation lib "Carbon" (psn as Ptr, _
        info as Ptr) as Integer
      dim info as new MemoryBlock(60)
      err = GetProcessInformation(psn, info)
      if err = 0 then
        dim p as Ptr = info
        dim type as String = p.OSType(20)
        HideShowFloaters(type)
      end if
    end if
  End Sub
 
  Protected Sub Install()
    declare function NewEventHandlerUPP lib "Carbon" (handler as Ptr) _
      as Integer
    declare function InstallEventHandler lib "Carbon" (target as Integer, _
      handler as Integer, itemCount as Integer, typeList as Ptr, _
      userData as Integer, outRef as Integer) as Integer
    declare function GetApplicationEventTarget lib "Carbon" () as Integer
 
    dim handler as Integer = _
      NewEventHandlerUPP(addressOf CarbonEventHandler)
    dim eventTypes as new MemoryBlock(8)
    eventTypes.Int32Value(0) = kEventClassApplication
    eventTypes.Int32Value(4) = kEventAppFrontSwitched
    dim err as Integer
    err = InstallEventHandler(GetApplicationEventTarget, _
      handler, 1, eventTypes, 0, 0)
 
    CheckOpenWindows
  End Sub
 
  Private Function CarbonEventHandler(inHandlerCallRef as Integer, _
      inEvent as Integer, inUserData as Integer) As Integer
    CheckOpenWindows
    return 0
  End Function
 
  Private Sub HideShowFloaters(frontAppCode as String)
    for i as integer = 0 to WindowCount - 1
      if strcomp(Window(i).FloaterProcess, frontAppCode, 0) = 0 then
        Window(i).Show
      elseif Window(i).FloaterProcess <> "" then
        Window(i).Hide
      end if
    next
  End Sub
End Module

How does the code work? There is a Carbon event that fires each time a new application is activated. By installing this handler, we can then find out what the creator code of the frontmost app is, loop over our windows, and hide/show them appropriately. Because Carbon events are fired only when the event happens, the CPU usage is nearly 0, and you can once again use FloaterProcess on OS X.

You can download a full example here.


Boot Camp Drivers from OS X

January 17th, 2008 by Ryan Govostes

Windows and I don’t have a very friendly relationship. Over the course of a year, I might reinstall my copy of Windows XP SP 2 half a dozen times because, inevitably, some .dll, .sys, or .act file has managed to ruin it for everyone by going and getting corrupted. This has happened so many times that I now have to call Microsoft every time I want to activate Windows (twice for each install, since I also use VMWare) and listen to a bot read off a thousand-digit number in groups of five.

The most recent argument between Windows and me happened on Tuesday; an annoying freeze led to a nightmarish six hour battle. Sadly, Windows emerged the victor by dying completely and thoroughly. Time for a reinstall, which means tracking down all of the .dll, .sys, and .act files that I need…

As you may know, Boot Camp comes with drivers to increase compatibility with Apple hardware. During the betas, they were stored in a disk image inside the application bundle; in Leopard, the drivers are stored on the Leopard install DVD. The DVD is a hybrid disc, meaning it has partitions for both Windows and Mac OS X. By default, OS X will only mount the HFS+ partition (containing the installer), and Windows will only mount the ISO 9660 partition (containing the Boot Camp drivers).

So if you don’t have your install disc and no writable DVD-DL is within arm’s reach, is it possible to get OS X to mount the ISO 9660 partition from the ADC disk image and copy the drivers to a thumb drive? Indeed it is.

(As an aside, you can view the partition map of the disk image by running hdiutil pmap2 leopard_9a581_userdvd.dmg)

If we use hdiutil to attach the image as a block device, it’s possible to get the ISO 9660 part to mount by using the cd9660.util program.

$ hdiutil attach leopard_9a581_userdvd.dmg -nomount
expected   CRC32 $0108CBDC
/dev/disk3          	Apple_partition_scheme
/dev/disk3s1        	Apple_partition_map
/dev/disk3s2        	Apple_Driver_ATAPI
/dev/disk3s3        	Apple_HFS
$ mkdir /Volumes/Drivers
$ /System/Library/Filesystems/cd9660.fs/cd9660.util -m disk3 /Volumes/Drivers

And when you’re done:

$ umount /Volumes/Drivers
$ rm -Rf /Volumes/Drivers

That’s all for today. If you need me, I’ll be on the phone with Microsoft…


Clocks with Google Chart API

January 7th, 2008 by Ryan Govostes

Have you seen Google’s Chart API? Send it a formatted GET request and it will spit out a pretty graph. The API is fairly powerful, supporting dozens of options and five different chart types, including Venn diagrams and pie charts.

What you may not know is that there is an undocumented “clock” chart type. It’s used by Google when you do time zone queries, such as time in ithaca, ny. The little icon that appears next to the result actually displays the correct local time; what’s more, the clock border is silver for night and gold for day. Here’s the URL of the image:

http://www.google.com/chart?chs=40×30&chc=localtime&cht=cf&chd=s:Wp&sig=-r0X-_m3bpzbNNulLXfJyr7SDTI

(Pay no attention to the fact that the URL is expired and go grab yourself a fresh one with a query like the one above.)

The cht (chart type) parameter is cf, likely short for “clock face.” The data, above Wp, is the encoded time; according to the encoding spec, W is 22 and p is 41, so the time is 10:41 PM. chc is not a documented parameter, and only seems to apply to the clock chart type.

But wait, something’s amiss — if you try to fiddle with the URL above, you’ll notice you can’t change any of the parameters or you get back a 400 Bad Request error. What’s the deal? It looks like Google has added a cryptographic signature in the sig parameter; if you change the URL string, you invalidate the signature and Google refuses to generate the clock.

Fear not, you can still generate your own clocks — just drop the sig and chc parameters and everything will work just fine. Note that the clock image is just a 40×30 bitmap; you can’t scale it or change the background.


Mail.app: IMAP Folders not updating?

January 2nd, 2008 by Jonathan Johnson

Ever since switching to Leopard, Mail.app sometimes doesn’t update folders with new messages, even if the messages added to those folders are a result of rules in Mail.app. I searched, and found partial solutions to my liking, but here’s what I settled on.

In Script Editor, save this script somewhere:

on run
  tell application "System Events"
    if exists (application processes whose name is "Mail") then
      tell application "Mail"
        set everyIMAPAccount to every imap account
        repeat with eachIMAPAccount in everyIMAPAccount
          tell eachIMAPAccount
            synchronize with eachIMAPAccount
          end tell
        end repeat
      end tell
    end if
  end tell
end run

Next, add a new rule to Mail.app that has the condition “Any Message,” and the action “Run Applescript” using this AppleScript. All this script does is automatically run “Synchronize” on each account any time a new message is found. This ensures that all folders are kept up to date.

I’ve been using this solution for a week now, and it’s been a godsend.

Edit:

See comments for a much simpler script.


Objective-C 2.0 Property Encoding

December 4th, 2007 by Joe Ranieri

The only way to inspect an Objective C 2.0 property is with a call to property_getAttributes(); sadly, the data returned by this function is completely undocumented. Here’s what I figured out:

The data is simply a NULL terminated string. Inside this string are several parts, separated by commas. (I’m assuming the order doesn’t matter, but this could be incorrect.)

Misc:

  • R - the property is read-only
  • T<type> - the property’s type, where <type> is the result of @encode() of the property’s type.

Assignment modes:

  • C - the property is copied on assignment
  • & - the property is retained on assignment

References:

  • P - the property is a strong reference
  • W - the property is a weak reference

Property accessors:

  • D - the property is dynamic
  • G<getter> - the property’s getter (named <getter>)
  • S<setter> - the property’s setter (named <setter>)
  • V<ivar> - the property is synthesized, using the ivar <ivar>

Concatenating Strings in a REALbasic Plugin

November 22nd, 2007 by Joe Ranieri

Concatenating strings in REALbasic itself is a very simple operation:

dim str as string = "foo" + "bar"

However, the plugins SDK provides no function for concatenating REALstrings together. There’s also no function we can load through REALLoadObjectMethod or REALLoadGlobalMethod. While it would be nice if it was provided, it’s rather easy to do yourself.

//! Creates a new string by concatenating `firstString` and `secondString`.
REALstring ALConcatStrings(REALstring firstString, REALstring secondString) {
    REALstring convertedFirstString = NULL;
    REALstring convertedSecondString = NULL;
    bool needsUnlock = false;
 
    if(REALGetStringEncoding(firstString) == REALGetStringEncoding(secondString)) {
        // encodings are equal, so no need to convert anything
        convertedFirstString = firstString;
        convertedSecondString = secondString;
    } else {
        // if the two strings aren't in the same encoding, we need to change them both to UTF8
        convertedFirstString = REALConvertString(firstString, kREALTextEncodingUTF8);
        convertedSecondString = REALConvertString(secondString, kREALTextEncodingUTF8);
        needsUnlock = true;
    }
 
    // the total length (in bytes) of the resulting string
    size_t totalLength = convertedFirstString->Length() + convertedSecondString->Length();
 
    // ok, now we need to create a buffer for the total string contents
    char *buffer = (char *)malloc(totalLength);
 
    // copy in the first and second strings' data to the right spots
    memcpy(buffer, convertedFirstString->CString(), convertedFirstString->Length());
    memcpy(buffer + convertedFirstString->Length(), convertedSecondString->CString(), convertedSecondString->Length());
 
    // next we create a REALstring from it
    REALstring result =  REALBuildString(buffer, totalLength, REALGetStringEncoding(convertedFirstString));
 
    // free our buffer (REALbasic copies it on BuildString)
    free(buffer);
 
    // unlock our converted strings if we have to
    if(needsUnlock) {
        REALUnlockString(convertedFirstString);
        REALUnlockString(convertedSecondString);
    }
 
    // phew, that was easy enough!
    return result;
}

I apologize for the awful formatting of this page! It looks decent in an RSS reader at least…


C++ instance method addresses

November 16th, 2007 by Joe Ranieri

Sorry if this looks like a repeat; it is a rewrite of a post on our old blog. It may differ from the original content.

C++ doesn’t let you get the address of an instance method. Even if you were to get the address, you couldn’t call it because the convention requires that ‘this’ be in the ecx register. However, with a little creativity we can work around this requirement.

(Please do not stare directly at the following code block until we manage to come up with a new color scheme for GeSHi!)

#include &lt;stdarg.h&gt;
 
void* GetInstanceMethodAddr(char x, ...) {
	va_list argp;
	void *addr;
 
	va_start(argp, x);
	addr = va_arg(argp, void *);
	va_end(argp);
 
	return addr;
}
 
// For example,
void *func = GetInstanceMethodAddr(42, &std::string::size);

The first parameter is arbitrary, and is only used because it’s required by varargs. You can change it as necessary.