对于目标C,可以在这里找到答案:通过注册的URL方案启动OSX应用程序时,如何访问完整的URL?
Java的解决方案是将ObjC代码重写为纯C,然后借助下的一组类将其转换为Java
org.eclipse.swt.internal.cocoa.*。
作为ObjC到C转换的参考,我们需要Apple的Objective-
C运行时参考。
普通C版本
首先,让我们翻译
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:targetObject andSelector:@selector(handleAppleEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
到普通C中。要在普通C中调用ObjC函数,我们使用
objc_msgSend()。此外,
@selector(method_footprint)用代替,用
sel_registerName("method_footprint")查找类objc_getClass()。类型
id和
SEL等效于指针(例如void
)或完整大小的int(即与void 相同的大小)。
结果:
// id mgr = [NSAppleEventManager sharedAppleEventManager]SEL sel_ sharedAppleEventManager = sel_registerName("sharedAppleEventManager");id mgr = objc_msgSend (objc_getClass("NSAppleEventManager"), sharedAppleEventManager);// and the restSEL sel_setEventHandler = sel_registerName("setEventHandler:andSelector:forEventClass:andEventID:");SEL sel_handleAppleEvent = sel_registerName("handleAppleEvent:withReplyEvent:");objc_msgSend (mgr, sel_setEventHandler, targetObject, sel_handleAppleEvent, kInternetEventClass, kAEGetURL);如您所见,我们在这里有两个子例程调用:第一个调用该类的
sharedAppleEventManager消息,从
NSAppleEventManager该类中检索一个单例对象。第二个调用是将setEventHandler
…消息发送到该对象,并传递4个参数(目标对象,目标消息和两个事件说明符)。
回调函数的声明,最初是:
- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
在纯C中看起来像这样:
void handleAppleEvent (id self, SEL selector, NSAppleEventDescriptor* event, NSAppleEventDescriptor* replyEvent)
这意味着调用该函数时,不仅会发送其对象引用(id),而且还会发送其方法足迹(选择器)。
在ObjC中,回调代码如下所示以获取URL:
NSString url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
在纯C中:
id desc_id = objc_msgSend (event_id, sel_registerName("paramDescriptorForKeyword:"), '----');id url_id = objc_msgSend (desc_id, desc_id, sel_registerName("stringValue"));仍然缺少一部分:
targetObject需要在调用上面的代码之前进行初始化,并且
handleAppleEvent:withReplyEvent:需要在该目标对象中创建与足迹匹配的方法,然后将其链接到我们的普通C事件处理程序(
handleAppleEvent())。
这意味着我们必须创建一个Objective C类,向其添加一个方法,然后为其创建一个对象实例:
// create an NSObject subclass for our target objectchar objcClassName[] = "ObjCAppleEventHandler";id objcClass = objc_allocateClassPair (objc_getClass("NSObject"), objcClassName);// add the callback method to the classSEL sel_handleAppleEvent = sel_registerName("handleAppleEvent:withReplyEvent:");class_addMethod (objcClass, sel_handleAppleEvent, handleAppleEvent, "i@:@@");// register the classobjc_registerClassPair (objcClass)// create an object instanceid targetObject = class_createInstance (objcClass, 0);// ... here follows the above pre with the setEventHandler invocation// (note: `SEL sel_handleAppleEvent` appears twice - the 2nd one can be removed)到此结束纯C版本。
(注意:上面的代码未经测试就编写,因此可能包含错误。但是,下面的Java代码已经过测试。)
Java版本
从Plain C到Java的转换现在非常简单。
前面提到的ObjC Runtime函数都可以从org.eclipse.swt.internal.cocoa.OS获得。
首先,一些预设:
static final long class_NSAppleEventManager = OS.objc_getClass("NSAppleEventManager");static final long sel_sharedAppleEventManager = OS.sel_registerName("sharedAppleEventManager");static final long sel_setEventHandler = OS.sel_registerName("setEventHandler:andSelector:forEventClass:andEventID:");static final long sel_handleAppleEvent = OS.sel_registerName("handleAppleEvent:withReplyEvent:");static final long sel_paramDescriptorForKeyword = OS.sel_registerName("paramDescriptorForKeyword:");static final long sel_stringValue = OS.sel_registerName("stringValue");static final long kInternetEventClass = 0x4755524C; // 'GURL'static final long kAEGetURL = 0x4755524C; // 'GURL'static final long kCoreEventClass = 0x61657674; // 'aevt'static final long kAEOpenApplication = 0x6F617070; // 'oapp'static final long kAEReopenApplication = 0x72617070; // 'rapp'static final long keyDirectObject = 0x2d2d2d2d; // '----'回调函数:
static long handleAppleEvent (long id, long sel, long event_id, long reply_id) { // This is a handler for AppleEvents that are registered with [NSAppleEventManager setEventHandler:...] // It matches this selector (footprint): // - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply // Invoke [[event paramDescriptorForKeyword:keyDirectObject] stringValue] to get the direct object containing the URL long direct_desc_id = OS.objc_msgSend (event_id, sel_paramDescriptorForKeyword, keyDirectObject); long direct_str_id = OS.objc_msgSend (direct_desc_id, sel_stringValue); NSString nsStr = new NSString (direct_str_id); String str = nsStr.getString(); // now 'str' contains the URL System.out.println ("handleAppleEvent invoked -- argument: "+url); return 0;}和注册回调函数的代码:
// Get access to a callback function for receiving the sel_handleAppleEvent messageaeCallback = new Callback(Main.class, "handleAppleEvent", 4);long aeProc = aeCallback.getAddress();// Create a ObjC class that provides a method with the sel_handleAppleEvent footprintString objcClassName = "ObjCAppleEventHandler";long objcClass = OS.objc_allocateClassPair(OS.class_NSObject, objcClassName, 0);OS.class_addMethod(objcClass, sel_handleAppleEvent, aeProc, "i@:@@");OS.objc_registerClassPair(objcClass);long objcHandlerInstance = OS.class_createInstance (objcClass, 0);// Invoke [[NSAppleEventManager sharedAppleEventManager] setEventHandler:objcHandlerInstance andSelector:sel_handleAppleEvent forEventClass:kInternetEventClass andEventID:kAEGetURL]long sharedAppleEventMgr = OS.objc_msgSend (class_NSAppleEventManager, sel_sharedAppleEventManager);OS.objc_msgSend (sharedAppleEventMgr, sel_setEventHandler, objcHandlerInstance, sel_handleAppleEvent, kInternetEventClass, kAEGetURL);
剩下要做的就是根据此代码构建应用程序捆绑包,然后将CFBundleURLTypes条目添加到其Info.plist中。
完整的示例源文件可以在这里下载:http : //files.tempel.org/Various/ObjectiveC-
bridging.java.zip



