Sign in to follow this  
Followers 0
Valik

New approach to only allowing one instance

26 posts in this topic

Thanks to DllCall(), the proper way of only allowing a single instance of a script to run is now possible. Here is the code:

Func Singleton($semaphore)
    Local $ERROR_ALREADY_EXISTS = 183
    DllCall("kernel32.dll", "int", "CreateSemaphore", "int", 0, "long", 1, "long", 1, "str", $semaphore)
    Local $lastError = DllCall("kernel32.dll", "int", "GetLastError")
    If $lastError[0] = $ERROR_ALREADY_EXISTS Then Exit -1
EndFunc; Singleton()

Once a semaphore is created, a second attemp to create it will result in ERROR_ALREADY_EXISTS. By checking for that error, you know that an instance is already running.

The main advantage to this over changing the window title is that its a lot harder to spoof or accidentally trigger since the semaphore is more likely to be completely unique than a window title. It also means the internal window title can be used for whatever purpose the scripter desires.

The parameter this function takes should be unique, but not generated randomly. It must be the same between all versions of the script, but it must be different from any other scripts. The longer and more random it is, the less likely to conflict with anything else (But again, it can't be randomly generated at run-time or it will defeat the purpose).

Share this post


Link to post
Share on other sites



I'd like to use this but I can't make it work...

I tested with this simple script:

Sleep(10000)

Func OnAutoItStart()
  SingleTon(@ScriptFullPath)
EndFunc  ;==>OnAutoItStart

Func SingleTon($SEMAPHORE)
   Local $ERROR_ALREADY_EXISTS = 183
   DllCall ("kernel32.dll", "int", "CreateSemaphore", "int", 0, "long", 1, "long", 1, "str", $SEMAPHORE)
   Local $LASTERROR = DllCall ("kernel32.dll", "int", "GetLastError")
   If $LASTERROR[0] = $ERROR_ALREADY_EXISTS Then Exit -1
EndFunc  ;==>Singleton

but starting on the .au3 icon many times, manys A appear in the tray for the 10 seconds.

Moreover putting a MsgBox(0,'',$LASTERROR[0]) before the If/Then shows always 126.

What am I doing wrong?

If it can help I am using Windows XP 5.1.2600 Sp2

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

ezzetabi... @ScriptFullName IS NOT STATIC. Moving the script will change this value, thus, a new Semaphore will be created, thus, two instances will exist. Static means static, as in, never ever will change in any version of the script since the beginning of time until the end of time, regardless of the name of the file or the location of the file on the file system. It can't be influenced by outside forces.

Singleton("The name of my project")

or

Singleton("askdfgjdksjfdskdjfs")

Something like that (I use project names without version number).

That all being said, attempting to use @ScriptFullPath is also why its not working. Even though the path is a constant string and is less than _MAX_PATH (A requirement of CreateSemaphore), it still doesn't seem to want to use it as the name of the Semaphore.

Basically, though, it boils down to incorrect usage of the function. Use it correctly and it works correctly.

Edit: Added a little text.

Edited by Valik

Share this post


Link to post
Share on other sites

ezzetabi... @ScriptFullName IS NOT STATIC.  Moving the script will change this value, thus, a new Semaphore will be created, thus, two instances will exist.  Static means static, as in, never ever will change in any version of the script since the beginning of time until the end of time. 

Singleton("The name of my project")

Something like that (I use project names without version number).

That all being said, attempting to use @ScriptFullPath is also why its not working.  Even though the path is a constant string and is less than _MAX_PATH (A requirement of CreateSemaphore), it still doesn't seem to want to use it as the name of the Semaphore.

Basically, though, it boils down to incorrect usage of the function.  Use it correctly and it works correctly.

<{POST_SNAPBACK}>

I see where you are coming from on it being static, but is there a way to check if the string passed is a literal? If there is then that may be something you would require to avoid this confusion, if not then I would at least note it in documentation. Great UDF though!

*** Matt @ MPCS

Share this post


Link to post
Share on other sites

I see where you are coming from on it being static, but is there a way to check if the string passed is a literal? If there is then that may be something you would require to avoid this confusion, if not then I would at least note it in documentation. Great UDF though!

*** Matt @ MPCS

<{POST_SNAPBACK}>

I wish there was Matt, but I thought I explained myself clearly enough when I said:

The parameter this function takes should be unique, but not generated randomly. It must be the same between all versions of the script, but it must be different from any other scripts. The longer and more random it is, the less likely to conflict with anything else (But again, it can't be randomly generated at run-time or it will defeat the purpose).

Share this post


Link to post
Share on other sites

Your an inspiration, Valik. :)

Share this post


Link to post
Share on other sites

I wish there was Matt, but I thought I explained myself clearly enough when I said:

<{POST_SNAPBACK}>

I see how you would think that it is covered, in fact I almost answered ezzetabi's question because I was thinking the same thing. It seems obvious that it isn't as clear to others as it is to you and me.

*** Matt @ MPCS

Share this post


Link to post
Share on other sites

a little question. on a terminal server with client wan to open the same script at same time. the kernel will respond the error ? and the second script will not openthat righr ... or the user got access of a copie for himself of the kernel32.dll i don't think soo! and if the respond is yes how to make one work. with dllcall fucntion? thx


GreenseedMCSE+I, CCNA, A+Canada, QuebecMake Love Around You.

Share this post


Link to post
Share on other sites

a little question. on a terminal server with client wan to open the same script at same time. the kernel will respond the error ? and the second script will not openthat righr ... or the user got access of a copie for himself of the kernel32.dll i don't think soo! and if the respond is yes how to make one work. with dllcall fucntion? thx

<{POST_SNAPBACK}>

I have no clue what you are asking or trying to say...

Share this post


Link to post
Share on other sites

Oh, ezzetabi, I finally see it in the documentation for CreateSemaphore. The backslash is an illegal character in a Semaphore name, which is why @ScriptFullPath fails as a valid Semaphore name; the path contains backslashes.

Share this post


Link to post
Share on other sites

Unpleasant as usual Valik. This second answer have a meaning since I was using @scriptfullpath because I created a script that works in its folder and its subfolders, but I wanted to use it at the same time other copies in other folders.

Indeed, I have nothing agaist you, but you should think that others people are not always clueless AND stupid AND do not read as you seems to think.

Btw, I also tried:

Sleep(10000)

Func OnAutoItStart()
 SingleTon('abcabc')
EndFunc ;==>OnAutoItStart

Func SingleTon($SEMAPHORE)
  Local $ERROR_ALREADY_EXISTS = 183
  DllCall ("kernel32.dll", "int", "CreateSemaphore", "int", 0, "long", 1, "long", 1, "str", $SEMAPHORE)
  Local $LASTERROR = DllCall ("kernel32.dll", "int", "GetLastError")
  If $LASTERROR[0] = $ERROR_ALREADY_EXISTS Then Exit -1
EndFunc ;==>Singleton

and

Sleep(10000)

Func OnAutoItStart()
 SingleTon(123123)
EndFunc ;==>OnAutoItStart

Func SingleTon($SEMAPHORE)
  Local $ERROR_ALREADY_EXISTS = 183
  DllCall ("kernel32.dll", "int", "CreateSemaphore", "int", 0, "long", 1, "long", 1, "str", $SEMAPHORE)
  Local $LASTERROR = DllCall ("kernel32.dll", "int", "GetLastError")
  If $LASTERROR[0] = $ERROR_ALREADY_EXISTS Then Exit -1
EndFunc ;==>Singleton

and it does not work either. So for what can I see your script simply does not work.

Share this post


Link to post
Share on other sites

Unpleasant as usual Valik. This second answer have a meaning since I was using @scriptfullpath because I created a script that works in its folder and its subfolders, but I wanted to use it at the same time other copies in other folders.

Indeed, I have nothing agaist you, but you should think that others people are not always clueless AND stupid AND do not read as you seems to think.

Btw, I also tried:

Sleep(10000)

Func OnAutoItStart()
 SingleTon('abcabc')
EndFunc;==>OnAutoItStart

Func SingleTon($SEMAPHORE)
  Local $ERROR_ALREADY_EXISTS = 183
  DllCall ("kernel32.dll", "int", "CreateSemaphore", "int", 0, "long", 1, "long", 1, "str", $SEMAPHORE)
  Local $LASTERROR = DllCall ("kernel32.dll", "int", "GetLastError")
  If $LASTERROR[0] = $ERROR_ALREADY_EXISTS Then Exit -1
EndFunc;==>Singleton

and

Sleep(10000)

Func OnAutoItStart()
 SingleTon(123123)
EndFunc;==>OnAutoItStart

Func SingleTon($SEMAPHORE)
  Local $ERROR_ALREADY_EXISTS = 183
  DllCall ("kernel32.dll", "int", "CreateSemaphore", "int", 0, "long", 1, "long", 1, "str", $SEMAPHORE)
  Local $LASTERROR = DllCall ("kernel32.dll", "int", "GetLastError")
  If $LASTERROR[0] = $ERROR_ALREADY_EXISTS Then Exit -1
EndFunc;==>Singleton

and it does not work either. So for what can I see your script simply does not work.

<{POST_SNAPBACK}>

Valik is never cruel... just brutally honest.

*** Matt @ MPCS

Share this post


Link to post
Share on other sites

From what I can tell, my script works perfectly fine. Even using your examples, including attempting to run mixed compiled and uncompiled versions of the same script. Only the first instance runs, any other attempt to run a compile/non-compiled version dies out immediately.

However, if your goal is to run multiple copies of the same script from different locations, why the hell do you need to use the singleton pattern at all?

As for treating you like you don't know what you're doing, lets review the facts, shall we?

  • I explain how to use this function.
  • You show an example of using the function and it doesn't work.
  • You aren't using the function as per my description of how to use it.
  • You do not explain why you are not using it like I said.
If you don't want somebody to get on you about incorrectly using something, then explain to them why you are using it in a different manner than they describe. Otherwise, you get what you got.

Share this post


Link to post
Share on other sites

Valik is never cruel... just brutally honest.

*** Matt @ MPCS

<{POST_SNAPBACK}>

There's a difference between "never cruel" and "not cruel yet". :)

Share this post


Link to post
Share on other sites

From what I can tell, my script works perfectly fine.  Even using your examples, including attempting to run mixed compiled and uncompiled versions of the same script.  Only the first instance runs, any other attempt to run a compile/non-compiled version dies out immediately.

However, if your goal is to run multiple copies of the same script from different locations, why the hell do you need to use the singleton pattern at all?

As for treating you like you don't know what you're doing, lets review the facts, shall we? 

  • I explain how to use this function. 

  • You show an example of using the function and it doesn't work. 

  • You aren't using the function as per my description of how to use it. 

  • You do not explain why you are not using it like I said.
If you don't want somebody to get on you about incorrectly using something, then explain to them why you are using it in a different manner than they describe.  Otherwise, you get what you got.

<{POST_SNAPBACK}>

Is there a chance that the API call was changed between your versions of Windows? I know ezzetabi said he was running WinXP SP2... Valik is your testing platform the same? Just a thought, I haven't seen it done since Win3.1 -> Win95 but MS has done stranger things.

*** Matt @ MPCS

Share this post


Link to post
Share on other sites

The function is standard in all versions of Windows. I use XP Pro SP2.

Share this post


Link to post
Share on other sites

#17 ·  Posted (edited)

Hello! Someone suggested I use this _Singleton UDF but, of course, I dont' want to do so blindly especially since it is beyond the realm of my knowledge. I'm *just* above "newbie" status :P

I just noticed there hasn't been any discussion on this in over a year, and that now the whole CreateSemaphore line in the misc file is commented out (does that mean the guy that didn't know what he was talking about was on to something?). i don't require any explanation I can't understand (which is likely anything you'll write) so all I want to know is if this _Singleton is reliable and safe to use for the purpose of discarding multiple instances of the same AutoIt executable? Or does it have bugs? Will it work on all versions of Windows?

Thanks!

- Steven

ps. to be sure we're on the same page, this is the code for the function in question:

;===============================================================================
;
; Description:  _Singleton
; Parameter(s): $occurenceName
;              $flag
; User CallTip:   _Singleton($occurenceName [,$flag = 0]) Check if no other occurence is running. (required: <Misc.au3>)
; Return Value(s):  if $flag = 1
; Author(s):      Jason Boggs <vampire dot valik at gmail dot com>
;
;===============================================================================

Func _Singleton($occurenceName, $flag=0)
    Local $ERROR_ALREADY_EXISTS = 183
    $occurenceName=StringReplace($occurenceName,"\", ""); to avoid error
    
;   Local $handle = DllCall("kernel32.dll", "int", "CreateSemaphore", "int", 0, "long", 1, "long", 1, "str", $occurenceName)
    Local $handle = DllCall("kernel32.dll", "int", "CreateMutex", "int", 0, "long", 1, "str", $occurenceName)
    Local $lastError = DllCall("kernel32.dll", "int", "GetLastError")
    If $lastError[0] = $ERROR_ALREADY_EXISTS Then
        If $flag = 0 Then
            Exit -1
        Else
            SetError($lastError[0])
            Return 0
        EndIf
    EndIf

    Return  $handle[0]
EndFunc; _Singleton()
Edited by s_mack

Share this post


Link to post
Share on other sites

Hello! Someone suggested I use this _Singleton UDF but, of course, I dont' want to do so blindly especially since it is beyond the realm of my knowledge. I'm *just* above "newbie" status :P

I just noticed there hasn't been any discussion on this in over a year, and that now the whole CreateSemaphore line in the misc file is commented out (does that mean the guy that didn't know what he was talking about was on to something?). i don't require any explanation I can't understand (which is likely anything you'll write) so all I want to know is if this _Singleton is reliable and safe to use for the purpose of discarding multiple instances of the same AutoIt executable? Or does it have bugs? Will it work on all versions of Windows?

Thanks!

- Steven

ps. to be sure we're on the same page, this is the code for the function in question:

;===============================================================================
;
; Description:  _Singleton
; Parameter(s): $occurenceName
;              $flag
; User CallTip:   _Singleton($occurenceName [,$flag = 0]) Check if no other occurence is running. (required: <Misc.au3>)
; Return Value(s):  if $flag = 1
; Author(s):      Jason Boggs <vampire dot valik at gmail dot com>
;
;===============================================================================

Func _Singleton($occurenceName, $flag=0)
    Local $ERROR_ALREADY_EXISTS = 183
    $occurenceName=StringReplace($occurenceName,"\", ""); to avoid error
    
;   Local $handle = DllCall("kernel32.dll", "int", "CreateSemaphore", "int", 0, "long", 1, "long", 1, "str", $occurenceName)
    Local $handle = DllCall("kernel32.dll", "int", "CreateMutex", "int", 0, "long", 1, "str", $occurenceName)
    Local $lastError = DllCall("kernel32.dll", "int", "GetLastError")
    If $lastError[0] = $ERROR_ALREADY_EXISTS Then
        If $flag = 0 Then
            Exit -1
        Else
            SetError($lastError[0])
            Return 0
        EndIf
    EndIf

    Return  $handle[0]
EndFunc; _Singleton()
as you have the code did you look at the doc of the official release for the used functions?

I this specific case not so may DLLCall, SetError, StringReplace and Func.

I am for sure you could have found out that's this function work any release.

For the commented line it was a modification I did to Valik code when I proposed to include it iin the standard "Misc.au3". :lmao:

Share this post


Link to post
Share on other sites

as you have the code did you look at the doc of the official release for the used functions?

I this specific case not so may DLLCall, SetError, StringReplace and Func.

I am for sure you could have found out that's this function work any release.

For the commented line it was a modification I did to Valik code when I proposed to include it iin the standard "Misc.au3". :P

Can you not see where I said I was a newbie!??! Lol... no, of course I can't tell (whatever it is you just mumbled out) from looking at the code because I have no idea how it works. How would I have found out that the function works for any release of Windows?? Sure, I looked at the help file - it doesn't say that.

Anyway (frustrates me to no end when people look down at you for no reason other than to feel geek-superiority) does that mean that yes, it is safe and stable in all releases of Windows?

- Steven

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0