Terenz

New _Singleton() need opinion

35 posts in this topic

#1 ·  Posted (edited)

Hello,

_Singleton is a controversy function, for unknow reason not work for everybody. The first version was using semaphore, the actual mutex. There is another alternative, CreateEvent.

If _SingletonEx("Global\MyApp", 1) = 0 Then
    MsgBox(16, "Warning", "An occurrence of test is already running")
    Exit
EndIf
MsgBox(64, "OK", "The first occurrence of test is running")

Func _SingletonEx($sOccurrenceName, $iFlag = 0)
    Local Const $ERROR_ALREADY_EXISTS = 183
    Local $hStartEvent = DllCall("kernel32.dll", "handle", "CreateEventW", "struct*", 0, "bool", False, "bool", False, "wstr", $sOccurrenceName)
    If @error Then Return SetError(@error, @extended, 0)
    Local $hError = DllCall("kernel32.dll", "dword", "GetLastError")
    If $hError[0] = $ERROR_ALREADY_EXISTS Then
        DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hStartEvent[0])
        If @error Then Return SetError(@error, @extended, 0)
        If BitAND($iFlag, 1) Then
            Return SetError($hError[0], $hError[0], 0)
        Else
            Exit -1
        EndIf
    EndIf
    Return $hStartEvent[0]
EndFunc   ;==>_SingletonEx

On my system work fine also without the $tSecurityAttributes for the Global\ since the switch between my two user recognize the old instance running, test also on your enviroment.

Please be gentle, i'm not so expert in structure and DllCall. If i have loose time and the actual _Singleton() is better then mine isn't a problem for me, I won't be offended :)

P.S. In thoery we need a CloseHandle for CreateEvent when the script has only 1 instance at exit. Since the original _Singleton() don't close the handle for the Mutex in the same situation and leave to do by Autoit at exit, i have do it the same.

Another one, with FileMapping

If _SingletonMap("Global\MyApp", 1) = 0 Then
    MsgBox(16, "Warning", "An occurrence of test is already running")
    Exit
EndIf
MsgBox(64, "OK", "The first occurrence of test is running")

Func _SingletonMap($sOccurrenceName, $iFlag = 0)
    Local Const $ERROR_ALREADY_EXISTS = 183
    Local $tInt64 = DllStructCreate('int64')
    Local $tQWord = DllStructCreate('dword;dword', DllStructGetPtr($tInt64))
    DllStructSetData($tInt64, 1, 1)
    Local $iSize_HiDWord = DllStructGetData($tQWord, 2), $iSize_LoDWord = DllStructGetData($tQWord, 1)
    Local $hStartEvent = DllCall('kernel32.dll', 'handle', 'CreateFileMappingW', 'handle', -1, 'struct*', 0, 'dword', 0x0004, 'dword', $iSize_HiDWord, 'dword', $iSize_LoDWord, 'wstr', $sOccurrenceName)
    If @error Then Return SetError(@error, @extended, 0)
    Local $hError = DllCall("kernel32.dll", "dword", "GetLastError")
    If $hError[0] = $ERROR_ALREADY_EXISTS Then
        DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hStartEvent[0])
        If @error Then Return SetError(@error, @extended, 0)
        If BitAND($iFlag, 1) Then
            Return SetError($hError[0], $hError[0], 0)
        Else
            Exit -1
        EndIf
    EndIf
    Return $hStartEvent[0]
EndFunc

With CreateWaitableTimer

If _SingletonWT("Global\MyApp", 1) = 0 Then
    MsgBox(16, "Warning", "An occurrence of test is already running")
    Exit
EndIf
MsgBox(64, "OK", "The first occurrence of test is running")

Func _SingletonWT($sOccurrenceName, $iFlag = 0)
    Local Const $ERROR_ALREADY_EXISTS = 183
    $hStartEvent = DllCall("kernel32.dll", "long", "CreateWaitableTimer", "long", 0, "long", True, "str", $sOccurrenceName)
    If @error Then Return SetError(@error, @extended, 0)
    Local $hError = DllCall("kernel32.dll", "dword", "GetLastError")
    If $hError[0] = $ERROR_ALREADY_EXISTS Then
        DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hStartEvent[0])
        If @error Then Return SetError(@error, @extended, 0)
        If BitAND($iFlag, 1) Then
            Return SetError($hError[0], $hError[0], 0)
        Else
            Exit -1
        EndIf
    EndIf
    Return $hStartEvent[0]
