【发布时间】:2013-03-14 23:42:19
【问题描述】:
我一直在尝试使用最近的 NSUserScriptTask 类及其子类(参见 this 和 this),到目前为止,我已经解决了一些问题,但还有一些问题有待解决。从docs 可以看出,NSUserScriptTask 不允许取消任务。因此,我决定创建一个简单的可执行文件,它将脚本路径作为参数并运行脚本。这样,我可以使用 NSTask 从我的主应用程序启动帮助程序,并在必要时调用[task terminate]。但是,我需要:
- 从它启动的帮助程序接收输出和错误的主应用程序
- 助手仅在 NSUserScriptTask 完成时终止
主应用程序的代码很简单:只需启动一个带有正确信息的 NSTask。这是我现在所拥有的(为了简单起见,我忽略了安全范围书签等的代码,这些都没有问题。但不要忘记这是在沙盒中运行的):
// 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") 之类的东西可以解决问题,但我不确定这是否是正确的做法。
因此,简而言之,感谢您对第一个和第二个问题的任何帮助。第三个我只是想确保我应该这样做。
谢谢
【问题讨论】:
-
你看过XPC吗?
-
@Dov 我将 XPC 用于其他目的,但老实说我不明白如何使用它。你认为它会在这里做吗?
-
我不能肯定地说,因为我没有尝试过,但它似乎更适合你正在做的事情。可能值得一看。
-
@Dov 为什么你认为它会更合适?我现在很好奇,我对 XPC 服务的了解几乎为零
-
如果你可以针对 10.8,有一些不错的 XPC Cocoa 包装器。否则,您将自己编写一些简单的包装器。我认为 XPC 非常适合您的用例。另一种选择是使用分布式通知来发出任务完成的信号,并让主应用程序注册这些通知。 1Password 的助手应用根据情况使用 XPC 和分布式通知在助手和主应用之间进行通信。
标签: objective-c cocoa nstask appstore-sandbox