私は最近の NSUserScriptTask クラスとそのサブクラスを使って何とかしようとしてきました (これとこれを見てください)。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 サブクラスを使用します。現実の世界では、3 種類のタスクに対応する必要があります)。これが私がこれまでに得たものです:
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;
}
ここで、3 つの質問があります (この SO エントリで聞きたい質問です)。まず、「終了したタスク」は印刷されません (ブロックは呼び出されません)。これは、タスクが実行を開始することさえないためです。代わりに、コンソールでこれを取得します。
MessageTracer: msgtracer_vlog_with_keys:377: odd number of keys (domain: com.apple.automation.nsuserscripttask_run, last key: com.apple.message.signature)
メインアプリからまったく同じコードを実行してみましたが、大騒ぎせずに完了しました(ただし、メインアプリからはスクリプトをキャンセルできなくなりました)。
第二return 0;
に、完了ハンドラが呼び出された後にのみ main() の最後に到達したい。しかし、私はそれを行う方法がわかりません。
三番目に、ヘルパーからのエラーまたは出力があるときはいつでも、そのエラー/出力をアプリに送り返し、アプリは errorPipe/outputPipe を介してそれらを受け取ります。のような何かfprintf(stderr/stdout, "string")
がトリックを行いますが、それが正しい方法であるかどうかはわかりません。
したがって、要するに、最初と 2 番目の問題に関するヘルプは大歓迎です。3 つ目は、それが本来のやり方であることを確認したいだけです。
ありがとう