Jump to content

Enforce Single Instance of Program via WM_COPYDATA


KaFu
 Share

Recommended Posts

For an example on how to send and receive data between different instances of the same script and which passes commandline parameters to the first instance via WM_COPYDATA take a look at post 2.

Just a short snippet which came to my mind as the _Singleton() standard UDF failed on my work-notebook for me (CreateMutexW returns winapi-error 126 - "The specified module could not be found" :evil:, I just assume it interferes with one of the 72 ;) processes running).

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

    AutoIt Version: 3.3.0.0
    Author:         KaFu

    Script Function:
    _EnforceSingleInstance() function, Standard _Singleton() UDF failed on some machines for me

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

_EnforceSingleInstance('e15ff08b-84ac-472a-89bf-5f92db683165') ; any 'unique' string; created with http://www.guidgen.com/Index.aspx

MsgBox(0, "", "Test")

Func _EnforceSingleInstance($GUID_Program)
    If $GUID_Program = "" Then Return SetError(1,'',1)
    if IsHWnd(WinGetHandle($GUID_Program)) then
        MsgBox(0,"Test","Second instance, will exit now...")
        Exit
    EndIf
    AutoItWinSetTitle($GUID_Program)
    Return WinGetHandle($GUID_Program)
EndFunc   ;==>_EnforceSingleInstance

Should work as long as the key is really 'unique' (as unique as it gets :evil: ), and script is not complied as CUI (I'm not sure but I assume then there is no default AutoItWin).

Edited by KaFu
Link to comment
Share on other sites

And while playing around... here is a version which passes commandline parameters to first instance via WM_COPYDATA before exiting...

Compile and start multiple times with different commandline parameters to see whats happening...

#NoTrayIcon
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

$hwnd_AutoIt = _EnforceSingleInstance('e15ff08b-84ac-472a-89bf-5f92db683165') ; any 'unique' string; created with http://www.guidgen.com/Index.aspx
Opt("TrayIconHide", 1)

$hGUI = GUICreate("My GUI " & $hwnd_AutoIt,300,300,Default,Default) ; will create a dialog box that when displayed is centered
$label = GUICtrlCreateLabel($CmdLineRaw,10,10,300,100)
GUISetState(@SW_SHOW) ; will display an empty dialog box

ControlSetText($hwnd_AutoIt,'',ControlGetHandle($hwnd_AutoIt,'','Edit1'),$hGUI) ; to pass hWnd of main GUI to AutoIt default GUI
GUIRegisterMsg($WM_COPYDATA, "WM_COPYDATA")

While 1
    sleep(10)
    $msg = GUIGetMsg()
    If $msg = $GUI_EVENT_CLOSE Then ExitLoop
WEnd
GUIDelete()

Func _EnforceSingleInstance($GUID_Program = "")
    If $GUID_Program = "" Then Return
    $hwnd = WinGetHandle($GUID_Program)
    If IsHWnd($hwnd) Then
        $hwnd_Target = ControlGetText($hwnd,'',ControlGetHandle($hwnd,'','Edit1'))
        WM_COPYDATA_SendData(HWnd($hwnd_Target), $CmdLineRaw)
        Exit
    EndIf
    AutoItWinSetTitle($GUID_Program)
    Return WinGetHandle($GUID_Program)
EndFunc   ;==>_EnforceSingleInstance

Func WM_COPYDATA($hWnd, $MsgID, $wParam, $lParam)
    ; http://www.autoitscript.com/forum/index.php?showtopic=105861&view=findpost&p=747887
    ; Melba23, based on code from Yashied
    Local $tCOPYDATA = DllStructCreate("ulong_ptr;dword;ptr", $lParam)
    Local $tMsg = DllStructCreate("char[" & DllStructGetData($tCOPYDATA, 2) & "]", DllStructGetData($tCOPYDATA, 3))
    $msg = DllStructGetData($tMsg, 1)
    if $msg = " " then
        GUICtrlSetData($label, "")
    else
        GUICtrlSetData($label, DllStructGetData($tMsg, 1))
    endif
    Return 0
EndFunc   ;==>WM_COPYDATA

Func WM_COPYDATA_SendData($hWnd, $sData)
    If Not IsHWnd($hWnd) Then Return 0
    if $sData = "" then $sData = " "
    Local $tCOPYDATA, $tMsg
    $tMsg = DllStructCreate("char[" & StringLen($sData) + 1 & "]")
    DllStructSetData($tMsg, 1, $sData)
    $tCOPYDATA = DllStructCreate("ulong_ptr;dword;ptr")
    DllStructSetData($tCOPYDATA, 2, StringLen($sData) + 1)
    DllStructSetData($tCOPYDATA, 3, DllStructGetPtr($tMsg))
    $Ret = DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hWnd, "int", $WM_COPYDATA, "wparam", 0, "lparam", DllStructGetPtr($tCOPYDATA))
    If (@error) Or ($Ret[0] = -1) Then Return 0
    Return 1
