Jump to content

Restart Script UDF clashes with Singleton


olympus
 Share

Recommended Posts

I'm trying out the restart script UDF by UP_NORTH: http://www.autoitscript.com/forum/index.php?showtopic=19370&view=findpost&p=199608

but when I add the Singleton UDF to my script, the restart function will stop working after a few tries. Is this expected?

#include <Misc.au3>

_Singleton(@ScriptFullPath)

While 1
    MsgBox(64,"test","123")
    _restart()
WEnd

Func _restart()
    If @Compiled = 1 Then
        Run( FileGetShortName(@ScriptFullPath))
    Else
        Run( FileGetShortName(@AutoItExe) & " " & FileGetShortName(@ScriptFullPath))
    EndIf
    Exit
EndFunc
Link to comment
Share on other sites

Think about it.

You start a new, identical script before the first one has exited, so both are running at the same time, hence clashing by that _Singleton.

I have two suggestions to fix it, delay the _Singleton so they won't clash, or don't restart the thing. Why are you doing that anyway?

Edit:clarification

Edited by AdmiralAlkex
Link to comment
Share on other sites

Think about it.

You start a new, identical script before the first one has exited, so both are running at the same time, hence clashing by that _Singleton.

I have two suggestions to fix it, delay the _Singleton so they won't clash, or don't restart the thing. Why are you doing that anyway?

Edit:clarification

When I change the script's settings, I need it to restart for the new settings to take effect, is there another way to restart the script that would work with singleton easily?

Link to comment
Share on other sites

Easiest would probably be to delay the _Singleton, maybe like this:

Sleep(1000)
_Singleton(@ScriptFullPath)

If you want it more userfriendly then I suggest you check for a specific command line parameter that you use when restarting, and only do the Sleep() when necessary.

Although the best would be if you removed that annoying dependancy on restarting the script. It's your choice.

Edited by AdmiralAlkex
Link to comment
Share on other sites

Checking the helpfile...

$sOccurenceName = String to identify the occurrence of the script. This string may not contain the \ character unless you are placing the object in a namespace (See Remarks).

@ScriptFullPath for sure contains a \ character...

Link to comment
Share on other sites

Something like this...

#include <Misc.au3>

If $CMDLINE[0] > 0 then 
    For $i = 1 to $CMDLINE[0]
        If StringLeft($CMDLINE[$i],5) = "/PID:" then 
            _WaitForProcessExit(StringTrimLeft($CMDLINE[$i],5))
            Exitloop
        EndIf
    Next
    
EndIf


_Singleton(@ScriptFullPath)

While 1
    MsgBox(64,"test","123")
    _restart()
WEnd

Func _restart()
    If @Compiled = 1 Then
        Run( FileGetShortName(@ScriptFullPath) & " /PID:" & @AutoItPID)
    Else
        Run( FileGetShortName(@AutoItExe) & " " & FileGetShortName(@ScriptFullPath)  & " /PID:" & @AutoItPID )
    EndIf
    Exit
EndFunc


Func _WaitForProcessExit($iPid)
    While ProcessExists($iPid)
        Sleep(100)
    WEnd
EndFunc
Link to comment
Share on other sites

I think this should be the best way:

#include<Misc.au3>
#include<WinAPI.au3>

; Create a new GUID for each script (e.g. use www.guidgen.com)
Global Const $_ghSINGLETONMUTEX = _Singleton('30d10c01-c33b-4074-95cd-62242c3e78ac', 1) ; use a GUID to identify script
If Not $_ghSINGLETONMUTEX Then Exit 1 ; If Script is already running Exit

... 
...

Func _Restart()
    _WinAPI_CloseHandle($_ghSINGLETONMUTEX) ; free Singelton
    ShellExecute(@AutoItExe, $CMDLINERAW, @WorkingDir) ; run Script
    Exit ; Exit
EndFunc
Edited by ProgAndy

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

I think this should be the best way:

Great idea to use a GUID ;) ...

Hmmmm, still got another problem here... singleton always returns the same number, regardless if another instance is running. I dug into the function and added

ClipPut(_WinAPI_GetLastError() & @crlf & _WinAPI_GetLastErrorMessage())

diretly after the CreateMutex call, the response is:

126

The specified module could not be found.

Anyone knows what this could mean? Google didn't return an useful answer :evil:... Maybe it's related to my notebook setup, will retest at home... after work :evil:..