EndFunc   ;==>_SingletonEx

With Semaphore, like the original Valik function but improved with CloseHandle and error checking.

If _SingletonSemaphore("Global\MyApp", 1) = 0 Then
    MsgBox(16, "Warning", "An occurrence of test is already running")
    Exit
EndIf
MsgBox(64, "OK", "The first occurrence of test is running")

Func _SingletonSemaphore($sOccurrenceName, $iFlag = 0)
    Local Const $ERROR_ALREADY_EXISTS = 183
    $hStartEvent = DllCall('kernel32.dll', 'handle', 'CreateSemaphoreW', 'struct*', 0, 'long', 0, 'long', 1, 'wstr', $sOccurrenceName)
    If @error Then Return SetError(@error, @extended, 0)
    Local $hError = DllCall("kernel32.dll", "dword", "GetLastError")
    If $hError[0] = $ERROR_ALREADY_EXISTS Then
        DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hStartEvent[0])
        If @error Then Return SetError(@error, @extended, 0)
        If BitAND($iFlag, 1) Then
            Return SetError($hError[0], $hError[0], 0)
        Else
            Exit -1
        EndIf
    EndIf
    Return $hStartEvent[0]
EndFunc   ;==>_SingletonEx

This was made just for fun from C# code:

Global $iListenSingleton

If _SingletonTCP(12345, 1) = 0 Then
    MsgBox(16, "Warning", "An occurrence of test is already running")
    Exit
EndIf
MsgBox(64, "OK", "The first occurrence of test is running")

OnAutoItExitRegister("_SingletonExit")

Func _SingletonTCP($iOccurrence, $iFlag = 0)
    If Not IsNumber($iOccurrence) Then Return SetError(1, 0, 0)
    Local Const $WSAEADDRINUSE = 10048
    TCPStartup()
    $iListenSingleton = TCPListen("127.0.0.1", $iOccurrence)
    If @error = $WSAEADDRINUSE Then
        If BitAND($iFlag, 1) Then
            Return 0
        Else
            Exit -1
        EndIf
    EndIf
    Return 1
EndFunc   ;==>_SingletonTCP

Func _SingletonExit()
    TCPCloseSocket($iListenSingleton)
    TCPShutdown()
EndFunc   ;==>_SingletonExit

All version work globally on multi user enviroment.

Edited by Terenz

Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites



Where have you seen anything related to _Singleton not working for some people? There's nothing in the bugtracker about it, so I'm surious how you determined there was an issue?


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

I'll post one. From 2014:

Another example made on the first post using FileMapping.

Many API has ERROR_ALREADY_EXISTS there is plenty of choice. For example i don't have understood why "Semaphore" was replaced by "Mutex", i don't say one is better then the other but the principe is the same, check if ERROR_ALREADY_EXISTS.

EDIT: BrewManNH you took part in that discussion and ask to me? LOL :D

Edited by Terenz

Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites

And as I wrote in the second page of that thread, I have never been able to duplicate the problem that was mentioned. Write a script that demonstrates the issue for you, figure out where in the script it's failing, and report back. Otherwise, we'll have to guess that it's a computer issue and not a function issue.

Do a line by line filewrite of all variable, and object contents, and see if you can determine what's happening.

Creating another function that does the same thing as _Singleton doesn't help the situation for everyone.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

It does't do the same thing of _Singleton() only the principle is the same, the API is different. CreateWindowEx, that has nothing to do with _Singleton(), can return ERROR_ALREADY_EXISTS. There is also CreateFile, CreateProcess, CreateDirectory, CreateFileMapping ( see the example ) etc. i'll repeat plenty of API with that error code. Do you think a guy like @KaFu that actually ( or in the past ) have a problem with _Singleton() can't use any of that API above? We are forced to use CreateMutex ( why CreateMutex suppress CreateSemaphore? ) until the rest of our days? Is not better to switch with another API more "compatible" since the principle is the same?

I don't have that problem anyway on my machine but in the past on a friend PC yes, i wasn't able to debug i was too n00b :D

Edited by Terenz

Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

Added another example with CreateWaitableTimer and Semaphore, i think i have done with this also if there are other kernel object like jobs, namedpipe and so on. Some bat-signal for people can't make _Singleton() work in other threads of the forum:

