Jump to content

Watching for a Mutex


Recommended Posts

Here is what I am trying to do:

Script2 runs and I want Script1 to wait until Script2 is complete before it continues with execution. I know I could just use ProcessExists() to do this but the problem is the compiled name will be diffrent 99% of the time so I was just going to have my apps create a mutex and watch for that mutex to go away.

The problem is when Script2 closes after 3 seconds. Script 1 is still seeing the mutex. Any ideas?

Here is the code:

Script1

CheckWDTCode()
MsgBox(0,0,"Mutex Not Found")

Func CheckWDTCode()
    $MutexName = StringReplace("WDTCodeRunning", "\", "")
    Local $handle = DllCall("kernel32.dll", "int", "CreateMutex", "int", 0, "long", 1, "str", $MutexName)
    Local $lastError = DllCall("kernel32.dll", "int", "GetLastError")
    If $lastError[0] = 183 Then
        ConsoleWrite("Mutex Found" & @CRLF)
        Sleep(1500)
        CheckWDTCode()
    EndIf
EndFunc

Script2

CheckWDTCode()
Sleep(3000)

Func CheckWDTCode()
    $MutexName = StringReplace("WDTCodeRunning", "\", "")
    Local $handle = DllCall("kernel32.dll", "int", "CreateMutex", "int", 0, "long", 1, "str", $MutexName)
    Local $lastError = DllCall("kernel32.dll", "int", "GetLastError")
    If $lastError[0] = 183 Then
        Sleep(1500)
        CheckWDTCode()
    EndIf
EndFunc

[sub]Quantum mechanics: The dreams stuff is made of[/sub]

Link to comment
Share on other sites

Use WinWaitNotActive(). It uses the windows title not the program name, so even if you rename the exe it still works.

Doh.. WinExists()

Edited by John
Link to comment
Share on other sites

Use WinWaitNotActive(). It uses the windows title not the program name, so even if you rename the exe it still works.

Doh.. WinExists()

Not looking for a distinct window name. This will be multiple scripts, multiple exes.

[sub]Quantum mechanics: The dreams stuff is made of[/sub]

Link to comment
Share on other sites

You can use WinSetTitle() to a certain text if the program has thread control then switch it back when released. This method wouldn't even require the program to actually exit, though it could change title before exiting. The title would just be a flag that determined thread ownership and all other scripts must wait on it to clear.

Link to comment
Share on other sites

You can use WinSetTitle() to a certain text if the program has thread control then switch it back when released. This method wouldn't even require the program to actually exit, though it could change title before exiting. The title would just be a flag that determined thread ownership and all other scripts must wait on it to clear.

Okay... Thats an idea, but I can not do that. This is for an enterprise solution with multiple devs creating install packages. I am working on a function that they would add in their scripts that would create a mutex. That way, only one script will run at a time... (Basically an installation monitor for dev created packages.)

[sub]Quantum mechanics: The dreams stuff is made of[/sub]

Link to comment
Share on other sites

That definately complicates the situation. If it's a function they must add to their script it could still be made to work. Although I'm not sure of you particular situation. You could also create a script they must call to execute their script. Your script would simply take the command line and run their script for them. Your script would then know what process to wait on to exit before it exited. In the meantime it would just loop a wait state. It wouldn't be the Dev script other scripts must wait on but the script needed to run it.

I made a media player implementation that did something similar. When it was playing a movie and another instance tried to open it would add that new movie to a que in a config file and exit. When the first instance finshed it automatically played the second movie and so on.

Link to comment
Share on other sites

This is what I am doing:

Opt("OnExitFunc", "ExitScript")

Dim $MutexHandle

CheckWDTCode()

MsgBox(0,0,"Mutex Not Found")

Func CheckWDTCode()

$MutexName = StringReplace("WDTCodeRunning", "\", "")

Local $dll = DllOpen("kernel32.dll")

$MutexHandle = DllCall($dll, "long", "CreateMutexA", "ptr", 0, "int", 1, "str", $MutexName)

Local $lastError = DllCall($dll, "int", "GetLastError")

DllClose($dll)

If $lastError[0] = 183 Then

ConsoleWrite("Mutex Found" & @CRLF)

Sleep(1500)

CheckWDTCode()

EndIf

EndFunc

Func ExitScript()

DllCall("kernel32.dll", "int", "ReleaseMutex", "hwnd", $MutexHandle[1])

EndFunc

The Mutex is still not Releasing.

[sub]Quantum mechanics: The dreams stuff is made of[/sub]

Link to comment
Share on other sites

  • Moderators

Singleton will close the script. I need the script to loop until that mutex is closed.

_SingleTon will close the script if you use that option. You will see that _SingleTon in fact uses the CreateMutex method as well.

OnExit you will need to release the Mutex as described above.

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

That is basically where I got the code from. But for some reason when the two scripts are running, script1 watching the mutex script2 creating it, when script 2 closes and calles the ReleaseMutex funtion, the mutex does not clear until the first script closes as well.

[sub]Quantum mechanics: The dreams stuff is made of[/sub]

Link to comment
Share on other sites

  • Moderators

That is basically where I got the code from. But for some reason when the two scripts are running, script1 watching the mutex script2 creating it, when script 2 closes and calles the ReleaseMutex funtion, the mutex does not clear until the first script closes as well.

Playing around with it:
Global $hMutexGlobal

ConsoleWrite("Mutex exists: " & (_MutexExists("WDTCodeRunning") = 1) & @CRLF)
$hMutexGlobal = _CreateMutex("WDTCodeRunning")
ConsoleWrite("Mutex Handle: " & $hMutexGlobal & @CRLF)
ConsoleWrite("Mutex exists: " & (_MutexExists("WDTCodeRunning") = 1) & @CRLF)
ConsoleWrite("Mutex Released: " &  (_ReleaseMutex($hMutexGlobal) = 1) & @CRLF)
ConsoleWrite("Mutex exists: " & (_MutexExists("WDTCodeRunning") = 1) & @CRLF)


Func _MutexExists($szMutexName)
    Local $hMutex = DllCall("Kernel32.dll", "hwnd", "OpenMutex", "int", 0x1F0001, "int", 1, "str", $szMutexName)
    Local $aGLE = DllCall("Kernel32.dll", "int", "GetLastError")
    If IsArray($aGLE) And $aGLE[0] = 127 Then Return 1
    Return 0
EndFunc   ;==>_MutexExists

Func _CreateMutex($szMutexName)
    Local $hMutex = DllCall("Kernel32.dll", "hwnd", "CreateMutex", "ptr", 0, "int", 1, "str", $szMutexName)
    Local $aGLE = DllCall("Kernel32.dll", "int", "GetLastError")
    If $aGLE[0] = 183 Then Return 0
    Return $hMutex[0]
EndFunc   ;==>_CreateMutex

Func _ReleaseMutex($hMutex)
    Local $aRM = DllCall("kernel32.dll", "int", "ReleaseMutex", "hwnd", $hMutex)
    If IsArray($aRM) And $aRM[0] > 0 Then Return 1
    Return 0
EndFunc   ;==>ExitScript
This might make it easier to figure out what you need.

I wouldn't loop inside functions with a recursive call, just do While condition etc... outside of it.

I'll look a bit more to see if I can figure out why it is releasing but still showing.

Edited by SmOke_N

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

  • Moderators

Here, this seems to work:

Global $hMutexGlobal

ConsoleWrite("Mutex exists: " & (_MutexExists("WDTCodeRunning") = 1) & @CRLF)
$hMutexGlobal = _CreateMutex("WDTCodeRunning")
ConsoleWrite("Mutex Handle: " & $hMutexGlobal & @CRLF)
ConsoleWrite("Mutex exists: " & (_MutexExists("WDTCodeRunning") = 1) & @CRLF)
ConsoleWrite("Mutex Released: " &  (_ReleaseMutex($hMutexGlobal) = 1) & @CRLF)
ConsoleWrite("Mutex exists: " & (_MutexExists("WDTCodeRunning") = 1) & @CRLF)


Func _MutexExists($szMutexName)
    Local $hMutex = DllCall("Kernel32.dll", "hwnd", "OpenMutex", "int", 0x1F0001, "int", 1, "str", $szMutexName)
    Local $aGLE = DllCall("Kernel32.dll", "int", "GetLastError")
    If IsArray($hMutex) And $hMutex[0] Then
        DllCall("Kernel32.dll", "int", "CloseHandle", "hwnd", $hMutex[0])
    EndIf
    If IsArray($aGLE) And $aGLE[0] = 127 Then Return 1
    Return 0
EndFunc   ;==>_MutexExists

Func _CreateMutex($szMutexName)
    Local $hMutex = DllCall("Kernel32.dll", "hwnd", "CreateMutex", "ptr", 0, "int", 1, "str", $szMutexName)
    Local $aGLE = DllCall("Kernel32.dll", "int", "GetLastError")
    If (IsArray($hMutex) = 0) Or (IsArray($aGLE) And $aGLE[0] = 183) Then Return 0
    Return $hMutex[0]
EndFunc   ;==>_CreateMutex

Func _ReleaseMutex($hMutex)
    Local $aRM = DllCall("kernel32.dll", "int", "ReleaseMutex", "hwnd", $hMutex)
    Local $aCH = DllCall("Kernel32.dll", "int", "CloseHandle", "hwnd", $hMutex)
    If (IsArray($aRM) And $aRM[0] > 0) And (IsArray($aCH) And $aCH[0] > 0) Then Return 1
    Return 0
EndFunc   ;==>ExitScript

Edited by SmOke_N

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

Singleton will close the script. I need the script to loop until that mutex is closed.

No, it only exits the script if you elect to do that with the second parameter.

_Singleton($sOccurenceName[, $iFlag = 0])

$iFlag

0 - Exit if occurrence already exists (default)

1 - Return if occurrence already exists

2 - Allow the object to be accessed by anybody in the system. This is useful if specifying a "Global\" object in a multi-user environment.

You seem to have ignored what SmOke_N told you to begin with -- If you set the second parameter correctly (i.e. 1 - read the help file!) _Singleton() will only return True or False, and could be run in a loop until it received True (only one instance running). Did I miss something special about your circumstances?

:)

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

  • Moderators

No, it only exits the script if you elect to do that with the second parameter.

You seem to have ignored what SmOke_N told you to begin with -- If you set the second parameter correctly (i.e. 1 - read the help file!) _Singleton() will only return True or False, and could be run in a loop until it received True (only one instance running). Did I miss something special about your circumstances?

:)

I can see it being handy to create your own mutex for this issue. Since _SingleTon does not return the handle of the Mutex created, only a boolean, there's no way to close the handle until you exit the app and AutoIt does it for you.

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

I can see it being handy to create your own mutex for this issue. Since _SingleTon does not return the handle of the Mutex created, only a boolean, there's no way to close the handle until you exit the app and AutoIt does it for you.

I see what you mean, I think.

I'm pretty sure the whole point of how _Singleton() works is that only one Mutex is created, no matter how many instances are running. All instances after the first detect their status by the FAILURE to create another Mutex with the same name. This means if there are three or more instances running, then when the first instance closes, the next remaining instance will succeed in creating the Mutex and return True from _Singleton(), even if there are still other instances running.

When I get a chance I'll test that with a demo script.

Learning something new every day...

:)

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

  • Moderators

