The Unofficial Macro Scheduler Puzzler of the Week 5

Anything Really. Just keep it clean!

Moderators: Dorian (MJT support), JRL

User avatar
JRL
Automation Wizard
Posts: 3501
Joined: Mon Jan 10, 2005 6:22 pm
Location: Iowa

The Unofficial Macro Scheduler Puzzler of the Week 5

Post by JRL » Fri Oct 11, 2013 8:15 pm

This weeks puzzler is all about tidiness.

There are multiple examples on this forum showing how to prevent a script or executable from running twice by testing to see that a program with the same name is already running, then closing the newly started program.

However, its a puzzler how one would compose a program to prevent that program from running twice by testing to see that a program with the same name is already running, then close the original program and let the newly started program continue running.

In other words, write a program that you run as instance A, when you start instance B of the same program instance A is closed and instance B continues. When instance C is started, instance B is closed and instance C continues. Etc. Etc. Thus, functionally, there is only a fraction of a second when there is more than one copy of the program running.

Make sense? Sound easy?... Maybe it is. 20 rep points and bragging rights to the cleanest shortest method submitted by next Friday.

armsys
Automation Wizard
Posts: 1108
Joined: Wed Dec 04, 2002 10:28 am
Location: Hong Kong

Post by armsys » Sun Oct 13, 2013 10:17 pm

Professor JRL,
Please evalute my humble code:

Code: Select all

// POI=program of interest
Let>poi=abc.exe
ProcessExist>%poi%,r
If>r=True
  KillProcess>%poi%
Endif
Run>%poi%
Thanks.

User avatar
Grovkillen
Automation Wizard
Posts: 1023
Joined: Fri Aug 10, 2012 2:38 pm
Location: Bräcke, Sweden
Contact:

Post by Grovkillen » Mon Oct 14, 2013 10:54 am

Hello!

I would make sure that the process I want to kill isn't my current process. So, I would need to kill the process using PID (process id) instead of only the name "app_name.exe".

Here's my solution:

Code: Select all

//Set example to Notepad just to demonstrate the idea
Let>APP_TITLE=Notepad

//Open a bunch of Notepads to close
Let>k=0
Repeat>k
    Let>k=k+1
    RunProgram>%APP_TITLE%.exe
Until>k=10

//Determine what your current process id (PID) is and all other id with the same app title = already running = need to be killed
LibFunc>Kernel32,GetCurrentProcessId,CURRENT_PROCESS_ID

//Get the other PID (" | clip " at the end of the cmd line is used to put outpot to clipboard...)
Let>RP_WAIT=1
RunProgram>cmd /c Tasklist /FI "Imagename eq %APP_TITLE%.exe" | clip
GetClipBoard>CLIPBOARD,0

//RegEx the result... MS seems to lack some RegEx logics (see comments below). I wish it didn't since that would help me find the correct values with more precision
Let>REGEX_PATTERN=[0-9]{4,5}
//Below doesn't work... I'm not sure why but the " (?<=%APP_TITLE%\.exe[\s]*) " would be nice to use in order to be sure that only the PID is collected and NOT the Memory useage...
//Let>REGEX_PATTERN=(?<=%APP_TITLE%\.exe[\s]*)[0-9]{3,5}
RegEx>%REGEX_PATTERN%,%CLIPBOARD%,0,ARRAY_TO_CLOSE,NUM_MATCHES,0,,

//Kill all other running running processes (with the same app title)
Let>k=0
Repeat>k
    Let>k=k+1
  If>ARRAY_TO_CLOSE_%k%=%CURRENT_PROCESS_ID%
    //Do not kill process...
  Else>
    KillProcess>ARRAY_TO_CLOSE_%k%
  Endif>
Until>k=NUM_MATCHES
I open 10 Notepad just to demonstrate the consept. I then get my current PID using a Kernel32 function. After that I call for every process named the same as my current app, the result is put to the clipboard and then RegExed into an array. After that all but the current process is killed one by one...

The wanted RegEx pattern seems not to be supported by MS but if it did work this is how it's built up:
REGEX_PATTERN=(?<=%APP_TITLE%\.exe[\s]*)[0-9]{3,5}

Explained:
( : start of match
?<= : tell to start "capture" after match is found
%APP_TITLE%\.exe : what to match (exact match of "Notepad.exe" " . " is any character in RegEx " \. " indicate that the " . " should be matched as a dot)
[\s]* : \s indicates that none printed character should be match (i.e. the spaces between the process name and the PID). the " * " make sure to find 0 to infinite number of none printed characters.
) : end of match
below is what to capture
[0-9] : this tells that only numbers 0 to 9 should be captured
{3,5} : this tells that capture 3 to 5 characters, discard the other (shorter or longer strings of numbers)