@Morthawt, @iamtheky, @WhiteLion, @martin, @Bridawg, @therks, @jchd

You are not force to do nothing or post here, just for let you know. But if _Singleton() UDF not work it today and want to test one of my versions i'm glad and *maybe* i can make a ticket for replace-improve actual version.

I have imagined more participants to this thread :(

Edited by Terenz

Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites
On 25/10/2016 at 1:09 PM, Terenz said:

P.S. In thoery we need a CloseHandle for CreateEvent when the script has only 1 instance at exit. Since the original _Singleton() don't close the handle for the Mutex in the same situation and leave to do by Autoit at exit, i have do it the same.

After this i'll stop to talk by myself. This is what i mean with that quote, added a GUI just for change.

#include <GUIConstantsEx.au3>

Local $aSingleton[2] = [0, "Global\MyApp"] ; declare 1D array for hWnd and OccurranceName

Local $iResult = _SingletonRead($aSingleton, 1)
If @error Then
    MsgBox(16, "DEBUG", "An error occurred" & @CRLF & "ERROR: " & @error & @CRLF & "EXTENDED: " & @extended)
EndIf
If $iResult = 0 Then ; check if already semaphore exist
    MsgBox(16, "Warning", "An occurrence of test is already running")
    Exit
EndIf

_SingletonInit($aSingleton) ; initiate new singleton
If @error Then
    MsgBox(16, "DEBUG", "An error occurred" & @CRLF & "ERROR: " & @error & @CRLF & "EXTENDED: " & @extended)
EndIf

Local $hGUI = GUICreate("_Singleton", 200, 150, -1, -1)
GUICtrlCreateLabel("START ANOTHER INSTANCE", 23, 65)
GUISetState(@SW_SHOW)

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            _SingletonRelease($aSingleton) ; release the resource
            GUIDelete($hGUI)
            Exit
    EndSwitch
WEnd

Func _SingletonRead($aOccurrenceName, $iFlag = 0)
    Local $hOpenSemaphore = DllCall('kernel32.dll', 'handle', 'OpenSemaphoreW', 'dword', 0x001F0003, 'bool', 0, 'wstr', $aOccurrenceName[1])
    If @error Then Return SetError(@error, @extended, -1)
    If $hOpenSemaphore[0] = 0 Then Return 1
    DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hOpenSemaphore[0])
    If @error Then Return SetError(@error, @extended, 0)
    If BitAND($iFlag, 1) Then
        Return 0
    Else
        Exit -1
    EndIf
EndFunc   ;==>_SingletonRead

Func _SingletonInit(ByRef $aOccurrenceName)
    Local $tSecurityDescriptor = DllStructCreate("byte;byte;word;ptr[4]")
    Local $aError, $aReturn = DllCall("advapi32.dll", "bool", "InitializeSecurityDescriptor", "struct*", $tSecurityDescriptor, "dword", 1)
    If @error Then Return SetError(@error, @extended, 0)
    If Not $aReturn[0] Then
        $aError = DllCall("kernel32.dll", "dword", "GetLastError")
        Return SetError(@error, @extended, $aError[0])
    EndIf
    $aReturn = DllCall("advapi32.dll", "bool", "SetSecurityDescriptorDacl", "struct*", $tSecurityDescriptor, "bool", 1, "ptr", 0, "bool", 0)
    If @error Then Return SetError(@error, @extended, 0)
    If Not $aReturn[0] Then
        $aError = DllCall("kernel32.dll", "dword", "GetLastError")
        Return SetError(@error, @extended, $aError[0])
    EndIf
    Local $tSecurityAttributes = DllStructCreate("dword Length;ptr Descriptor;bool InheritHandle")
    DllStructSetData($tSecurityAttributes, 1, DllStructGetSize($tSecurityAttributes))
    DllStructSetData($tSecurityAttributes, 2, DllStructGetPtr($tSecurityDescriptor))
    DllStructSetData($tSecurityAttributes, 3, 0)
    Local $aResult = DllCall('kernel32.dll', 'handle', 'CreateSemaphoreW', 'struct*', $tSecurityAttributes, 'long', 0, 'long', 1, 'wstr', $aOccurrenceName[1])
    If @error Then Return SetError(@error, @extended, 0)
    $aOccurrenceName[0] = $aResult[0] ; set handle to zero index of the array