EndFunc   ;==>WM_COPYDATA_SendData
Edited by KaFu
Link to comment
Share on other sites

Link to comment
Share on other sites

  • 3 months later...

Hey, this is a pretty neat workaround. I didn't even know about the 'AutoItWinSetTitle' function! Go figure. I like the WM_COPYDATA use too - I had intended on writing something that would pass parameters to an already-running program in the future. Now i don't have to do the work :(

Link to comment
Share on other sites

Hey, this is a pretty neat workaround.

Thanks for the praise :), nice to hear, especially from you :(.

This is how I balance the access to the Sqlite DB between searching and reporting instance in SMF (two instances of smf.exe running at the same time to give instant access to results).

And btw, a 4 star rating for this post? Someone didn't seem understand what's going on here :) ...

Edited by KaFu
Link to comment
Share on other sites

  • 5 months later...

I posted on the General Help and Support about getting your example of WM_COPYDATA to work in x64 and Yashied kindly replied with a solution, replace the following instances of "dword;dword;ptr" to "ulong_ptr;dword;ptr".

Here is the post.

UDF List:

 
_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_ArrayFilter/_ArrayReduce_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 22/04/2018

Link to comment
Share on other sites

  • 3 weeks later...
  • 10 years later...
On 12/15/2009 at 5:31 PM, KaFu said:

here is a version which passes commandline parameters to first instance via WM_COPYDATA before exiting

I'm really a bit late  :whistle: , but...
This 2nd code is a true gift. Still working, clever, extremely easy to implement and use in an existing script
Thanks  ^_^

Link to comment
Share on other sites

Test Script:

For $i=1 to 200
    Run(@ScriptDir&"\EnforceSingleInstance_86.exe")
    ConsoleWrite($i&@CRLF)

Next

 

Main script to compile:

#NoTrayIcon
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Outfile=EnforceSingleInstance_86.exe
#AutoIt3Wrapper_Outfile_x64=EnforceSingleInstance_64.exe
#AutoIt3Wrapper_Compile_Both=y
#AutoIt3Wrapper_UseX64=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <StaticConstants.au3>

Opt("MustDeclareVars", 1)
Global $sGUID = 'e15ff08b-84ac-472a-89bf-5f92db683165'
Global $gMsg, $hwnd_AutoIt = _EnforceSingleInstance($sGUID) ; any 'unique' string; created with http://www.guidgen.com/Index.aspx
Opt("TrayIconHide", 1)

Global $hGUI = GUICreate("My GUI " & $hwnd_AutoIt, 550, 150, -1, -1, -1, BitOR($WS_EX_TOPMOST, $WS_EX_WINDOWEDGE))
GUICtrlCreateLabel("CmdLineRaw:", 5, 60, 79, 60, BitOR($SS_RIGHT, $SS_CENTERIMAGE), $GUI_WS_EX_PARENTDRAG)
GUICtrlCreateLabel("Process PID:", 5, 5, 79, 17, BitOR($SS_RIGHT, $SS_CENTERIMAGE), $GUI_WS_EX_PARENTDRAG)
GUICtrlCreateLabel("GUID:", 6, 32, 79, 17, BitOR($SS_RIGHT, $SS_CENTERIMAGE), $GUI_WS_EX_PARENTDRAG)
GUICtrlCreateLabel(StringInStr(@OSArch, '64') ? "OS x64" : "OS x86", 192, 5, 52, 17, $SS_CENTERIMAGE, $GUI_WS_EX_PARENTDRAG)
GUICtrlCreateLabel(@AutoItX64 ? "Exe x64" : "EXE x86", 249, 5, 54, 17, $SS_CENTERIMAGE, $GUI_WS_EX_PARENTDRAG)
GUICtrlCreateLabel(@AutoItPID, 94, 5, 86, 17, $SS_CENTERIMAGE, $GUI_WS_EX_PARENTDRAG)
GUICtrlCreateLabel(IsAdmin() ? "Runing on Admin" : "Not Run as Admin", 318, 5, 94, 17, $SS_CENTERIMAGE, $GUI_WS_EX_PARENTDRAG)

Global $Label_CmdLineRaw = GUICtrlCreateLabel($CmdLineRaw, 94, 60, 450, 60, $SS_CENTERIMAGE, $GUI_WS_EX_PARENTDRAG)
Global $Label_GUID = GUICtrlCreateLabel($sGUID, 94, 32, 450, 17, $SS_CENTERIMAGE, $GUI_WS_EX_PARENTDRAG)
Global $Label_Time = GUICtrlCreateLabel("", 448, 5, 100, 17, BitOR($SS_CENTER, $SS_CENTERIMAGE), $GUI_WS_EX_PARENTDRAG)
Global $Label_Status = GUICtrlCreateLabel("", 5, 128, 535, 17, BitOR($SS_CENTER, $SS_CENTERIMAGE), $GUI_WS_EX_PARENTDRAG)