Super trick
cmd /c Tasklist /FI "Imagename eq %APP_TITLE%.exe" | clip
The " | clip " at the end of any cmd will put the output to the clipboard. Super handy if you ask me! :)

EDIT: I have found that the MS RegEx flavor is Perl 5.10 and the " * " is not compatable with Perl RegEx. In order to fix this I need to know how many none printed characters it is between the app title and the PID. In the case with "notepad.exe" this is 19. The RegEx pattern should then say:
REGEX_PATTERN=(?<=%APP_TITLE%\.exe[\s]{19})[0-9]{3,5}
Let>ME=%Script%

Running: 15.0.24
version history

armsys
Automation Wizard
Posts: 1108
Joined: Wed Dec 04, 2002 10:28 am
Location: Hong Kong

Post by armsys » Mon Oct 14, 2013 1:02 pm

Hi Grovkillen,
It seems your code deviates from Professor JRL's requirement.
In a nutshell, no second instance of a program is allowed.
But in your case, you fire up 10 instances of Notepad.
On the other hand, I enjoy studying your convoluted showoff of tradecraft.

User avatar
JRL
Automation Wizard
Posts: 3501
Joined: Mon Jan 10, 2005 6:22 pm
Location: Iowa

Post by JRL » Mon Oct 14, 2013 1:35 pm

Wow... woke up this morning to two puzzler responses. I'm going to need to fix a cup of tea and study these....

@armsys,

Short and sweet but unfortunately I don't see how this will work properly unless your program is named something other than "abc.exe" which defeats the puzzlement. When we run the program named "abc.exe", line five, KillProcess>, will kill all instances of "abc.exe" which includes the currently running program. "abc.exe" will never reach line seven to start the program back up again.

@Grovkillen,

You have demonstrated a method to kill multiple processes, but you did not provide a program that manages its own occurrences. I do feel you are on the right track.

P.S. I like clip too. Though I don't think it was available until Vista? or maybe Win7. In any case its not available in WinXP. But it was available in Windows server software as far back as NT so you could copy the executable over to XP and use it.

armsys
Automation Wizard
Posts: 1108
Joined: Wed Dec 04, 2002 10:28 am
Location: Hong Kong

Post by armsys » Mon Oct 14, 2013 1:53 pm

JRL,
Thanks for your sweet words. How about the folloing code:

Code: Select all

// POI=program of interest
Let>poi=abc.exe
ProcessExist>%poi%,r
If>r=True
  KillProcess>%poi%
 WaitProcessTerminated>%poi%
Endif
ProcessExist>%poi%,r
If>r=False
  Run>%poi%
Endif

User avatar
Rain
Automation Wizard
Posts: 550
Joined: Tue Aug 09, 2005 5:02 pm
Contact:

Post by Rain » Mon Oct 14, 2013 2:03 pm

Here is my method.

Edited: Shortened script from 15 to 13 lines.

Code: Select all

IfWindowOpen>~!My App Is Running!~
  CloseWindow>~!My App Is Running!~
Endif

Dialog>Dialog100
   Caption=~!My App Is Running!~
EndDialog>Dialog100

AddDialogHandler>Dialog100,,OnClose,ExitScript

Label>ActionLoop
Wait>0.1
Goto>ActionLoop

SRT>ExitScript
  Exit>1
END>ExitScript
Last edited by Rain on Mon Oct 14, 2013 2:15 pm, edited 1 time in total.

User avatar
JRL
Automation Wizard
Posts: 3501
Joined: Mon Jan 10, 2005 6:22 pm
Location: Iowa

Post by JRL » Mon Oct 14, 2013 2:06 pm

@armsys,

Exactly the same issue. If the value of variable "poi" is the name of your program, ProcessExist>%poi%,r will always test "True", then the KillProcess line will always put an end to the program's process. Its exactly the same as putting in an Exit>.

This concept is flawed. Some form of excluding the current process will be required.


@Rain,

Good job! It works and meets the criteria.


@everone else

Any other ideas?

User avatar
Grovkillen
Automation Wizard
Posts: 1023
Joined: Fri Aug 10, 2012 2:38 pm
Location: Bräcke, Sweden
Contact:

Post by Grovkillen » Mon Oct 14, 2013 2:22 pm

