Я пытался сделать (см. это и this) с недавним классом NSUserScriptTask и его подклассами, и до сих пор я решил некоторые проблемы, но некоторые другие еще предстоит решить. Как видно из документов, 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];
Помните, мне нужно, чтобы обработчик завершения запускался только тогда, когда: (а) задача была отменена (б) задача завершилась сама по себе, потому что сценарий завершил работу.
Теперь, на стороне помощника, вещи начинают становиться волосатыми, по крайней мере, для меня. Давайте для простоты представим, что сценарий представляет собой файл 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 работает с приложением, а не с помощником. - person Alex   schedule 28.03.2013