Insuring (idiot-proofing) a macro by using "if application is running"

I’m designing a repetitive macro that has a loop that has to be executed thousands of times, however, in the midst of this macro is a program that is crashing every once in a while (irregularly, and I’m in contact with the developer about it, but there is no ETA for a fix in sight).

Would it work if I wrap my entire macro inside an “If Then Else” action, which states "If all of the following are true: this application is running, then execute "

I’ve never used If Then Else before so I’m asking if it works as I am hoping: will the macro continue to execute smoothly, unless my problem application crashes, in which case, will KM stop the macro at that point?

Or does KM only check for the running of the app upon the launch of the whole macro? (In which case, this action would be useless to me.)

I hope that makes sense.

Another way to ask it: how often does KM poll for the conditions specified in an If Then Else action? Only at the beginning? Or constantly?

If it only polls at the beginning, is there a way to write a KM macro with a loop in such a way that it stops if a specific application is no longer running?

for example:

Is Chrome for example, running ?.kmmacros (2.9 KB)

tell application "System Events"
	count of (application processes where name = "Google Chrome")
end tell

Ah! So what you’re saying is that I could use this INSIDE my loop, and then use a Cancel All Macros action as a consequence if my app isn’t running.

That would make sure that the macro doesn’t keep running (and mess up stuff) when one of my target applications isn’t there anymore.

Thanks so much!

Quick question: why would you use the AppleScript? Wouldn’t it be simpler to use:

If Then Else > If All Conditions Are Met > If This Application > (the problem application) > Is Not Running > Execute the following actions > Cancel All Macros

(I don’t mind using AppleScript, but am not that savvy with it, so I always look for the simplest solution, and it seems like the simplest solution would be to just use the above KM action.)

Hey David

There's no need — the Keyboard Maestro Application Condition for 'If Then Else' will work fine.

AppleScript is just another method of doing it.

If you do use AppleScript there is also no need to use System Events for this task, because apps these days have a running property that will return a boolean value without sending an event to the app — so the app is NOT launched or otherwise run. This is true even of apps that have no sdef (AppleScript dictionary).

set PCalcIsRunning to application "PCalc" is running

In your macro you can do more than just cancel if your app isn't running — you can relaunch it if the task is not complete.

Loop
    Test for app-running — launch if NOT running.
    …
    Do stuff
    …
    App must be running for next step, so — Test for app-running — launch if NOT running.
    …
    Do stuff
    …
End Loop

You can put as many tests in there as you need.

You can put a Pause Until app-is-running condition after the test to make sure the app finishes starting up (if it needs to), before the macro continues.

Take the time to experiment with it with some little app like Calculator.

If running then
    Display text — app is running…
Else if NOT running then
    Display text — app is not running… Launching app…
    Launch app
    Pause until app is running
    Display text — app is running…
End if

Until you test it for yourself you're just depending upon someone else's word that it works.

It does work, but that’s beside the point. :smile:

-Chris

1 Like

I’m not sure I understood the first part of your response (regarding System Events) since I’m (so far) not using any AppleScript nor System Events (that I know of).

But your suggestion to have KM relaunch the problem application and pause until it’s running is brilliant. I don’t know why I didn’t think of that. That could potentially make my life a lot easier. The only thing I have to make sure is that my problem application doesn’t re-launch with windows left over from the previous launch. That might confuse my macro. I need it to open (or re-open) with all windows closed. Then the guts of my long macro commandeers it from there.

I did, however, have a brainstorm to have KM send me a text message if the app crashes! This had to be done via AppleScript but was very easy to script:

tell application "Messages"
	send "The app just crashed" to buddy "+11234567890" of service "SMS"
end tell

Hey David,

See @ComplexPoint's discussion above to salve your confusion. :smile:

You don't mention what application that is, so it's difficult to offer salient advice — but it shouldn't be too hard to close every window.

[quote]I did, however, have a brainstorm to have KM send me a text message if the app crashes! This had to be done via AppleScript but was very easy to script:
[/quote]

Very good. Use the tools that help you do your work.

-Chris

Oh I get it now.

I left off the name of the app just to eliminate distraction/clutter and try and get my KM-specific request as focused as possible. It’s a small utility tool that’s particular to my industry and it’s not likely anyone else on the KM forum is using it. It’s also a one-developer app and I’m already in contact with that developer about the crashing, so again, I felt it irrelevant to the KM questions.

It sounds like you’re all on track now. Check with the If Then Else action inside the loop, and if the application is not running, either cancel the macro or relaunch the application.

If you want to cancel the macro if the application crashes/quits, an alternative would be to have a second macro that is triggered when the application quits, and have it cancel all macros. Disable it by default, and enable it when you start this loop. So your macro could look like:

  • Launch Target App
  • Enable Macro “Track Target App”
  • Repeat 1000 Times
    • Do stuff
  • Disable Macro “Track Target App”

And the “Track Target App” macro is:

  • Trigger on “Target App” quits
  • Disable Macro “Track Target App”
  • Cancel All Macros

Relaunching the application is probably a better solution in this case, but the above technique can be useful in other cases.

1 Like

Hey David,

You'd be surprised at how often this assumption is wrong.

Often downloading and testing with a demo is key to solving someone's problem. Sometimes just looking at images of the app on Google-Images is enough — or enough to formulate the right question to ask that ends up being key.

If users don't give us enough information to help them they waste everyone's time. If you look around the board a while you'll discover that I keep saying this over and over in hopes that folks will listen.

-Chris

Thanks so much to both of you; I’m really appreciative for the help.

@peternlewis : is there anything wrong with a macro like this:

•Begin loop (1000x)
•Do stuff
•Check if the app is still running; if not, send me a text message, then stop all macros (or alternatively, do a sequence of actions to get it running again)
•End loop

