سؤال

I've been banging my head against the wall with this one. I'm trying to create a new autotext entry in MS Word using scripting bridge.

Here is the code I am trying to use:

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]);

Using this, I get the following error:

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

I have also tried:

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]);

I get the same error this way.

Something interesting is that when I run the following:

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

I get this output:

<future MicrosoftWordAutoTextEntry with properties (null)>

Where the auto text entry is indicated as "future". Any ideas? Thanks in advance!

هل كانت مفيدة؟

المحلول 3

SB doesn't work properly; never has, never will. It's particularly prone to compatibility problems with Carbon-based apps like Word which have greater variability in how they construct references and commands, though even Cocoa apps will give it gyp.

Here is how you do it in AppleScript, where it works perfectly:

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

However, translating that to SB code just produces a 'selector not recognized' error on the following line, since SB is incapable of constructing the required reference form:

[theWordTemplate addObject:theNewAutoTextEntry];

Assuming you're targeting 10.6 or later, forget about SB and use the AppleScript-ObjC bridge instead. AppleScript is the only still-supported solution that knows how to talk Apple events correctly. ASOC makes AppleScript script objects appear as ordinary Cocoa classes and instances to the rest of your application, allowing your ObjC code to talk directly to AppleScript and vice-versa, thereby avoiding both SB defects and NSAppleScript drudgery.

See these posts for more:

Pass Variable or String from OS X Cocoa App to Applescript

Execute AppleScript file in Mac app?

...

I get this output: [...] Where the auto text entry is indicated as "future". Any ideas?

That's just SB's pseudo-OO fakery leaking out. Ignore it; it just confuses and is irrelevant to your problem anyhow.

نصائح أخرى

You were on the right track, but rather don't appear to be getting the object from its array, which is required to work with it. Best to init the object with the name property to ensure easy retrieval. To wit...

    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"];

@Philp Regan: I tested your code and - surprise - it does not work: the desired 'auto text entry' object is not created. Perhaps if you hadn't included the if block that silently hides any failures in element creation, you might have noticed this yourself.

Here's my test translated from your original ObjC code to Python - unfortunately, I was unable to use your ObjC code because sdp puked while trying to create a glue header for Word 2011 (sdp is buggy as anthills too, and screws up on InDesign and other applications as well).

#!/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

As you can see, the auto text entry element is not created; furthermore, SB fails silently, giving you no idea why the operation didn't work.

(I'll be happy to retry your original ObjC code with a Word.h file of your own creation should you wish, but it's going to have exactly the same outcome.)

And just to prove this is SB's fault, not anything else's, here is the equivalent code in Python appscript, tweaked only to provide the correct reference form for the make command's at parameter:

#!/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

...

Once again: Scripting Bridge and sdp do not work properly; never have, never will. In addition to the usual bugs and omissions, SB's design is fundamentally flawed: being an ORM, it tries to impose Cocoa's narrow OO semantics on Apple events' broad RPC + relational query semantics, creating a serious impedance mismatch between the two. (It's the 80/20/80 problem: 80% of the time it works but 20% of the time it doesn't and 80% of the time you'll have no clue why.) There are numerous examples of SB failing on commands that work perfectly in AppleScript and appscript due to all the flawed or incorrect assumptions it makes about how application scripting actually works.

Some references and commands can't even be constructed at all - for example, try converting the following to 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

The first example will fail to compile because SB can't construct the every file of entire contents portion because it can only refer to properties and elements of a concrete object (file, folder, disk, etc) - and the entire contents property holds a reference (object specifier), not a concrete object.

The second will fail because it can't construct the after paragraph 2 portion: The Apple developers responsible for SB simply forgot to implement the appropriate before/after/beginning/end methods needed to construct insertion location references!

The OP's Word example is particularly ironic because the underlying problem was reported to Apple within days of SB's release: SB's -[NSElementArray addObject:] only knows how to construct make events of form make new <element> at end of <elements> .... However, in the Apple events world, the following forms are also completely legitimate: make new <element> at <element> ..., make new <element> at <property> ..., make new <element> at <elements> ..., make new <element> at end of <property> ..., and probably a few others.

Which form(s) a particular application will understand and which it won't is for the application itself to decide. Applications whose scripting interfaces are built on Cocoa Scripting all understand the make new <element> at end of <elements> ... form; however, applications whose scripting interfaces are built directly on the Carbon Apple Event Manager APIs or various bespoke or third-party C/CC+ frameworks often vary greatly.

For example, Carbon-based apps like Finder, iTunes, Illustrator and Word often require you to say make new <element> at <elements> ..., but SB does not allow you to construct those forms. And if you try to use SB's -[NSElementArray addObject:], the application throws an error because it doesn't understand the make new <element> at end of <elements> ... form. (And SB then silently squelches that error because its error reporting sucks too.)

The Apple developer responsible for SB even admitted it was incapable of constructing the correct reference form for these applications' make handlers. His "solution" was to hack in one-off workarounds that caused -addObject: to assemble make new <element> at <elements> ... events when talking specifically to Finder and iTunes. Needless to say, that did nothing to address the underlying problem though, so six years and four major OS releases later, other applications continue to break on this any many other stupid SB design flaws.

...

These are not even difficult or obscure use cases - they're basic Apple Events 101 stuff. So Apple really have no excuse for screwing up SB to this extent: especially not when they had open access to all of the internal AppleScript implementation and documentation, not to mention all the code and knowledge behind at least half-a-dozen third-party bridges ranging from excellent to lousy from which to learn all the Do's and Don'ts of Apple event programming. As I've said elsewhere, this was already a solved problem: it took SB to completely unsolve it again.

SB is simply incapable of speaking Apple events as they are actually spoken in the real world by real applications that real users rely on to do real work. Real application scripting is often messy, inconsistent, plagued by vague or buggy dictionaries and a dreadful lack of hard specifications, robust, competent frameworks, or developer or user documentation worth a damn. But instead of accepting this unpleasant reality and providing users with the sometimes unattractive but competent tools to deal with it reliably, SB decided to sweep the whole mess under the ORM rug and pretend it doesn't exist.

Which may be fine for those users who use SB only for trivial work on trivial applications, and so never push it enough for its myriad defects and shortcomings to jump out and bite them. But for those of us who do real professional automation work, SB is just a bad joke that's done more to set back the AppleScript world than if Apple'd done nothing at all.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top