-[AMWorkflowController stop:]
does not stop a workflow from executing (more info). Here is a workaround that will stop all workflows your application is be running.
When you execute a worklfow using Automator.framework, the workflow is run in its own process, which insulates it from the invoking application, and vica verca 1
. At least on Leopard, the program that runs the workflow is /System/Library/CoreServices/Automator Runner.app/Contents/MacOS/Automator Runner
. It has the bundle-identifier com.apple.AutomatorRunner
, and it will be a sub-process of your application, since your application kicked it off.
That gives us enough information to kill just the instances of Automator Runner
that are running your worklfows. This will stop workflow execution dead in it’s tracks.
- (void) killAutomatorRunnerSubprocesses; {
ProcessSerialNumber PSN = {kNoProcess};
while(GetNextProcess(&PSN) == noErr) {
NSDictionary *info = (NSDictionary*)ProcessInformationCopyDictionary(&PSN,kProcessDictionaryIncludeAllInformationMask);
if(info) {
NSString *theirBundleID = [info objectForKey:@"CFBundleIdentifier"];
if([theirBundleID isEqualTo:@"com.apple.AutomatorRunner"] && [self processWasLaunchedByUs:PSN]){
NSLog(@"Killing AutomatorRunner sub-process: PSN={high=%u low=%u}", PSN.highLongOfPSN, PSN.lowLongOfPSN);
KillProcess(&PSN);
}
CFRelease((CFDictionaryRef)info);
}
}
}
- (BOOL) processWasLaunchedByUs:(ProcessSerialNumber)PSN; {
ProcessInfoRec processInfo = {0};
//we use GetProcessInformation(), and not the more modern ProcessInformationCopyDictionary()
//because ProcessInformationCopyDictionary stores the "ParentPSN" as a 32 or 64-bit number,
//and it is not clear to me what the endian-safe way to transform it into a ProcessSerialNumber
//structure is, see http://lists.apple.com/archives/carbon-dev/2007/Mar/msg00283.html
if(GetProcessInformation(&PSN,&processInfo) != noErr)
return NO;
Boolean theyAreOurChild = FALSE;
ProcessSerialNumber ourPSN = {0,kCurrentProcess};
OSStatus err = SameProcess(&processInfo.processLauncher, &ourPSN, &theyAreOurChild);
return !err && theyAreOurChild;
}
Please let me know if this works for you, of if you have a better solution.
1
Automator.app does not appear to spawn an
Automator Runner
when it runs a workflow. I’m not sure why. But since the stop button works in Automator.app, understanding why might lead to a better work around. Or not.