我一直在尝试使用最近的NSUserScriptTask类及其子类来解决问题(请参见this和this),到目前为止,我已经解决了一些问题,但仍有一些问题需要解决。正如你从docs中看到的那样,NSUserScriptTask不允许取消任务。因此,我决定创建一个简单的可执行文件,以脚本路径作为参数运行脚本。这样,我就可以使用NSTask从我的主应用程序启动助手,并在必要时调用[task terminate]
。然而,我需要:
- 主应用程序接收来自启动助手的输出和错误
- 启动助手只在NSUserScriptTask完成时终止
// Create task
task = [NSTask new];
[task setLaunchPath: [[NSBundle mainBundle] pathForResource: @"ScriptHelper" ofType: @""]];
[task setArguments: [NSArray arrayWithObjects: scriptPath, nil]];
// Create error pipe
NSPipe* errorPipe = [NSPipe new];
[task setStandardError: errorPipe];
// Create output pipe
NSPipe* outputPipe = [NSPipe new];
[task setStandardOutput: outputPipe];
// Set termination handler
[task setTerminationHandler: ^(NSTask* task){
// Save output
NSFileHandle* outFile = [outputPipe fileHandleForReading];
NSString* output = [[NSString alloc] initWithData: [outFile readDataToEndOfFile] encoding: NSUTF8StringEncoding];
if ([output length]) {
[output writeToFile: outputPath atomically: NO encoding: NSUTF8StringEncoding error: nil];
}
// Log errors
NSFileHandle* errFile = [errorPipe fileHandleForReading];
NSString* error = [[NSString alloc] initWithData: [errFile readDataToEndOfFile] encoding: NSUTF8StringEncoding];
if ([error length]) {
[error writeToFile: errorPath atomically: NO encoding: NSUTF8StringEncoding error: nil];
}
// Do some other stuff after the script finished running <-- IMPORTANT!
}];
// Start task
[task launch];
记住,我需要终止处理程序仅在以下情况下运行:(a)任务被取消 (b)任务由于脚本完成而自行终止。
现在,在帮助者方面,事情开始变得棘手,至少对我来说是这样。为了简单起见,让我们假设脚本是一个AppleScript文件(因此我使用NSUserAppleScriptTask子类 - 在真实世界中,我必须适应三种类型的任务)。以下是我目前所拥有的:
int main(int argc, const char * argv[])
{
@autoreleasepool {
NSString* filePath = [NSString stringWithUTF8String: argv[1]];
__block BOOL done = NO;
NSError* error;
NSUserAppleScriptTask* task = [[NSUserAppleScriptTask alloc] initWithURL: [NSURL fileURLWithPath: filePath] error: &error];
NSLog(@"Task: %@", task); // Prints: "Task: <NSUserAppleScriptTask: 0x1043001f0>" Everything OK
if (error) {
NSLog(@"Error creating task: %@", error); // This is not printed
return 0;
}
NSLog(@"Starting task");
[task executeWithAppleEvent: nil completionHandler: ^(NSAppleEventDescriptor *result, NSError *error) {
NSLog(@"Finished task");
if (error) {
NSLog(@"Error running task: %@", error);
}
done = YES;
}];
// Wait until (done == YES). How??
}
return 0;
}
现在,我有三个问题(这是我想通过这个SO条目提出的问题)。首先,“完成任务”从未被打印出来(该块从未被调用),因为任务甚至没有开始执行。相反,我在控制台上看到了这个:
MessageTracer: msgtracer_vlog_with_keys:377: odd number of keys (domain: com.apple.automation.nsuserscripttask_run, last key: com.apple.message.signature)
我尝试从主应用程序运行完全相同的代码,它完成时没有问题(但是从主应用程序中,我失去了取消脚本的能力)。
其次,我只想在完成处理程序被调用后到达main的结尾(return 0;
)。但我不知道该怎么做。
第三,每当有错误或来自辅助程序的输出时,我希望将这些错误/输出发送回应用程序,应用程序将通过errorPipe/outputPipe接收它们。像fprintf(stderr/stdout, "string")
这样的东西可以解决问题,但我不确定这是否是正确的方法。
因此,简而言之,对于第一和第二个问题的任何帮助都会受到赞赏。第三个问题,我只想确保这就是我应该这样做的方式。
谢谢
~/Library/Application Scripts/com.devname.appname/
。而且应用程序和助手都是沙盒化的,但是 NSUserScriptTask 在应用程序上可以工作,但不幸的是在助手上不能工作。 - Alex