2012-03-18 115 views
1

我想在我的Cocoa項目中使用一些Apple事件來訪問終端應用程序。我不想使用嵌入式AppleScript或任何編譯腳本,所以我開始研究NSAppleEventDescriptor獲取NSAppleEventDescriptor的AppleScript屬性

我已經成功地使用do script命令創建了一個新窗口,並且我成功地從返回值中獲取窗口。我現在想要做的唯一事情就是獲得該窗口的屬性。

我現在已經知道如何獲得這樣的屬性,所以我開始使用Google搜索。不幸的是,沒有很多關於如何使用Apple事件的很好的例子,我沒有找到我想要的東西。

所以我開始深入挖掘。我做的第一件事是在Terminal .sdef文件中尋找get命令的代碼。我沒有找到它,所以我在我的/System/目錄上做了grep,我找到了/System/Library/Frameworks/AppleScriptKit.framework/Versions/A/Resources/AppleScriptKit.sdef。我顯然找到了AppleScript語法的核心。該文件確實有getd四個字符的代碼。

所以現在我知道我必須使用Core Suite中的getd事件。但是,該命令的參數是objectSpecifier。我搜索了一個使用kAEGetData的示例。但是我沒有找到任何可以學習任何東西的代碼。

所以我在這裏問:我該如何建立這樣一個objectSpecifier描述符?

這是我已經有了:

代碼來創建並獲得標籤描述

NSAppleEventDescriptor *createEvent; 
NSAppleEventDescriptor *scriptParam; 
AppleEvent aeReply; 
OSErr err; 

/* Make the do script event */ 
createEvent = [NSAppleEventDescriptor 
       appleEventWithEventClass:kAECoreSuite 
       eventID:kAEDoScript 
       targetDescriptor:_applicationDescriptor 
       returnID:kAutoGenerateReturnID 
       transactionID:kAnyTransactionID]; 
if(createEvent == nil) { 
    NSLog(@"%s Failed to create a do script event", 
      __PRETTY_FUNCTION__); 
    return nil; 
} 

/* Make script parameter */ 
scriptParam = [NSAppleEventDescriptor descriptorWithString:@"echo Hello World"]; 
if(scriptParam == nil) { 
    NSLog(@"%s Failed to create the script parameter", 
      __PRETTY_FUNCTION__); 
    return nil; 
} 

/* Set parameter */ 
[createEvent setParamDescriptor:scriptParam forKeyword:keyDirectObject]; 

/* Send event */ 
err = AESendMessage([createEvent aeDesc], &aeReply, 
        kAEWaitReply | kAENeverInteract, kAEDefaultTimeout); 
if(err != noErr) { 
    NSLog(@"%s Failed to send the create command", 
      __PRETTY_FUNCTION__); 
    return nil; 
} 

/* Retrieve information */ 
{ 
    /* SEE BELOW TO SEE HOW I GET THE WINDOW DESCRIPTOR */ 
} 

現在,我嘗試在獲得該選項卡的窗口成功

// NSAppleEventDescriptor *ansr is set to the result of the code above 

NSAppleEventDescriptor *mainObj; 
NSAppleEventDescriptor *desiredClass; 
NSAppleEventDescriptor *window; 

mainObj = [ansr paramDescriptorForKeyword:keyDirectObject]; 
if([mainObj descriptorType] != typeObjectSpecifier) { 
    NSLog(@"%s Main object was not an object specifier", 
      __PRETTY_FUNCTION__); 
    return nil; 
} 

desiredClass = [mainObj paramDescriptorForKeyword:keyAEDesiredClass]; 
if([desiredClass typeCodeValue] != kAETerminalTab) { 
    NSLog(@"%s Main object's desired class was not a Terminal tab", 
      __PRETTY_FUNCTION__); 
    return nil; 
} 

window = [mainObj paramDescriptorForKeyword:keyAEContainer]; 
if(window == nil) { 
    NSLog(@"%s Couldn't get container of the tab", 
      __PRETTY_FUNCTION__); 
    return nil; 
} 

desiredClass = [window paramDescriptorForKeyword:keyAEDesiredClass]; 
if([desiredClass typeCodeValue] != cWindow) { 
    NSLog(@"%s The container of the tab was not a window", 
      __PRETTY_FUNCTION__); 
    return nil; 
} 

return window; 

現在我失敗getti NG,讓我們說,bounds屬性

// _windowDescriptor is the result of the code above 

NSAppleEventDescriptor *getEvent; 
NSAppleEventDescriptor *prop; 
AppleEvent aeReply; 
NSAppleEventDescriptor *reply; 
FourCharCode propName; 
OSErr err; 

propName = keyAEBounds; 

/* Create get event */ 
getEvent = [NSAppleEventDescriptor 
      appleEventWithEventClass:kAECoreSuite 
      eventID:kAEGetData 
      targetDescriptor:_windowDescriptor 
      returnID:kAutoGenerateReturnID 
      transactionID:kAnyTransactionID]; 
if(getEvent == nil) { 
    NSLog(@"%s Failed to create a get event", 
      __PRETTY_FUNCTION__); 
    return NSZeroRect; 
} 

/* Get property */ 
prop = [NSAppleEventDescriptor 
     descriptorWithDescriptorType:typeProperty 
     bytes:&propName length:sizeof(propName)]; 
if(prop == nil) { 
    NSLog(@"%s Failed to create the bounds property", 
      __PRETTY_FUNCTION__); 
    return; 
} 

/* Set parameter */ 
[getEvent setParamDescriptor:prop forKeyword:keyDirectObject]; 