GUISetState(@SW_SHOW)

ControlSetText($hwnd_AutoIt, '', ControlGetHandle($hwnd_AutoIt, '', 'Edit1'), $hGUI) ; to pass hWnd of main GUI to AutoIt default GUI
GUIRegisterMsg($WM_COPYDATA, "WM_COPYDATA")
AdlibRegister("_Check_Process_Same_Name", 1000)

While 1
    Sleep(10)
    $gMsg = GUIGetMsg()
    If $gMsg = $GUI_EVENT_CLOSE Then ExitLoop
    GUICtrlSetData($Label_Time, @HOUR & ":" & @MIN & ":" & @SEC & ":" & @MSEC)
WEnd

GUIDelete()
Exit

Func _Check_Process_Same_Name()
    Local $ScriptName = @ScriptName
    If Not @Compiled Then $ScriptName = StringRegExpReplace(@AutoItExe, "^.*\\", "")
    ;ConsoleWrite("- Script Name: "&$ScriptName&@CRLF)
    Local $ProcessList = '', $aProc = ProcessList($ScriptName)
    For $i = 1 To $aProc[0][0]
        If $aProc[$i][1] <> @AutoItPID Then $ProcessList &= $aProc[$i][1] & " / "
    Next
    If $ProcessList <> "" Then
        GUICtrlSetData($Label_Status, "Process Same Name: " & $ProcessList)
    Else
        GUICtrlSetData($Label_Status, "")
    EndIf
    $ProcessList = ''
EndFunc   ;==>_Check_Process_Same_Name

Func _EnforceSingleInstance($GUID_Program = "")
    If $GUID_Program = "" Then Return
    Local $hwnd = WinGetHandle($GUID_Program)
    If IsHWnd($hwnd) Then
        Local $hwnd_Target = ControlGetText($hwnd, '', ControlGetHandle($hwnd, '', 'Edit1'))
        WM_COPYDATA_SendData(HWnd($hwnd_Target), $CmdLineRaw)
        Exit
    EndIf
    AutoItWinSetTitle($GUID_Program)
    Return WinGetHandle($GUID_Program)
EndFunc   ;==>_EnforceSingleInstance

Func WM_COPYDATA($hwnd, $MsgID, $wParam, $lParam)
    ; http://www.autoitscript.com/forum/index.php?showtopic=105861&view=findpost&p=747887
    ; Melba23, based on code from Yashied
    Local $tCOPYDATA = DllStructCreate("ulong_ptr;dword;ptr", $lParam)
    Local $tMsg = DllStructCreate("char[" & DllStructGetData($tCOPYDATA, 2) & "]", DllStructGetData($tCOPYDATA, 3))
    Local $xMsg = DllStructGetData($tMsg, 1)
    If StringStripWS($xMsg, 8) = "" Then
        GUICtrlSetData($Label_CmdLineRaw, "")
    Else
        GUICtrlSetData($Label_CmdLineRaw, $xMsg)
    EndIf
    Return 0
EndFunc   ;==>WM_COPYDATA

Func WM_COPYDATA_SendData($hwnd, $sData)
    If Not IsHWnd($hwnd) Then Return 0
    If $sData = "" Then $sData = " "
    Local $tMsg = DllStructCreate("char[" & StringLen($sData) + 1 & "]")
    DllStructSetData($tMsg, 1, $sData)
    Local $tCOPYDATA = DllStructCreate("ulong_ptr;dword;ptr")
    DllStructSetData($tCOPYDATA, 2, StringLen($sData) + 1)
    DllStructSetData($tCOPYDATA, 3, DllStructGetPtr($tMsg))
    Local $aRet = DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hwnd, "int", $WM_COPYDATA, "wparam", 0, "lparam", DllStructGetPtr($tCOPYDATA))
    If (@error) Or ($aRet[0] = -1) Then Return 0
    Return 1
EndFunc   ;==>WM_COPYDATA_SendData

 

Result:

Enforce-Single-Instance.png

 

 

I want to find a solution to "Execute a single instance" but the new session closes the old instance or the old one exits on its own.

Regards,
 

Link to comment
Share on other sites

Hi VIP

Does Opt(WinWaitDelay,'1') improve this?

edit: Not sure what I was thinking!! Ran it a few times with delay reduced and it behaved, just a coincidence.

edit2: I can't see how this method can truly work in VIP's test, the way I see it multiple CPU cycles are required to look for a window and rename it's own window if not found. Because of the process queue multiple processes can be taking turns to complete that task.

Need to use a mutex or semaphore (semaphore if you want to set max nth of running)

BTW. Singleton() (mutex method) has always worked for me.

Regards

Sjardz

Edited by Jardz
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...