Reputation: 13698
I'm trying to execute an apple script from my C++ application but I still no have success to make it stable. My first approach was with Carbon API:
::OSACompileExecute(Component, &ScriptTextDesc, kOSANullScript, kOSAModeNull, &ResultID);
Where I created component every script execution and release it in the same scope. But I stuck with the problem: sometimes it crashes when trying to release Component. So it seems that some problem is occured in the script engine and it corrupts Component somehow. I didn't find any soultions so I switch to another but similar approach:::OSACompileExecute(m_Component, &ScriptTextDesc, kOSANullScript, kOSAModeNull, &ResultID);
Where m_Component is the ComponentInstance which is created in ctor and lives during the application execution. So the crashes on releasing Component is gone due to the absence that release step while application is working. But there is another problem: when user tries to log out then pop-up is shown which says "MyApp prevents log out"(something like that) and MyApp closes. So when I close this pop-up I can log out. So it seems that the problem with Component releasing do something that tells System that it can't be closed. I didn't find any solution here so I decided to sitch to the 3rd approach:Cocoa - NSAppleScript The solution is very simple and small:
QString ExecuteAppleScript(const QString& strScriptText)
{
QString strResult;
NSAutoreleasePool* Pool = [[NSAutoreleasePool alloc] init];
NSString* ScriptText = [[NSString alloc] initWithBytes:strScriptText.toUtf8().data() length:strScriptText.size()
encoding:NSUTF8StringEncoding];
NSAppleScript* Script = [[NSAppleScript alloc] initWithSource:ScriptText];
if (Script)
{
NSDictionary* ErrorInfo = nil;
NSAppleEventDescriptor* Result = [Script executeAndReturnError:&ErrorInfo];
if (Result)
{
strResult = QString::fromUtf8([[Result stringValue] UTF8String]);
}
else if (ErrorInfo)
{
QString strErrorInfo = QString::fromUtf8([[ErrorInfo description] UTF8String]);
}
}
[ScriptText release];
[Pool release];
return strResult;
}
There are a bit of Qt classes here but they are not relevant. So it works but it crashes with different places inside the NSAppleScript guts, one of the logs:
Thread 5 Crashed:
0 com.apple.applescript 0x000000010ff0a4c9 ASCoerceToDesc(unsigned int, unsigned int, int, AEDesc*) + 84
1 ...ple.CoreServices.CarbonCore 0x00007fff86545e48 CallComponentFunction + 28
2 com.apple.applescript 0x000000010ff05cb1 AppleScriptComponent + 1728
3 com.apple.applescript 0x000000010ff1ebd0 AGenericCall::Delegate(ComponentInstanceRecord*) + 46
4 com.apple.applescript 0x000000010ff1e524 AGenericManager::HandleOSACall(ComponentParameters*) + 54
5 com.apple.applescript 0x000000010ff1e4b4 GenericComponent + 219
6 com.apple.openscripting 0x00007fff8716ae34 OSACoerceToDesc + 65
7 com.apple.Foundation 0x00007fff87c61a83 -[NSAppleScript(NSPrivate) _executeWithMode:andReturnError:] + 170
So for now I don't know what else I can do to overcome those AppleScript crashes...
Additional info:
Script is(just took it from the C++ code):
do shell script \"ioreg -c IOHIDSystem" "| perl -ane 'if (/Idle/) {$idle=(pop @F)/1000000000; print $idle,\\"\\";last}'\"
Could anyone help me with it? Thank you in advance!
Upvotes: 1
Views: 960
Reputation: 19032
You mention that you're running NSApplescript from a different thread. If you look in the documentation for NSApplescript, right at the top it says this:
Important: You should access NSAppleScript only from the main thread.
So just know that it's not thread safe and that's probably the reason for your problems.
EDIT: I see a couple other issues in your code. First, you are not releasing "Script" which you have created using alloc/init. Next, and this is the main issue... your "ErrorInfo" is a dictionary with key/value pairs. You are not using the keys to get the values and that's where it looks like your crash is occuring. It's having trouble returning the "description" of the dictionary. Here's example code I use to create an NSString of that error dictionary, so do something like this.
NSString* errorInfo = [NSString stringWithFormat:@"Error:%@ %@", [errorDict valueForKey:@"NSAppleScriptErrorNumber"], [errorDict valueForKey:@"NSAppleScriptErrorMessage"]];
Finally, are you using some method like "performSelectorOnMainThread" to ensure it's run on the main thread?
Upvotes: 1