【问题标题】:OBJ-C Scripting Bridge - Trouble creating scripting object with properties (Microsoft Word)OBJ-C 脚本桥 - 使用属性创建脚本对象时遇到问题 (Microsoft Word)
【发布时间】:2013-08-26 21:47:55
【问题描述】:

我一直用这个把头撞在墙上。我正在尝试使用脚本桥在 MS Word 中创建一个新的自动文本条目。

这是我尝试使用的代码:

wordApplication *theWordApp = [SBApplication applicationWithBundleIdentifier:@"com.microsoft.Word"];
wordTemplate *theWordTemplate = [[theWordApp activeDocument] attachedTemplate];
wordAutoTextEntry *theNewAutoTextEntry = [[[theWordApp classForScriptingClass:@"auto text entry"] alloc] init];
[[theWordTemplate autoTextEntries] addObject:theNewAutoTextEntry];
[theNewAutoTextEntry setName:@"test name"];
NSLog(@"%@", [theNewAutoTextEntry name]);

使用这个,我得到以下错误:

*** -[SBProxyByClass setName:]: object has not been added to a container yet; selector not recognized [self = 0x6d9fbd0]

我也试过了:

wordApplication *theWordApp = [SBApplication applicationWithBundleIdentifier:@"com.microsoft.Word"];
wordTemplate *theWordTemplate = [[theWordApp activeDocument] attachedTemplate];
wordAutoTextEntry *theNewAutoTextEntry = [[[theWordApp classForScriptingClass:@"auto text entry"] alloc] initWithProperties:[NSDictionary dictionaryWithObjectsAndKeys:@"testname", @"test", nil]];
NSLog(@"%@", [theNewAutoTextEntry name]);

这样我得到了同样的错误。

有趣的是,当我运行以下命令时:

wordApplication *theWordApp = [SBApplication applicationWithBundleIdentifier:@"com.microsoft.Word"];
wordTemplate *theWordTemplate = [[theWordApp activeDocument] attachedTemplate];
wordAutoTextEntry *theNewAutoTextEntry = [[[theWordApp classForScriptingClass:@"auto text entry"] alloc] init];
NSLog(@"%@", theNewAutoTextEntry);

我得到这个输出:

<future MicrosoftWordAutoTextEntry with properties (null)>

自动文本条目指示为“未来”。有任何想法吗?提前致谢!