Here's an updated script, compile as "Puzzler5.exe" and try to make it break...

@Rain: good job! :)

Code: Select all

//Name of app
Let>APP_TITLE=Puzzler5

Dialog>Dialog1
object Dialog1: TForm
  Left = 247
  Top = 96
  HelpContext = 5000
  BorderIcons = [biSystemMenu]
  Caption = 'CustomDialog'
  ClientHeight = 93
  ClientWidth = 623
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = True
  ShowHint = True
  OnTaskBar = False
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 8
    Top = 16
    Width = 125
    Height = 57
    Alignment = taCenter
    Caption = 'Label1'
    Font.Charset = ANSI_CHARSET
    Font.Color = clBlack
    Font.Height = -48
    Font.Name = 'Arial Narrow'
    Font.Style = [fsBold]
    ParentFont = False
  end
end
EndDialog>Dialog1
SetDialogProperty>Dialog1,Label1,Caption,I'M THE ONLY APP RUNNING :)
AddDialogHandler>Dialog1,,OnClose,EXIT_APP

//Determine what your current process id (PID) is
LibFunc>Kernel32,GetCurrentProcessId,CURRENT_PROCESS_ID
SetDialogProperty>Dialog1,,Caption,%APP_TITLE% (PID: %CURRENT_PROCESS_ID%)

//Get the other PID (" | clip " at the end of the cmd line is used to put outpot to clipboard...)
Let>RP_WAIT=1
RunProgram>cmd /c Tasklist /FI "Imagename eq %APP_TITLE%.exe" | clip
GetClipBoard>CLIPBOARD,0

//RegEx the result... 
Let>REGEX_PATTERN=(?<=[\s]{5})[0-9]{3,5}
RegEx>%REGEX_PATTERN%,%CLIPBOARD%,0,ARRAY_TO_CLOSE,NUM_MATCHES,0,,

Show>Dialog1

If>NUM_MATCHES>1
  //Kill all other running running processes (with the same app title)
  Let>k=0
  Repeat>k
      Let>k=k+1
    If>ARRAY_TO_CLOSE_%k%=%CURRENT_PROCESS_ID%
      //Do not kill process...
    Else>
        //This text will unlikely be seen but anyways...
      SetDialogProperty>Dialog1,Label1,Caption,CLOSING OLD APP
      KillProcess>ARRAY_TO_CLOSE_%k%
      SetDialogProperty>Dialog1,Label1,Caption,I'M THE ONLY APP RUNNING :)
    Endif>
  Until>k=NUM_MATCHES
Endif>

Label>WAIT_FOR_AWHILE
Wait>0.02
GoTo>WAIT_FOR_AWHILE

SRT>EXIT_APP
  Exit>0
END>EXIT_APP
Let>ME=%Script%

Running: 15.0.24
version history

User avatar
JRL
Automation Wizard
Posts: 3501
Joined: Mon Jan 10, 2005 6:22 pm
Location: Iowa

Post by JRL » Mon Oct 14, 2013 2:34 pm

Grovkillen,

Good job! It works and it also meets the criteria. I like the process ID in the dialog title, very clever.

armsys
Automation Wizard
Posts: 1108
Joined: Wed Dec 04, 2002 10:28 am
Location: Hong Kong

Post by armsys » Mon Oct 14, 2013 9:57 pm

Hi JRL, Grovkillen, & Rain,
Thanks for the resourceful learning experience.
I like Rain's clean code.
I like Grovkillen's colorful annotation (documentation).
Thanks again.

User avatar
PepsiHog
Automation Wizard
Posts: 511
Joined: Wed Apr 08, 2009 4:19 pm
Location: Florida

Post by PepsiHog » Wed Oct 16, 2013 12:20 am

CRUD! OOPS. It appears Rain already did this. I wasn't paying attention very well. This entry is INVALID. Sorry.

GREAT IDEA, Rain. Plus I didn't think of gutting the dialog. So Rain would still have won.

Rain,

Did you steal my idea before I thought of it? Shame. Shame. Shame. :lol:

************************************************************
ORIGINAL POST / BUT INVALID


It's meeeee. Hello.

I'm sure Friday already came and went. But just for fun, this is what I thought of. JRL and I spoke of this M.O. some time back.

Do you remember JRL?

Anyway, check it out. Would this have qualified or did I miss something?

Code: Select all

GetWindowHandle>Program Name Here*,winhan
let>win_usehandle=1

IfWindowOpen>%winhan%
         CloseWindow>winhan
endif

