TheSaint Posted December 6, 2015 Posted December 6, 2015 (edited) This is just a little helpful script, that I started using a month or so ago (one program so far), with this version having a Restore ability added (so now using with two programs of mine, as of tonight).You can find other alternatives or similar scripts here, the most recent being - _SingleScriptguinness also has something much more elaborate, that is well worth checking out - _SingletonHWID.Just place the following at the beginning of your script, and adjust where needed and wanted (including Scriptname, variable names and MsgBox magic numbers, etc). Perhaps you should read and use the following - Best coding practices ... unlike my slack hobby programmer self (not always).expandcollapse popup#include <Misc.au3> Global $ans, $exe, $handle, $pid, $script, $Scriptname, $status, $w, $wins ; Assign the program name (window title). $Scriptname = "Update Mp3 Artwork v7.9 (updated December 2015)" ; The following line both sets the unique ID of the current program, plus queries if a program with the same ID is running already. $status = _Singleton("update-mp3artwork-thsaint", 1) If $status = 0 Then ; A program with the same ID is already running. ; Assign a PID for previous script, based on current executable name, whether current script is compiled or not, and assign the executable name. If @Compiled = 1 Then $pid = ProcessExists(@ScriptName) $exe = @ScriptName Else $pid = ProcessExists("AutoIt3.exe") $exe = "AutoIt3.exe" EndIf ; Assign a PID for current script. $script = @AutoItPID ; $ans = MsgBox(262177, "Close Running Instance Query", _ "'Update Mp3 Artwork' program is already running." & @LF & @LF & _ "Do you want to close it for another instance?" & @LF & @LF & _ "NOTE - If all work has been saved, and you are" & @LF & _ "trying to work on another album, then a click on" & @LF & _ "OK is recommended, else just click CANCEL, to" & @LF & _ "attempt a restore or re-activate of original." & @LF & @LF & _ $pid & " (" & $exe & ") " & $script & @LF & @LF & _ "(will default to CANCEL in 30 seconds)", 30) If $ans = 1 Then ; Attempt to close original instance, and continue with new. If $pid <> $script Then ProcessClose($pid) Else MsgBox(262192, "Close Error", "OK process failed!", 0) Exit EndIf ElseIf $ans = 2 Or $ans = -1 Then ; Attempt to retore and activate a non-active or minimized window. ; Get a list of all running programs with the same name. $wins = WinList($Scriptname, "") ; Loop through found instances and compare PID's, restoring or re-activating any match. For $w = 1 to $wins[0][0] $handle = $wins[$w][1] If WinGetProcess($handle, "") = $pid Then WinSetState($handle, "", @SW_RESTORE) WinActivate($handle, "") ExitLoop EndIf Next Exit EndIf EndIfEnjoy!P.S. I have a couple of programs that I regularly forget are minimized while working with another related one, so this saves me some mucking around to close and restart or restore etc. Pretty basic, but does the job well so far. Edited December 7, 2015 by TheSaint Deye 1 Make sure brain is in gear before opening mouth! Remember, what is not said, can be just as important as what is said. Spoiler What is the Secret Key? Life is like a Donut If I put effort into communication, I expect you to read properly & fully, or just not comment. Ignoring those who try to divert conversation with irrelevancies. If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it. I'm only big and bad, to those who have an over-active imagination. I may have the Artistic Liesense to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)
TheSaint Posted December 7, 2015 Author Posted December 7, 2015 I've added some more comments to first post and script.The first time I used this code, before the update, I also had a command-line parameter query. I didn't need that in the above version, but you could add it back in as an option, so that if no parameter is detected then a restore happens. perhaps without a query. So likewise, if a parameter is detected, then close original (first) instance and process parameters and new running instance of the program without a query.If one were to make a UDF based on my code etc, then all these elements/options should probably be considered and included ... plus any more that come to mind (user renaming the program executable, etc). Make sure brain is in gear before opening mouth! Remember, what is not said, can be just as important as what is said. Spoiler What is the Secret Key? Life is like a Donut If I put effort into communication, I expect you to read properly & fully, or just not comment. Ignoring those who try to divert conversation with irrelevancies. If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it. I'm only big and bad, to those who have an over-active imagination. I may have the Artistic Liesense to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)
TheSaint Posted December 14, 2015 Author Posted December 14, 2015 I'm now using a partial implementation of this code (pre-determined without prompts), with the latest version of my KindEbook Wishlist program (v1.9), for any that want to check some of the code out in action.That means I am now using three variations with three different programs. Make sure brain is in gear before opening mouth! Remember, what is not said, can be just as important as what is said. Spoiler What is the Secret Key? Life is like a Donut If I put effort into communication, I expect you to read properly & fully, or just not comment. Ignoring those who try to divert conversation with irrelevancies. If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it. I'm only big and bad, to those who have an over-active imagination. I may have the Artistic Liesense to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)
Deye Posted February 10, 2016 Posted February 10, 2016 Just implemented this and it works flawlessly thanks TheSaint 1
AntS Posted April 1, 2025 Posted April 1, 2025 (edited) Late question here... Apologies. In this line: $pid = ProcessExists(@ScriptName) ... how do we know that ProcessExists() is returning the process ID of the previous instance and not the current one? 🤔 Edited April 4, 2025 by AntS
argumentum Posted April 2, 2025 Posted April 2, 2025 (edited) 1 hour ago, AntS said: ProcessExists is returning the process ID of the previous instance and not the current one? Func OthersPID($iPID) Local $pid = ProcessExists(@ScriptName) If $pid = @AutoItPID Then Return "is me" If $pid = 0 Then Return "no such EXE" Return "ain't me" EndFunc this was meant for compiled scripts. But you are right, use ProcessList() instead Edited April 2, 2025 by argumentum English Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
AntS Posted April 4, 2025 Posted April 4, 2025 Thanks, argumentum. If there were something in ProcessExists() that meant it always returned the ID of a process other than its own then I could see how it'd make sense to use it. Below is an alternative, condensed script that uses ProcessList() to deal with a previous instance. It attempts to close all matching processes other than its own. expandcollapse popup#include <Misc.au3> Local $aProcessList Local $iPID Local $i, $iCount Local $sMsg ;close any previous instances If _Singleton("43832176", 1) = 0 then ;1 - do not exit script MsgBox ($MB_SYSTEMMODAL, "Example Script (ID " & @AutoItPID & ")", "At least one previous instance found.") ;get list of processes with same .exe name $aProcessList = ProcessList(@ScriptName) $iCount = $aProcessList[0][0] $i = 1 While $i <= $iCount ;get process ID of i-th process $iPID = $aProcessList[$i][1] If $iPID <> @AutoItPID then ;close process MsgBox ($MB_SYSTEMMODAL, "Example Script (ID " & @AutoItPID & ")", "Attempting to close process (ID " & $iPID & ").") ProcessClose($iPID) ;check whether process closed Sleep(1000) If ProcessExists($iPID) = 0 Then $sMsg = "Process (ID " & $iPID & ") no longer running." Else $sMsg = "Unsuccessful: process (ID " & $iPID & ") still running!" EndIf MsgBox ($MB_SYSTEMMODAL, "Example Script (ID " & @AutoItPID & ")", $sMsg) EndIf $i = $i + 1 Wend EndIf ;perform script's task Sleep(60000) However, I'm encountering some strange behaviour. If the .exe is run afresh, it does so correctly as a "singleton". If a second instance of the .exe is run, it correctly identifies and closes the first process. However, when a third instance is run (after the first has closed), it does not close the second process. It doesn't even flag its existence. The code should allow you to "chain" process closures. Do you have any idea what's happening? I'm wondering if it's something to do with (the implementation of) _Singleton().
AntS Posted April 4, 2025 Posted April 4, 2025 Almost as soon as I hit Submit Reply, I realised... it's not necessary to use _Singleton() at all. If ProcessList() returns a count of 2 or more for the given .exe name, we know there's at least one previous instance. The contents of the process list can then be used as normal to identify and close the unwanted processes. The code below succesfully allows you to chain process closures. expandcollapse popup#include <Misc.au3> Local $aProcessList Local $iPID Local $i, $iCount Local $sMsg ;get list of processes with same .exe name $aProcessList = ProcessList(@ScriptName) $iCount = $aProcessList[0][0] ;close any previous instances If $iCount >= 2 then MsgBox ($MB_SYSTEMMODAL, "Example Script (ID " & @AutoItPID & ")", "At least one previous instance found.") $i = 1 While $i <= $iCount ;get process ID of i-th process $iPID = $aProcessList[$i][1] If $iPID <> @AutoItPID then ;close process MsgBox ($MB_SYSTEMMODAL, "Example Script (ID " & @AutoItPID & ")", "Attempting to close process (ID " & $iPID & ").") ProcessClose($iPID) ;check whether process closed Sleep(1000) If ProcessExists($iPID) = 0 Then $sMsg = "Process (ID " & $iPID & ") no longer running." Else $sMsg = "Unsuccessful: process (ID " & $iPID & ") still running!" EndIf MsgBox ($MB_SYSTEMMODAL, "Example Script (ID " & @AutoItPID & ")", $sMsg) EndIf $i = $i + 1 Wend EndIf ;perform script's task Sleep(60000)
Nine Posted April 4, 2025 Posted April 4, 2025 40 minutes ago, AntS said: I'm wondering if it's something to do with (the implementation of) _Singleton(). Yes it is. When you kill the process that has created the mutex, its handle is automatically closed. You need to recreate the mutex in the new process. Like this : expandcollapse popup#include <Misc.au3> If Not @Compiled Then Exit MsgBox($MB_OK, "Error", "This script must be compiled") Local $aProcessList, $iPID, $sMsg ;close any previous instances If _Singleton("43832176", 1) = 0 Then ;1 - do not exit script MsgBox($MB_SYSTEMMODAL, "Example Script (ID " & @AutoItPID & ")", "At least one previous instance found.") ;get list of processes with same .exe name $aProcessList = ProcessList(@ScriptName) For $i = 1 To $aProcessList[0][0] ;get process ID of i-th process $iPID = $aProcessList[$i][1] If $iPID <> @AutoItPID Then ;close process MsgBox($MB_SYSTEMMODAL, "Example Script (ID " & @AutoItPID & ")", "Attempting to close process (ID " & $iPID & ").") ProcessClose($iPID) ProcessWaitClose($iPID) If Not ProcessExists($iPID) Then $sMsg = "Process (ID " & $iPID & ") no longer running." _Singleton("43832176") Else $sMsg = "Unsuccessful: process (ID " & $iPID & ") still running!" EndIf MsgBox($MB_SYSTEMMODAL, "Example Script (ID " & @AutoItPID & ")", $sMsg) EndIf Next EndIf While Sleep(100) WEnd ps. this script only works when compiled... “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
argumentum Posted April 4, 2025 Posted April 4, 2025 1 hour ago, AntS said: If the .exe is run afresh, it does so 25 minutes ago, AntS said: Almost as soon as I hit Submit Reply, I realized... it's not necessary to use _Singleton() at all. I use a different approach. _OnlyMe() Func _OnlyMe() Local $sSingleStr = StringTrimRight(@ScriptName, 4) & ": plz don't kill me, thank you" If WinExists($sSingleStr) Then WinClose($sSingleStr) ;This function sends a close message to a window, the result ;depends on the window (it may ask to save data, etc.). ;To force a window to close, use the WinKill() function. Sleep(300) If WinExists($sSingleStr) Then WinKill($sSingleStr) EndIf EndIf AutoItWinSetTitle($sSingleStr & ":" & @AutoItPID) EndFunc ;==>_OnlyMe Example() Func Example() GUICreate("_OnlyMe", 400, 200) GUICtrlCreateLabel(@HOUR & ":" & @MIN & ":" & @SEC & "." & @MSEC, 10, 10) Local $idBttnAgain = GUICtrlCreateButton("run again", 50, 50, 100) GUISetState(@SW_SHOW) While 1 Switch GUIGetMsg() Case $idBttnAgain ShellExecute(@AutoItExe, '"' & @ScriptFullPath & '"') Case -3 GUIDelete() ExitLoop EndSwitch WEnd EndFunc ;==>Example It does the same ( give or take ) for what you are looking for. Once you are more familiar with AutoIt, using an IPC would be a better approach. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
AntS Posted April 4, 2025 Posted April 4, 2025 55 minutes ago, Nine said: When you kill the process that has created the mutex, its handle is automatically closed. Aha! Now, it makes sense. When the second instance is run, _Singleton() doesn't create a second mutex object, but it does recognise the existence of first. The object is lost when the first process is closed. Thank you very much for the explanation, Nine, and for the modified code. Thank you, argumentum, for the additional code and pointers. 🙂 argumentum 1
Nine Posted April 4, 2025 Posted April 4, 2025 @AntS If you want to mix compiled and uncompiled scripts at the same time, you could create a file map that will hold the previous PID : #NoTrayIcon #include <WinAPIFiles.au3> #include <APIErrorsConstants.au3> #include <WinAPIHObj.au3> Local $hMap = _WinAPI_CreateFileMapping(-1, 128, "SingletonMap") If @extended = $ERROR_ALREADY_EXISTS Then KillProcess($hMap) Local $pAddress = _WinAPI_MapViewOfFile($hMap) Local $tData = DllStructCreate("uint iPID", $pAddress) $tData.iPID = @AutoItPID _WinAPI_UnmapViewOfFile($pAddress) MsgBox($MB_OK, "Running", "Instance " & @AutoItPID) _WinAPI_CloseHandle($hMap) Func KillProcess($hMapping) Local $pAddress = _WinAPI_MapViewOfFile($hMapping) Local $tData = DllStructCreate("uint iPID", $pAddress) MsgBox($MB_OK, "Closing", $tData.iPID) ProcessClose($tData.iPID) ProcessWaitClose($tData.iPID) EndFunc “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
AntS Posted April 7, 2025 Posted April 7, 2025 @NineNoted. Thanks. There's one question that needs to be asked, so I may as well ask it... Is there any particular benefit to using _Singleton() to check for a previous instance over ProcessList() (or WinExists())?
Nine Posted April 7, 2025 Posted April 7, 2025 Yes of course. A mutex is Windows method to check for process unicity. Other methods will work under circumstances, and not under others. _Singleton is the way to use mutex but following your requirements, my solution is the best... “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now