【问题讨论】:

    标签: objective-c cocoa scripting applescript scripting-bridge


    【解决方案1】:

    您在正确的轨道上,但似乎没有从其数组中获取对象,这是使用它所必需的。最好用 name 属性初始化对象,以确保轻松检索。也就是说……

        word011 *theWordApp = [SBApplication applicationWithBundleIdentifier:@"com.microsoft.Word"];
        word2011Template *theWordTemplate = [[theWordApp activeDocument] attachedTemplate];
    
        // create the object with them name property to grab it later
        NSString *testName = @"test name";
        word2011AutoTextEntry *theNewAutoTextEntry = [[[theWordApp classForScriptingClass:@"auto text entry"] alloc] initWithProperties:[NSDictionary dictionaryWithObjectsAndKeys:testName, @"name", nil]];
        [[theWordTemplate autoTextEntries] addObject:theNewAutoTextEntry];
    
        // you created the object, but now you have to actually *get* the object
        // check for existence first other EXC_BAD_ACCESS will happen
        if ( [[[theWordTemplate autoTextEntries] objectWithName:testName] exists] {
            theNewAutoTextEntry = [[theWordTemplate autoTextEntries] objectWithName:testName];
        }
    
        // name already set, so you can move with working with the object
        //[theNewAutoTextEntry setName:@"test name"];
    

    【讨论】:

    • 如果你想证明 SB 在 Word 中创建了 auto text entry 对象,插入一个 if 块来掩盖任何失败可能是不明智的。请参阅我的另一篇文章,其中我证明了您的解决方案不起作用,并准确解释这是如何以及为什么会这样。
    【解决方案2】:

    @Philp Regan:我测试了您的代码 - 令人惊讶的是 - 它不起作用:创建了所需的“自动文本输入”对象。也许如果你没有包含if 块,它会默默地隐藏元素创建中的任何失败,你自己可能已经注意到了。

    这是我从您的原始 ObjC 代码转换为 Python 的测试 - 不幸的是,我无法使用您的 ObjC 代码,因为sdp 在尝试为 Word 2011 创建胶水标题时呕吐(sdp 也像蚁丘一样有缺陷,并在 InDesign 和其他应用程序上搞砸了)。

    #!/usr/bin/python
    
    from ScriptingBridge import *
    
    theWordApp = SBApplication.applicationWithBundleIdentifier_("com.microsoft.Word")
    
    theWordTemplate = theWordApp.activeDocument().attachedTemplate()
    
    testName = "test name 99"
    
    theNewAutoTextEntry = theWordApp.classForScriptingClass_("auto text entry").alloc().initWithProperties_(NSDictionary.dictionaryWithObjectsAndKeys_(testName, "name", None))
    
    theWordTemplate.autoTextEntries().addObject_(theNewAutoTextEntry)
    
    print theWordTemplate.autoTextEntries().objectWithName_(testName).exists() # prints: False (!)
    
    print theWordApp.lastError() # prints: None
    

    如您所见,auto text entry 元素是创建的;此外,SB 静默失败,让您不知道为什么操作不起作用。

    (如果您愿意,我很乐意使用您自己创建的 Word.h 文件重试您的原始 ObjC 代码,但结果将完全相同。)

    而且只是为了证明这是 SB 的错,而不是其他的错,这里是 Python appscript 中的等效代码,经过调整只是为了为 make 命令的 at 参数提供正确的参考形式:

    #!/usr/local/bin/python3
    
    from appscript import *
    
    theWordApp = app(id="com.microsoft.Word")
    
    theWordTemplate = theWordApp.active_document.attached_template
    
    testName = "test name 101"
    
    theNewAutoTextEntry = theWordApp.make(new=k.auto_text_entry, at=theWordTemplate, with_properties={k.name: testName})
    
    print (theWordTemplate.auto_text_entries[testName].exists()) # prints: True
    

    ...

    再一次:Scripting Bridge 和 sdp 不能正常工作;从来没有,永远不会。除了常见的错误和遗漏之外,SB 的设计存在根本缺陷:作为一个 ORM,它试图将 Cocoa 狭窄的 OO 语义强加于 Apple 事件的广泛 RPC + 关系查询语义,从而在两者之间造成严重的阻抗不匹配。 (这是 80/20/80 问题:80% 的时间它有效,但 20% 的时间它不起作用,80% 的时间你不知道为什么。)有很多 SB 失败的例子由于它对应用程序脚本的实际工作方式做出的所有有缺陷或不正确的假设,在 AppleScript 和 appscript 中完美运行的命令。

    一些引用和命令甚至根本无法构造 - 例如,尝试将以下内容转换为 SB:

    tell application "Finder"
        get name of every file of entire contents of desktop
    end tell
    
    tell application "TextEdit"
        tell document 1
           duplicate paragraph 1 to after paragraph 2
        end tell
    end tell
    

    第一个示例将无法编译,因为 SB 无法构造 every file of entire contents 部分,因为它只能引用具体对象(文件、文件夹、磁盘等)的属性和元素 - 以及 entire contents 属性持有一个引用(对象说明符),而不是一个具体的对象。

    第二个将失败,因为它无法构造 after paragraph 2 部分:负责 SB 的 Apple 开发人员只是忘记实现适当的 before/after/beginning/end 构造所需的方法插入位置参考!

    OP 的 Word 示例特别具有讽刺意味,因为根本问题在 SB 发布后几天内就报告给了 Apple:SB 的 -[NSElementArray addObject:] 只知道如何构造 make new &lt;element&gt; at end of &lt;elements&gt; ... 形式的 make 事件。但是,在 Apple 活动世界中,以下形式也是完全合法的:make new &lt;element&gt; at &lt;element&gt; ...make new &lt;element&gt; at &lt;property&gt; ...make new &lt;element&gt; at &lt;elements&gt; ...make new &lt;element&gt; at end of &lt;property&gt; ...,可能还有其他一些形式。

    特定应用程序可以理解哪些形式,哪些不可以由应用程序本身决定。脚本接口基于 Cocoa Scripting 构建的应用程序都理解make new &lt;element&gt; at end of &lt;elements&gt; ... 形式;但是,其脚本接口直接构建在 Carbon Apple 事件管理器 API 或各种定制或第三方 C/CC+ 框架上的应用程序通常差异很大。

    例如,Finder、iTunes、Illustrator 和 Word 等基于 Carbon 的应用程序通常要求您输入 make new &lt;element&gt; at &lt;elements&gt; ...,但 SB 不允许您构建这些表单。如果您尝试使用 SB 的 -[NSElementArray addObject:],应用程序会抛出错误,因为它不理解 make new &lt;element&gt; at end of &lt;elements&gt; ... 表单。 (然后 SB 会默默地消除该错误,因为它的错误报告也很糟糕。)

    负责为这些应用程序的make 处理程序构建正确参考表单的 SB even admitted it was incapable 的 Apple 开发人员。他的“解决方案”是破解一次性变通办法,导致-addObject: 在专门与 Finder 和 iTunes 交谈时组装make new &lt;element&gt; at &lt;elements&gt; ... 事件。不用说,这并没有解决根本问题,所以六年和四个主要操作系统版本之后,其他应用程序继续打破这个任何其他愚蠢的 SB 设计缺陷。

    ...

    这些甚至不是困难或晦涩的用例 - 它们是基本的 Apple Events 101 内容。所以苹果真的没有理由把 SB 搞到这种程度:尤其是当他们可以开放访问所有内部 AppleScript 实现和文档时,更不用说至少六个第三方背后的所有代码和知识从优秀到糟糕的桥梁,从中学习 Apple 活动编程的所有注意事项。正如我在其他地方所说,这已经是一个已解决的问题:SB 再次完全解决它。

    SB 根本无法说出 Apple 事件,因为它们实际上是由 真实用户 依赖的真实应用程序真实世界中说出的做真正的工作。真正的应用程序脚本通常是混乱的、不一致的、被模糊或错误的字典所困扰,并且严重缺乏硬性规范、健壮、称职的框架或值得该死的开发人员或用户文档。但是,SB 没有接受这个令人不快的现实并为用户提供有时不吸引人但能胜任的工具来可靠地处理它,而是决定将整个烂摊子扫到 ORM 地毯下,假装它不存在。

    对于那些仅将 SB 用于琐碎应用程序的琐碎工作的用户来说,这可能没问题,因此永远不要将其推到足够的程度,以使其无数的缺陷和缺点跳出来咬他们。但对于我们这些从事真正专业自动化工作的人来说,SB 只是一个糟糕的笑话,它比 Apple 什么都不做更能挫败 AppleScript 世界。

    【讨论】:

    • 哇,感谢您的彻底回复。绝对可以说明一些事情!我几乎已经接受了这样一个事实,即我不会让这个特定的代码脱离实际。呃,好吧。继续下一个!
    【解决方案3】:

    SB 无法正常工作;从来没有,永远不会。它特别容易与 Word 等基于 Carbon 的应用程序出现兼容性问题,这些应用程序在构建引用和命令的方式上具有更大的可变性,尽管即使 Cocoa 应用程序也会给它 gyp。

    以下是您在 AppleScript 中的操作方法,它可以完美运行:

    tell application "Microsoft Word"
        make new auto text entry at attached template of active document ¬
            with properties {name:"test name", auto text value:"test value"}
    end tell
    

    但是,将其转换为 SB 代码只会在下一行产生“选择器无法识别”错误,因为 SB 无法构造所需的参考表单:

    [theWordTemplate addObject:theNewAutoTextEntry];
    

    假设您的目标是 10.6 或更高版本,请忘记 SB 并改用 AppleScript-ObjC 桥。 AppleScript 是唯一仍受支持的解决方案,它知道如何正确处理 Apple 事件。 ASOC 使 AppleScript 脚本对象对应用程序的其余部分显示为普通的 Cocoa 类和实例,从而允许您的 ObjC 代码直接与 AppleScript 对话,反之亦然,从而避免 SB 缺陷和 NSAppleScript 苦差事。

    查看这些帖子了解更多信息:

    Pass Variable or String from OS X Cocoa App to Applescript

    Execute AppleScript file in Mac app?

    ...

    我得到这个输出:[...]自动文本条目被指示为“未来”。有什么想法吗?

    这只是 SB 的伪 OO 伪造外泄。忽略它;它只是令人困惑,无论如何与您的问题无关。

    【讨论】:

    • 感谢您为我省去继续搞砸这个的麻烦。
    • 投反对票,因为 SB 有效,但您必须遵守其规则并阅读头文件以了解随之而来的限制。 AppleScript-ObjC 有它自己的(可以说和脚本桥一样糟糕的)陷阱;两种技术都不是完美的。例如,[theWordTemplate addObject:theNewAutoTextEntry]; 行将给出错误,因为这根本不是在 Scripting Bridge 中创建对象的方式,因此将 OP 引导到其他地方是不正确的。
    • @Philip:我不在乎 SB相信 应用程序对象应该如何创建,Word 也不。请参阅我的另一篇文章,其中我证明了您的解决方案不起作用,并准确解释这是如何以及为什么会这样。
    • 至于 ASOC,我并不认为它是完美的,只是说它是目前可用的最不可怕的支持选项。 ASOC 的 ASObjC 类型和方法映射的缺点不难识别和解决,作为回报,您可以直接访问一流的 Apple 事件桥。 SB 的问题被严重混淆,而且通常无能为力;作为一个 AE 桥,它从根本上是有缺陷的,而且非常不适合任何琐碎的任务。 (唯一的其他选择是 appscript - 也是一个出色的 AE 桥,但由于 Apple 遗留了 Carbon API,因此不再支持。)
    • 我承认我的代码没有经过全面测试,并确认我无法为 Word 制作完整的标题。在 Word 的情况下将 OP 重定向到另一种技术,好吧。但是发布不正确的一揽子声明会遭到反对。我仍然支持我的批评,即“Scripting Bridge 和 sdp 不能正常工作;永远不会,永远不会”的绝对陈述既具有误导性又是错误的。我已经开发(并且正在开发)使用它的应用程序。我能够从整个 Adob​​e Creative Suite 和 Applescript 现在失败的移植代码中获取正确的头文件。 -1。对不起。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-28
    • 2015-03-24
    • 1970-01-01
    • 2015-04-08
    相关资源
    最近更新 更多