Dialog>Dialog1
object Dialog1: TForm
  Left = 251
  Top = 104
  HelpContext = 5000
  BorderIcons = [biSystemMenu]
  Caption = 'Program Name Here'
  ClientHeight = 27
  ClientWidth = 115
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = True
  ShowHint = True
  OnTaskBar = False
  PixelsPerInch = 96
  TextHeight = 13
end
EndDialog>Dialog1


AddDialogHandler>Dialog1,,OnClose,DoExit

....Do something.....
let>NeverEnding=0
While>NeverEnding=0

                        // This would be the "Do Something" part.
                       // Does not really need to be a loop.
                      // Could be anything.

EndWhile

srt>DoExit
  mdl>New Instance Running...GoodBye.
  exit
END>DoExit

Actually, I believe the idea was JRL's for another application that we were then asked not to discuss on the forum. (The Three Musketeers)

PepsiHog
Windows 7

PepsiHog. Yep! I drink LOTS of Pepsi (still..in 2021) AND enjoy programming. (That's my little piece of heaven!)

The immensity of the scope of possibilities within Macro Scheduler pushes the user beyond just macros!

User avatar
PepsiHog
Automation Wizard
Posts: 511
Joined: Wed Apr 08, 2009 4:19 pm
Location: Florida

Post by PepsiHog » Fri Oct 18, 2013 3:54 pm

Hello again,

So far as I can tell this will work perfectly. Did I do something that's not allowed?

Code: Select all

TimeStamp>%Temp_Dir%\Stop.txt,

OnEvent>File_Exists,%Temp_Dir%\Stop.txt,0,RemoveFile
OnEvent>File_Exists,%Temp_Dir%\Stop.txt,0,
OnEvent>File_Exists,%Temp_Dir%\Stop.txt,0,DoExit

// Do Something Here............
While>NeverEnding<>1
EndWhile

srt>RemoveFile
      DeleteFile>%Temp_Dir%\Stop.txt
END>RemoveFile

srt>DoExit
  exit
END>DoExit
Only 12 lines of code.

See if it works,
PepsiHog
Windows 7

PepsiHog. Yep! I drink LOTS of Pepsi (still..in 2021) AND enjoy programming. (That's my little piece of heaven!)

The immensity of the scope of possibilities within Macro Scheduler pushes the user beyond just macros!

User avatar
JRL
Automation Wizard
Posts: 3501
Joined: Mon Jan 10, 2005 6:22 pm
Location: Iowa

Post by JRL » Fri Oct 18, 2013 10:38 pm

@PepsiHog

Your concept is good. I had to make a small mod to make it work on my computer.

PepsiHog's script rearranged for line counting clarity and slightly modified.

Code: Select all

TimeStamp>%Temp_Dir%\Stop.txt,
OnEvent>File_Exists,%Temp_Dir%\Stop.txt,0,RemoveFile
Wait>0.1
OnEvent>File_Exists,%Temp_Dir%\Stop.txt,0,DoExit
srt>RemoveFile
  DeleteFile>%Temp_Dir%\Stop.txt
END>RemoveFile
srt>DoExit
  exit
END>DoExit




Label>Loop
Wait>0.01
Goto>Loop

@Rain and PepsiHog,

Even though I had to take liberties with PepsiHog's to make it work for me, I think the concept is good. Both scripts have the same number of lines so I think I'm going to split the points and call you both winners.

Congrats to both of you.

Rain's script rearranged for line counting clarity.

Code: Select all

IfWindowOpen>~!My App Is Running!~
  CloseWindow>~!My App Is Running!~
EndIf
Dialog>Dialog100
Caption=~!My App Is Running!~
EndDialog>Dialog100
AddDialogHandler>Dialog100,,OnClose,ExitScript
SRT>ExitScript
  Exit>1
END>ExitScript



Label>Loop
Wait>0.01
Goto>Loop

Edit - Fixed the error I made in Rain's script. Sorry Rain. Can't even imagine how I did that, might make a good puzzler if it could be explained.
Last edited by JRL on Sun Oct 20, 2013 1:09 am, edited 1 time in total.

User avatar
Rain
Automation Wizard
Posts: 550
Joined: Tue Aug 09, 2005 5:02 pm
Contact:

Post by Rain » Sat Oct 19, 2013 4:52 pm

Thanks JRL and congrats Pepsi!

@JRL

IfWindowOpen> In your rearranged script should be IfWindowOpen>~!My App Is Running!~

Post Reply
Sign up to our newsletter for free automation tips, tricks & discounts