EndFunc   ;==>_SingletonInit

Func _SingletonRelease($aOccurrenceName)
    DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $aOccurrenceName[0])
    If @error Then Return SetError(@error, @extended, 0)
EndFunc   ;==>_SingletonRelease

if we want to do "good coding" so release all handle before close, do a better error checking, the security descriptor, don't create and check for ERROR_ALREADY_EXISTS but instead read if exist. I have use Semaphore but can be applied to every example posted before. That's all.


Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites

I havent messed with singleton in a while, I will try this in the coming week.  You've only given it 24 hours and singleton is a pretty niche need, so dont be discouraged. 

I did have issues year(s) ago with it but settled for a one-liner that enumerated processes and if scriptname existed then exit.  It just required that I use unique names.


,-. .--. ________ .-. .-. ,---. ,-. .-. .-. .-.
|(| / /\ \ |\ /| |__ __||| | | || .-' | |/ / \ \_/ )/
(_) / /__\ \ |(\ / | )| | | `-' | | `-. | | / __ \ (_)
| | | __ | (_)\/ | (_) | | .-. | | .-' | | \ |__| ) (
| | | | |)| | \ / | | | | | |)| | `--. | |) \ | |
`-' |_| (_) | |\/| | `-' /( (_)/( __.' |((_)-' /(_|
'-' '-' (__) (__) (_) (__)

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

I know every "alternative" function to _Singleton, a couple made also by MPVs. Since the function is there from 2005, lol 11 years ago, and we still found people in the recent years, like you, with problem with that imho we have two ways:

1) Ask to remove _Singleton() from UDF library

2) Fix it and make it work for everyone, not 99% yes and 1% not ( random numbers )

I'll try, in my little with my limited knowledge, to fix it with alternative APIs ( post #1 ) and an alternative approach ( post #7 )

Test it when you want but if actual _Singleton() work for you now well unfortunately we can't consider it a "valid test" if you know what i mean. Thanks.

Edited by Terenz

Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites

#10 ·  Posted

Thanks for tagging me @Terenz. While I have since rewritten my code to use my own singleton-like process, I will give your code a test on my laptop when I get back on it tonight.

@BrewManNH, for what it's worth, if you have a look at the thread I started, I'm getting virtually the same results today with the newest version of AutoIt, so it's still a problem. You say it's not a function issue, but shouldn't a function that's quite possibly integral to the proper operation of a program, and that is included with AutoIt with no warnings about its inconsistency, work properly unless the end user is going out of their way to defeat it? It's not like I was specifically trying to break _Singleton() when I discovered the issue. I had simply created a program on my home PC that incorporated _Singleton(), and it ran perfectly in all my tests. Fast forward a couple years and I get a laptop. I put the program on my laptop and didn't even realize for months that it wasn't working properly, simply because I never accidentally ran it twice. At that point I did a bunch of testing trying to figure out how I was breaking the program, only to discover that it was a flaw in the function itself, and not in my code. I also then realized that I had several different compiled programs that relied on _Singleton() and they would need to be rewritten and recompiled (which was a small hassle since the version changes since the original compile had included several script breaking changes).

Share this post


Link to post
Share on other sites

#11 ·  Posted

You are welcome @therks. Please test all the version so CreateEventW, CreateFileMappingW, CreateWaitableTimer and CreateSemaphoreW. For the last one try both post 1#  and #7. I'll really hope one of that will work, if not we'll try to debug in some way. I'm sure ( well "sure" is a big word, better to say "i think" ) is CreateMutex the problem, in some way not compatible with any system/configuration for unknow reason.

P.S. Remember the example are with Global\ so also other user in a multi-user enviroment, in theory, can't access to your script doesn't care if they are admin or standard. I'm sure you know how _Singleton() work but i'll write it for future reference:

Copy-paste-save the script ( compile or not will work at the same, try in both way ) launch the script, wait for the MsgBox with text "The first occurrence of test is running", DON'T CLOSE the Msgbox, start the script again. A new Msgbox will appear with the text "An occurrence of test is already running"


Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites

#12 ·  Posted

@therks Hello. would be great if you can check AutoIt Default singleton (API and Dll call Return values)  to be able to check where the issue is coming...

 

Saludos

Share this post


Link to post
Share on other sites

#13 ·  Posted

1 hour ago, therks said:

You say it's not a function issue, but shouldn't a function that's quite possibly integral to the proper operation of a program, and that is included with AutoIt with no warnings about its inconsistency, work properly unless the end user is going out of their way to defeat it?

As far as I am aware of, the function is written properly, and if you're having a problem are you SURE that it's the function and not the PC it's running on?

1 hour ago, therks said:

At that point I did a bunch of testing trying to figure out how I was breaking the program, only to discover that it was a flaw in the function itself, and not in my code

If there's a problem with the function let us know what you found out. The only person/people that can help with fixing the original function are the ones that are experiencing an issue with it.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

After 11 years i don't think we can able to debug, or is a problem of the API of internally to Autoit. At least a couple of attempt have made to debug, Kafu for example with the problem of _Singleton() has the error "The specified module could not be found". So? What do you do with this information now? All of they have problems with their PC but only _Singleton suffers? Come on.

Since for us don't change nothing to use a different API ( the function is pratically the same! ) is more simple to change the approach / the API actually used insted to try, i'll repeat after 11 years, to understand why Mutex not work in some scenario. Using semaphore/event/job/whatever, assuming work for therks, is the same and you get EXACTLY the same result of Mutex.

Edited by Terenz

Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites

#15 ·  Posted

there have definitely been issues with singleton.

But also why wait or even discuss the merits in this manner?  Wrap yours up and call it _singletonEx, and it now exists.  If it catches on with a vocal minority bearing valid reproduers, it will get folded in like all the other _*Ex UDFs.


,-. .--. ________ .-. .-. ,---. ,-. .-. .-. .-.
|(| / /\ \ |\ /| |__ __||| | | || .-' | |/ / \ \_/ )/
(_) / /__\ \ |(\ / | )| | | `-' | | `-. | | / __ \ (_)
| | | __ | (_)\/ | (_) | | .-. | | .-' | | \ |__| ) (
| | | | |)| | \ / | | | | | |)| | `--. | |) \ | |
`-' |_| (_) | |\/| | `-' /( (_)/( __.' |((_)-' /(_|
'-' '-' (__) (__) (_) (__)

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

I'll discuss it because i don't what to make the N alternative to _Singleton ( we have 5-6 different version, with PID, hardware id, process name, file lock, autoit title, probably forget a couple, and in this list i don't want to add mine with TCP or other APIs ) but i'd like to make a dot to this story, trash that and replace with one that work for everyone.

This is probably the only autoit function, integrated or in the UDF, that have so many "unofficial" way that give the same result of the original one, without adding nothing more that is so essential or different. It has so many alternative for one reason and everybody knows why, _Singleton() in some scenario\situation rare or not don't work.

P.S. After some research i have found why CreateMutex suppress CreateSemaphore. Has i have already say the original function was with semaphore. When that function was integrated in the UDF by ticket one of dev arbitrarily, without any public explanation, replace semaphore with mutex and leave the function as it it. The error checking and the security descriptor was added later. Original one was 4 line of 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

P.P.S Mutex is very common for malware/bot. It is used for detect whether an operating system has any security programs installed on it or avoid double infection of the OS. So in theory any security software can monitor Mutex API and maybe block the execution, don't forget how many false-positive we have with Autoit. So this is another reason to avoid the use of Mutex and choose an alternative API.

Edited by Terenz

Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites

#17 ·  Posted (edited)

4 hours ago, Terenz said:

P.P.S Mutex is very common for malware/bot. It is used for detect whether an operating system has any security programs installed on it or avoid double infection of the OS. So in theory any security software can monitor Mutex API and maybe block the execution, don't forget how many false-positive we have with Autoit. So this is another reason to avoid the use of Mutex and choose an alternative API.

And this the proof!

A script compiled ( NO UPX ) with _Singleton() i have simply take the example provide here, nothing else:

_Singleton

And the result is...drumroll:

https://virustotal.com/en/file/5ee651362ae24de352e993d37c0dd3d6643910f5b1b6d0ea49717172bece04b8/analysis/1477566486/

9 detection. What happened if i compile a couple of the example above, without UPX?

https://virustotal.com/en/file/042ea5991d5b413f0aa5edb0a516afef0201004ef38e907621d0a3a303f700bf/analysis/1477567292/

https://virustotal.com/en/file/a7670e55044d0e00b0a47bb1dcc045bcc2485ab1ef395b3a9533931273e3b198/analysis/

https://virustotal.com/en/file/29ccfad5e09135c8160d50d38d558211fc4e9dfc745a08542fb677f425ef516f/analysis/

https://virustotal.com/en/file/689d1359cbe617416279694c7fb9ebf58b715c76c1b2f338717a6d040d5c8d52/analysis/1477568202/

Uhm...i see 6 detection for three and 5 detection for CreateFileMapping. So using CreateMutex *seems* give to us +3/+4 more detection respect to other APIs. I believe it is appropriate to stop defending _Singleton() and think to an alternative, whatever API you like from the first post is better then the actual for all the reason provided in this thread.

Edited by Terenz

Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites

#18 ·  Posted

@Terenz, I have good and bad news. I tested each of those scripts and, except for the TCP and GUI examples, they all failed to perform as expected. In every instance GetLastError returns 126 (ERROR_MOD_NOT_FOUND).

 

22 hours ago, BrewManNH said:

As far as I am aware of, the function is written properly, and if you're having a problem are you SURE that it's the function and not the PC it's running on?

At first I agreed with this (the function being written properly) however, after testing Terenz' scripts and checking the variables, I rediscovered that all my issues stem back to GetLastError returning a value that the function isn't checking for. The function only checks if it returns 183 (ERROR_ALREADY_EXISTS) but in my case it's consistently returning 126 (ERROR_MOD_NOT_FOUND). This is the same issue that @KaFu and I had in the thread I linked to previously. I'm not that familiar with GetLastError, does it often return false positives or unrelated errors? That's the only reason I can think of for the function to ignore any error besides 183.

Share this post


Link to post
Share on other sites

#19 ·  Posted (edited)

That's really shock me @therks an unexpected result! You can't load any kernel object...

Try this:

$aDLL1 = DllOpen("kernel32.dll")
If $aDLL1 = -1 Then MsgBox(16, "ERROR", "Can't load the DLL 1")
MsgBox(64, "INFO", "DLL LOADED 1: " & $aDLL1)
$aDLL2 = DllOpen(@SystemDir & "\kernel32.dll")
If $aDLL2 = -1 Then MsgBox(16, "ERROR", "Can't load the DLL 2")
MsgBox(64, "INFO", "DLL LOADED 2: " & $aDLL2)

$hStartEvent1 = DllCall($aDLL1, 'handle', 'CreateSemaphoreW', 'struct*', 0, 'long', 0, 'long', 1, 'wstr', 'Test')
$hError1 = DllCall($aDLL1, "dword", "GetLastError")
MsgBox(64, "INFO", "DLL LOADED: " & $hStartEvent1[0])
MsgBox(64, "INFO", "ERROR: " & $hError1[0])

$hStartEvent2 = DllCall($aDLL1, 'handle', 'CreateSemaphoreW', 'struct*', 0, 'long', 0, 'long', 1, 'wstr', 'Test')
Local $hError2 = DllCall($aDLL1, "dword", "GetLastError")
MsgBox(64, "INFO", "DLL LOADED: " & $hStartEvent2[0])
MsgBox(64, "INFO", "ERROR: " & $hError2[0])

$hStartEvent3 = DllCall($aDLL2, 'handle', 'CreateSemaphoreW', 'struct*', 0, 'long', 0, 'long', 1, 'wstr', 'Test')
Local $hError3 = DllCall($aDLL2, "dword", "GetLastError")
MsgBox(64, "INFO", "DLL LOADED: " & $hStartEvent3[0])
MsgBox(64, "INFO", "ERROR: " & $hError3[0])


DllClose($aDLL1)
DllClose($aDLL2)

Expected value are:

  • 1
  • 2
  • An handle like 0x000006B0
  • 0
  • An handle like 0x000006BC ( different then previuos )
  • 183
  • An handle like 0x000006BD  ( different then previuos )
  • 183

If you are on 64 Bit OS try also _WinAPI_Wow64EnableWow64FsRedirection

EDIT: Check also in the eviroment variable:

Local $sEnvVar = EnvGet("PATH")
MsgBox(64, "Info", $sEnvVar)

C:\Windows and C:\Windows\System32. If you open a CMD windows and simply write and confirm:

kernel32.dll

The expected result is an MsgBox with text "you can't open it / not associated" or similar with the path of "Kernel32.dll" inside the title. If instead you have a ConsoleWrite like "not recognized as an internal or external command" something not work.

P.S. The GUI example at post #7 works? TCP ok but the GUI use semaphore so always a kernel object so is very strange that this work and the others not. The difference is the method applied, instead to check the error for ERROR_ALREADY_EXISTS use OpenSemaphore.

2 hours ago, therks said:

@Terenz, I have good and bad news. I tested each of those scripts and, except for the TCP and GUI examples, they all failed to perform as expected.

Edited by Terenz

Nothing is so strong as gentleness. Nothing is so gentle as real strength

 

Share this post


Link to post
Share on other sites

#20 ·  Posted (edited)

The script: I got all the results you said I should (I am also confused by this).

The path: Looks normal to me.

Running kernel32 from command line: I get the error (no program associated).

Example in post #7: Yes.

 

Edit: Wow, so I just tested your _SingletonSemaphore function, but using DllOpen first and everything works fine. Also did the same to the Singleton UDF and it ALSO works fine. WTF..?

Func _Singleton($sOccurrenceName, $iFlag = 0)
    Local Const $ERROR_ALREADY_EXISTS = 183
    Local Const $SECURITY_DESCRIPTOR_REVISION = 1
    Local $tSecurityAttributes = 0
    Local Const $hKernel32 = DllOpen('kernel32.dll')
    Local $iError, $iExtended, $vReturn

    Do
        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 $tSecurityDescriptor = DllStructCreate("byte;byte;word;ptr[4]")
            ; Initialize the security descriptor.
            Local $aRet = DllCall("advapi32.dll", "bool", "InitializeSecurityDescriptor", _
                    "struct*", $tSecurityDescriptor, "dword", $SECURITY_DESCRIPTOR_REVISION)
            If @error Then Return SetError(@error, @extended, 0)
            If $aRet[0] Then
                ; Add the NULL DACL specifying access to everybody.
                $aRet = DllCall("advapi32.dll", "bool", "SetSecurityDescriptorDacl", _
                        "struct*", $tSecurityDescriptor, "bool", 1, "ptr", 0, "bool", 0)
                If @error Then Return SetError(@error, @extended, 0)
                If $aRet[0] Then
                    ; Create a SECURITY_ATTRIBUTES structure.
                    $tSecurityAttributes = DllStructCreate($tagSECURITY_ATTRIBUTES)
                    ; Assign the members.
                    DllStructSetData($tSecurityAttributes, 1, DllStructGetSize($tSecurityAttributes))
                    DllStructSetData($tSecurityAttributes, 2, DllStructGetPtr($tSecurityDescriptor))
                    DllStructSetData($tSecurityAttributes, 3, 0)
                EndIf
            EndIf
        EndIf

        Local $aHandle = DllCall($hKernel32, "handle", "CreateMutexW", "struct*", $tSecurityAttributes, "bool", 1, "wstr", $sOccurrenceName)
        If @error Then
            $iError = @error
            $iExtended = @extended
            $vReturn = 0
            ExitLoop
        EndIf
        Local $aLastError = DllCall($hKernel32, "dword", "GetLastError")
        If @error Then
            $iError = @error
            $iExtended = @extended
            $vReturn = 0
            ExitLoop
        EndIf
        If $aLastError[0] = $ERROR_ALREADY_EXISTS Then
            If BitAND($iFlag, 1) Then
                DllCall($hKernel32, "bool", "CloseHandle", "handle", $aHandle[0])
                If @error Then
                    $iError = @error
                    $iExtended = @extended
                    $vReturn = 0
                    ExitLoop
                EndIf
                $iError = $aLastError[0]
                $iExtended = $aLastError[0]
                $vReturn = 0
                ExitLoop
            Else
                Exit -1
            EndIf
        EndIf

        $vReturn = $aHandle[0]
    Until 1

    DllClose($hKernel32)
    Return SetError($iError, $iExtended, $vReturn)
EndFunc   ;==>_Singleton

I don't know if @KaFu is still around, or was still having the same problem, but I'd be curious to know if this works for him also.

Edited by therks
Added some code

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