I see what you mean, I think.

I'm pretty sure the whole point of how _Singleton() works is that only one Mutex is created, no matter how many instances are running. All instances after the first detect their status by the FAILURE to create another Mutex with the same name. This means if there are three or more instances running, then when the first instance closes, the next remaining instance will succeed in creating the Mutex and return True from _Singleton(), even if there are still other instances running.

When I get a chance I'll test that with a demo script.

Learning something new every day...

:)

If that's how it is intended, that's not how it's written:

; #FUNCTION# ====================================================================================================================
; Name...........: _Singleton
; Description ...: Check if no other occurence is running
; Syntax.........: _Singleton($sOccurenceName[, $iFlag = 0])
; Parameters ....: $sOccurenceName - String to identify the occurrence of the script.  This string may not contain any \ characters unless you are placing the object in a namespace.
;                  $iFlag          - Action if @error
;                  |0 - Exit if occurrence already exists
;                  |1 - Return if occurrence already exists
;                  |2 - Allow the object to be accessed by anybody in the system.  This is useful if specifying a "Global\" object in a multi-user environment.
; Return values .: Success      - 1
;                  Failure      - 0
; Author ........: Valik
; Modified.......:
; Remarks .......: You can place the object in a namespace by prefixing your object name with either "Global\" or "Local\".  "Global\" objects combined with the flag 2 are useful in multi-user environments.
; Related .......:
; Link ..........;
; Example .......; Yes
; ===============================================================================================================================
Func _Singleton($sOccurenceName, $iFlag = 0)
    Local Const $ERROR_ALREADY_EXISTS = 183
    Local Const $SECURITY_DESCRIPTOR_REVISION = 1
    Local $handle, $lastError, $pSecurityAttributes = 0

    If BitAND($iFlag, 2) Then
        ; The size of SECURITY_DESCRIPTOR is 20 bytes.  We just
        ; need a block of memory the right size, we aren't going to
        ; access any members directly so it's not important what
        ; the members are, just that the total size is correct.
        Local $structSecurityDescriptor = DllStructCreate("dword[5]")
        Local $pSecurityDescriptor = DllStructGetPtr($structSecurityDescriptor)
        ; Initialize the security descriptor.
        Local $aRet = DllCall("advapi32.dll", "int", "InitializeSecurityDescriptor", _
                "ptr", $pSecurityDescriptor, "dword", $SECURITY_DESCRIPTOR_REVISION)
        If Not @error And $aRet[0] Then
            ; Add the NULL DACL specifying access to everybody.
            $aRet = DllCall("advapi32.dll", "int", "SetSecurityDescriptorDacl", _
                    "ptr", $pSecurityDescriptor, "int", 1, "ptr", 0, "int", 0)
            If Not @error And $aRet[0] Then
                ; Create a SECURITY_ATTRIBUTES structure.
                Local $structSecurityAttributes = DllStructCreate("dword;ptr;int")
                ; Assign the members.
                DllStructSetData($structSecurityAttributes, 1, DllStructGetSize($structSecurityAttributes))
                DllStructSetData($structSecurityAttributes, 2, $pSecurityDescriptor)
                DllStructSetData($structSecurityAttributes, 3, 0)
                ; Everything went okay so update our pointer to point to our structure.
                $pSecurityAttributes = DllStructGetPtr($structSecurityAttributes)
            EndIf
        EndIf
    EndIf

    $handle = DllCall("kernel32.dll", "int", "CreateMutex", "ptr", $pSecurityAttributes, "long", 1, "str", $sOccurenceName)
    $lastError = DllCall("kernel32.dll", "int", "GetLastError")
    If $lastError[0] = $ERROR_ALREADY_EXISTS Then
        If BitAND($iFlag, 1) Then
            Return SetError($lastError[0], $lastError[0], 0)
        Else
            Exit -1
        EndIf
    EndIf
    Return $handle[0]
EndFunc   ;==>_Singleton
Edited by SmOke_N

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...