(Seems like a simpler version of what you suggested, and I’m not clear on what the advantage would be in doing it your way.)

@ccstone: yes, point taken. To satisfy your curiosity :slight_smile: the app is Audiofile Engineering’s Triumph. It’s a sophisticated audio tool. It’s actually very AppleScript-able, but I don’t have enough of a background in AppleScript to be able to script it, but am finding ways to make KM control it via keyboard and mouse controls. (Yes, I realize this is prone to problems if x/y positions change or the program stalls, etc., but I’ve got it running fairly smoothly.)

Also, this is an operation I need to execute 1800 times, but once it’s done, will never have to do again. This isn’t a macro that will last months or years. It will last days at most.

The looped operations are basically: import a bunch of audio files, automatically lay them out as per a specific Triumph command, then export them. I have to do this 1800 times. At one point in the loop, Triumph will occasionally crash. But it’s very occasional (approx. 1 in 300-500 loops) so it’s not insurmountable. I’ve already notified the company about the crash and am hopeful they’ll fix it in a future version, however, I need to get the 1800 loops done now, so I’ll put up with the occasional crash (especially now that I can be notified via text message!)

I also have a few ways to error-check the final files it’s outputting, therefore, with 1800 versions of the final product being produced, if a couple of them get corrupt or lost due to a crash mid-way, I’ll find them.

KM grows more and more awesome every day as I keep realizing there are more and more uses for it!

No, there is nothing wrong with that. The issue comes when your loop looks more like this:

  • Repeat 1000 Times
    • Do stuff A
    • Do stuff with Target App
    • Do stuff B
    • Do stuff with Target App
    • Do stuff C
    • Do stuff with Target App
    • Do stuff D
    • Do stuff with Target App
    • Do stuff E
    • Do stuff with Target App
    • Check Target App and cancel/relaunch

Depending on what the stuff is and when the app crashes, it may be better to catch it earlier in the loop.

You might like to try simply quitting and restarting the app every 100 iterations of the loop. That might resolve the crashes.

Hey David,

Good to know.

So I see.

It lacks some niceties like:

tell application "TextWrangler"
  close every document without saving
end tell

OR

tell application "TextWrangler"
  close every document with saving
end tell

If you use Triumph a lot I suggest you invest some time in learning how to script it. You may not have time for it on this job, but it will pay dividends in future.

Triumph will handle this:

tell application "Triumph" to close every document

But if you have unsaved changes in a window it will ask if you want to save them.

Nevertheless you can either use the AppleScript + Keyboard Maestro to hit ‘Don't Save’ buttons (if needed).

Or you can loop through Triumph's windows in KM and press Cmd-W and press the ‘Don't Save’ button if necessary.

However, it's nicer when you can talk directly to the app. (You might mention the need to the developer for close without saving.)

System Events can do the job effectively if a bit slower than regular AppleScript:

# Close every document window WITHOUT saving.
tell application "System Events"
  tell application process "Triumph"
    set frontmost to true
    set winList to windows whose attribute "AXSubrole"'s value is "AXStandardWindow"
    repeat with theWindow in winList
      tell theWindow
        perform action "AXRaise"
        tell (first button whose description is "close button")
          if value of attribute "AXEdited" is true then
            click
            delay 0.1
            tell theWindow to click button "Don’t Save" of sheet 1
          else if value of attribute "AXEdited" is false then
            click
          end if
        end tell
      end tell
    end repeat
  end tell
end tell

* Only tested on Mavericks.

It should close any open windows AND click the ‘Don't Save’ button if necessary.

A bit of a pain to figure out — impossible to figure out without having access to the app — there is a demo.

So, a restart script might go something like this:

set closeWindows to false

tell application "Triumph"
  if not running then
    run
    set closeWindows to true
  end if
  activate
end tell
delay 0.25

if closeWindows then
  
  tell application "System Events"
    tell application process "Triumph"
      set frontmost to true
      set winList to windows whose attribute "AXSubrole"'s value is "AXStandardWindow"
      repeat with theWindow in winList
        tell theWindow
          perform action "AXRaise"
          tell (first button whose description is "close button")
            if value of attribute "AXEdited" is true then
              click
              delay 0.1
              tell theWindow to click button "Don’t Save" of sheet 1
            else if value of attribute "AXEdited" is false then
              click
            end if
          end tell
        end tell
      end repeat
    end tell
  end tell
  
end if

* Only tested on Mavericks.

-Chris

Thank you very, very much for going into such detail. This is all really good info for me to learn.

Since I’ve only got to accomplish one big task (albeit 1800 times) I’ve got a script going now that is working well for me – a combination of KM and embedded AppleScript.

I’ll pass on the comment about “close windows without saving” to the developer. It’s funny that they took so much time to implement deep AppleScripting but missed a simple thing like that…

As a quick footnote, the JavaScript for Automation version of AppleScript's is running idiom is the .running() method on the Application interface, so:

if (Application('TaskPaper').running()) {
    // ...
}

For reference, the full set of methods on the Application class (shown here in the Safari debugger) is:

2 Likes

Even though this reply comes 5 years after the Original Poster posted it, It may help someone who is having trouble making ComplexPoint's macro work.

I just spent a couple of hours pulling my hair out trying to figure out why it wasn't working.

In my case every time I changed the application name inside the AppleScript (line #2) my Mac kept changing the first double quote to curly quotes—and I didn't notice! It took me a long time to notice it since they are small.

Turns out there's a MacOS preference that was causing this. You can find this pref setting at: Keyboard > Text. Untick the box for "Use smart quotes and dashes".

Once I made sure those quotes were both straight quotes, the macro worked like a charm.

Hope this saves someone time and aggravation.