#cs ----------------------------------------------------------------------------

 AutoIt Version: 3.3.1.3 (beta)
 Author:         myName

 Script Function:
    Template AutoIt script.

#ce ----------------------------------------------------------------------------

; Script Start - Add your code below here

#include<Misc.au3>
#include<WinAPI.au3>

; Create a new GUID for each script (e.g. use www.guidgen.com)
Global Const $_ghSINGLETONMUTEX = _SingletonEx('30d10c01-c33b-4074-95cd-62242c3e78ac', 1) ; use a GUID to identify script
MsgBox(0,"",$_ghSINGLETONMUTEX)
;If Not $_ghSINGLETONMUTEX Then Exit 1 ; If Script is already running Exit



; #FUNCTION# ====================================================================================================================
; Name...........: _Singleton
; Description ...: Enforce a design paradigm where only one instance of the script may be running.
; Syntax.........: _Singleton($sOccurenceName[, $iFlag = 0])
; Parameters ....: $sOccurenceName - String to identify the occurrence of the script.  This string may not contain the \ character unless you are placing the object in a namespace (See Remarks).
;                  $iFlag          - Behavior options.
;                  |0 - Exit the script with the exit code -1 if another instance already exists.
;                  |1 - Return from the function without exiting the script.
;                  |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      - The handle to the object used for synchronization (a mutex).
;                  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 _SingletonEx($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)

    ClipPut(_WinAPI_GetLastError() & @crlf & _WinAPI_GetLastErrorMessage())

    $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 KaFu
Link to comment
Share on other sites

Restart.au3 UDF

#Region Header

#cs

    Title:          Restart script UDF Library for AutoIt3
    Filename:       Restart.au3
    Description:    Accurate restarting the script (AU3 or EXE)
    Author:         Yashied
    Version:        1.0
    Requirements:   AutoIt v3.3 +, Developed/Tested on WindowsXP Pro Service Pack 2
    Uses:           None
    Notes:          The library uses OnAutoItStart() function

    Available functions:

    _ScriptRestart

    Example:

        #NoTrayIcon

        #Include <Misc.au3>
        #Include <Restart.au3>

        _Singleton('MyProgram')

        If MsgBox(36, 'Restarting...', 'Press OK to restart this script.') = 6 Then
            _ScriptRestart()
        EndIf

#ce

#Include-once

#EndRegion Header

#Region Local Variables and Constants

Global $__Restart = False

#EndRegion Local Variables and Constants

#Region Public Functions

; #FUNCTION# ====================================================================================================================
; Name...........: _ScriptRestart
; Description....: Initiates a restart of the current script.
; Syntax.........: _ScriptRestart ( [$fExit] )
;                  $fExit  - Specifies whether terminates the current script, valid values:
;                  |TRUE   - Terminates script. (Default)
;                  |FALSE  - Does not terminates script.
; Return values..: Success - 1 ($fExit = TRUE)
;                  Failure - 0 and sets the @error flag to non-zero.
; Author.........: Yashied
; Modified.......:
; Remarks........:
; Related .......:
; Link...........:
; Example........: Yes
; ===============================================================================================================================

Func _ScriptRestart($fExit = 1)

    Local $Pid

    If Not $__Restart Then
        If @compiled Then
            $Pid = Run(@ScriptFullPath & ' ' & $CmdLineRaw, @ScriptDir, Default, 1)
        Else
            $Pid = Run(@AutoItExe & ' "' & @ScriptFullPath & '" ' & $CmdLineRaw, @ScriptDir, Default, 1)
        EndIf
        If @error Then
            Return SetError(@error, 0, 0)
        EndIf
        StdinWrite($Pid, @AutoItPID)
;       If @error Then
;
;       EndIf
    EndIf
    $__Restart = 1
    If $fExit Then
        Sleep(50)
        Exit
    EndIf
    Return 1
EndFunc   ;==>_ScriptRestart

#EndRegion Public Functions

#Region OnAutoItStart

Func OnAutoItStart()

    Sleep(50)

    Local $Pid = ConsoleRead(1)

    If @extended Then
        While ProcessExists($Pid)
            Sleep(100)
        WEnd
    EndIf
EndFunc   ;==>OnAutoItStart

#EndRegion OnAutoItStart
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...