/* Exectue */ 
err = AESendMessage([getEvent aeDesc], &aeReply, 
        kAEWaitReply | kAENeverInteract, kAEDefaultTimeout); 
if(err != noErr) { 
    NSLog(@"%s Failed to send the get message", 
      __PRETTY_FUNCTION__); 
    return; 
} 

reply = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&aeReply]; 
[reply autorelease]; 

NSLog(@"Bounds: %@", reply); 

如前所述,上面的代碼工作,只是直到最後塊。
非常感謝您的幫助。

回答

2

感謝Rob Keniger,我成功地獲得了我想要的東西。

顯然我必須創建一個記錄描述符,設置我想要的屬性,然後強制它到typeObjectSpecifier

此外,我設置窗口描述符作爲我的Apple事件的接收器是錯誤的。您必須始終處理應用程序本身,並將直接對象的fromkeyAEContainer)屬性設置爲所需的窗口。

這裏是工作的代碼,用NSLog -statements一點點:

- (NSRect)bounds { 

    // ! ! ! 
    // _windowDescriptor is an instance variable which points to a valid 
    // window NSAppleEventDescriptor 
    // ! ! ! 

    NSAppleEventDescriptor *getEvent; 
    NSAppleEventDescriptor *objSpec; 
    NSAppleEventDescriptor *propEnum, *propType, *propSeld; 
    AppleEvent aeReply; 
    NSAppleEventDescriptor *reply; 
    FourCharCode propName; 
    OSErr err; 

    propName = keyAEBounds; 

    /* Create get event */ 
    getEvent = [NSAppleEventDescriptor 
       appleEventWithEventClass:kAECoreSuite 
       eventID:kAEGetData 
       targetDescriptor:[[FTMTerminalApp sharedApp] AEDescriptor] 
       returnID:kAutoGenerateReturnID 
       transactionID:kAnyTransactionID]; 
    if(getEvent == nil) { 
     NSLog(@"%s Failed to create a get event", 
       __PRETTY_FUNCTION__); 
     return NSZeroRect; 
    } 

    /* Get property */ 
    /* create object specifier main ojcect */ 
    objSpec = [[[NSAppleEventDescriptor alloc] initRecordDescriptor] 
       autorelease]; 
    if(objSpec == nil) { 
     NSLog(@"%s Failed to create the object specifier", 
       __PRETTY_FUNCTION__); 
     return NSZeroRect; 
    } 
    NSLog(@"%s Created object specifier %@", 
      __PRETTY_FUNCTION__, objSpec); 

    /* create property enum, we want a property */ 
    propEnum = [NSAppleEventDescriptor 
       descriptorWithEnumCode:formPropertyID]; 
    if(propEnum == nil) { 
     NSLog(@"%s Failed to create the property enum", 
       __PRETTY_FUNCTION__); 
     return NSZeroRect; 
    } 
    NSLog(@"%s Created property enum %@", 
      __PRETTY_FUNCTION__, propEnum); 
    [objSpec setDescriptor:propEnum forKeyword:keyAEKeyForm]; 

    /* create prop type */ 
    propType = [NSAppleEventDescriptor 
       descriptorWithTypeCode:typeProperty]; 
    if(propType == nil) { 
     NSLog(@"%s Failed to create the property type", 
       __PRETTY_FUNCTION__); 
     return NSZeroRect; 
    } 
    NSLog(@"%s Created property type %@", 
      __PRETTY_FUNCTION__, propType); 
    [objSpec setDescriptor:propType forKeyword:keyAEDesiredClass]; 

    /* create property key data */ 
    propSeld = [NSAppleEventDescriptor 
       descriptorWithTypeCode:keyAEBounds]; 
    if(propSeld == nil) { 
     NSLog(@"%s Failed to create the bounds property type", 
       __PRETTY_FUNCTION__); 
     return NSZeroRect; 
    } 
    NSLog(@"%s Created property key data %@", 
      __PRETTY_FUNCTION__, propSeld); 
    [objSpec setDescriptor:propSeld forKeyword:keyAEKeyData]; 

    /* Set destination */ 
    NSLog(@"%s Setting from key %@", 
      __PRETTY_FUNCTION__, _windowDescriptor); 
    [objSpec setDescriptor:_windowDescriptor forKeyword:keyAEContainer]; 

    /* Send message */ 
    objSpec = [objSpec coerceToDescriptorType:typeObjectSpecifier]; 
    NSLog(@"Coerced: %@", objSpec); 
    [getEvent setParamDescriptor:objSpec forKeyword:keyDirectObject]; 
    err = AESendMessage([getEvent aeDesc], &aeReply, 
         kAEWaitReply | kAENeverInteract, kAEDefaultTimeout); 
    if(err != noErr) { 
     NSLog(@"%s Failed to send the message (event = %@)", 
       __PRETTY_FUNCTION__, getEvent); 
     return NSZeroRect; 
    } 

    reply = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&aeReply]; 
    NSLog(@"BOUNDS = %@", reply); 
    [reply autorelease]; 

    return NSZeroRect; 
} 

我希望這會幫助別人。

1

Apple事件使用起來很複雜。除非你真的想花時間處理令人費解且通常令人討厭的API,否則我建議你通過使用Mike Ash的優秀AEVTBuilder課程來節省大量時間和心痛。

這是一個很好的可可包裝所有討厭的碳蘋果事件代碼。

+0

其實我真的很喜歡使用Apple Event API的核心。 – v1Axvw 2012-03-19 15:35:29

+0

太棒了,我很高興能夠提供一點幫助。 – 2012-03-21 20:42:22