views:

4182

answers:

2

I'm working on modifying the PTHotKeyLib to be 64-bit friendly, but I've run into an issue in the code that I'm not sure how to get around. In PTHotKeyCenter, the registerHotKey method create an EventHotKeyID instance and then stuffs the of PTHotKey object into the id attribute. The original code used a long. I converted it to NSInteger per Apple's 64 bit programming guide.

  - (BOOL)registerHotKey:(PTHotKey *)theHotKey {
  OSStatus error;
  EventHotKeyID hotKeyID;
  EventHotKeyRef carbonHotKey;
  NSValue *key = nil;

  if ([[self allHotKeys] containsObject:theHotKey])
    [self unregisterHotKey:theHotKey];

  if (![[theHotKey keyCombo] isValidHotKeyCombo])
    return YES;

  hotKeyID.signature = kHotKeySignature;
  hotKeyID.id = (NSInteger)theHotKey;
  ... //Rest is not relevant
  }

When a user triggers the hot key, it calls the sendCarbonEvent: method that will try to pull the PTHotKey instance out of EventHotKeyID. It worked in 32-bit land, but when compiling against 64-bit, it gives a "cast to pointer from integer of different size" warning

- (OSStatus)sendCarbonEvent:(EventRef)event {
  OSStatus error;
  EventHotKeyID hotKeyID;
  SGHotKey *hotKey;

  NSAssert(GetEventClass(event) == kEventClassKeyboard, @"Unknown event class");

  error = GetEventParameter(event,
                            kEventParamDirectObject, 
                            typeEventHotKeyID,
                            nil,
                            sizeof(EventHotKeyID),
                            nil,
                            &hotKeyID);
  if (error)
    return error;

  NSAssert(hotKeyID.signature == kHotKeySignature, @"Invalid hot key id" );
  NSAssert(hotKeyID.id != 0, @"Invalid hot key id");

  hotKey = (SGHotKey *)hotKeyID.id; // warning: cast to pointer from integer of different size
// Omitting the rest of the code
}

Switching from x86_64 back to i386 removes the warning and everything compiled and runs properly. Under x86_64 it causes a crasher, and I'm not sure how to get around that issue. Any suggestions on how to resolve it?

+3  A: 

Casting between pointers and integers is not recommended because it results in non-portable code.

What you are doing results in what C99 defines as "undefined behavior". This basically means it might work, and it might not. The C language is famous for letting you do things like this because it implicitly assumes that you knew what you were doing when you typed it in and that you have the wizardly 37337 mad skillz and years of experience to know when it's safe to do something like this and when it's not.

It's safe to say this this is one of those times when it was not safe to do.

The problem, from context, is probably due to casting a 64-bit pointer in to an int sized variable. Mac OS X 64-bit ABI is what is known as LP64, which means that longs and pointers are 64-bits wide, and that ints are 32-bits wide. So, in short, during one of your pointer to int and back again castings, you chopped off the top 32 bits, which just so happened to be really important.

Looking at the docs, the type for id is UInt32. So, the short answer is you can't put 64-bit pointers in to 32-bit sized integers.

johne
+1  A: 

The classic answer is you make a dictionary or something, and put the key to the dictionary in the id, and the pointer in the value, thus avoiding having the actual pointer in the 32bit id.

But have you solved it yet?

Chris
Putting a pointer into a variable too small to hold a pointer is not going to work, no matter what the pointer points to.
Peter Hosey