trancexx

Communication between scripts

10 posts in this topic

#1 ·  Posted (edited)

You know that SciTE quality to show how many instances of it is running and what is current one that you view. Information about this is in SciTE window title. Something like: "path\name - SciTE [2 of 7]"

I find that very useful an wanted to implement that in one of my script. This is the idea and the procedure:

1. When script starts call function CreateSemaphore from kernel32.dll. Use unique string (GUID)

2. Call ReleaseSemaphore function from kernel32 to increas the count of the semaphore object by one

3. Register new unique window message using function RegisterWindowMessage from user32.dll

4. Use function SendMessageCallback from user32.dll to send message to all top-level windows in the system. This message will be caught only by our windows. This is to say "Hey I'm here and I'm your new instance!"

5. Use GUIRegisterMsg() to register function ( _IntermediaryFunction) that deals with enumeration. Message ID is return value of RegisterWindowMessage (step 3)

To make it work properly few more steps needs to be done when script is about to exit:

6. Get handle of the semaphore object (that is done for example calling CreateSemaphore function again)

7. Decrease the semaphore's count by one calling function WaitForSingleObject from kernel32.dll

8. Use SendMessageCallback from user32.dll to announce to other windows intention to exit.

Other windows processes that message and updates their titles (_IntermediaryFunction from step 5).

And that's it!!!

Example script:

Global $iInstanceCurrent, $iInstancesOverall

_NumInst("SomeUniqueString")

Global $iMessage = _RegisterWindowMessage("AnotherUniqueString")

Global $hGui = GUICreate("My Example script [" & $iInstanceCurrent & " of " & $iInstancesOverall & "]")


GUISetState(@SW_SHOW)

While 1
    If GUIGetMsg() = -3 Then ExitLoop
WEnd






Func _NumInst($sName)

    Local $a_hCall = DllCall("kernel32.dll", "hwnd", "CreateSemaphore", _
            "ptr", 0, _
            "int", 1, _
            "int", 999, _
            "str", $sName)

    If @error Or Not $a_hCall[0] Then
        Return SetError(1, 0, "")
    EndIf

    Local $hSemaphore = $a_hCall[0]

    $a_iCall = DllCall("kernel32.dll", "int", "ReleaseSemaphore", "hwnd", $hSemaphore, "int", 1, "int*", 0)

    If @error Or Not $a_iCall[0] Then
        Return SetError(2, 0, "")
    EndIf

    $iInstanceCurrent = $a_iCall[3] ; global variable
    $iInstancesOverall = $a_iCall[3] ; this too, initially they are the same

    Return SetError(0, 0, 1)

EndFunc   ;==>_NumInst


Func _RegisterWindowMessage($sMessage)

    Local $a_iCall = DllCall("user32.dll", "dword", "RegisterWindowMessage", "str", $sMessage)

    If @error Or Not $a_iCall[0] Then
        Return SetError(1, 0, "")
    EndIf

    Local $iMessage = $a_iCall[0]

    $a_iCall = DllCall("user32.dll", "int", "SendMessageCallback", _
            "hwnd", 0xFFFF, _
            "dword", $iMessage, _
            "int", $iInstanceCurrent, _
            "int", 0, _
            "ptr", 0, _
            "dword", 0)

    If @error Or Not $a_iCall[0] Then
        Return SetError(2, 0, "")
    EndIf

    GUIRegisterMsg($iMessage, "_IntermediaryFunction")

    Return SetError(0, 0, $iMessage)

EndFunc   ;==>_RegisterWindowMessage


Func _IntermediaryFunction($hHwnd, $iMsg, $wParam, $lParam)

    $wParam = BitOR($wParam, 0)
    $lParam = BitOR($lParam, 0)

    If $wParam > $iInstanceCurrent Then
        $iInstancesOverall += 1
    EndIf

    If $lParam Then
        $iInstancesOverall -= 1
        If $lParam <= $iInstanceCurrent Then
            $iInstanceCurrent -= 1
        EndIf
    EndIf

    WinSetTitle($hGui, 0, "My Example script [" & $iInstanceCurrent & " of " & $iInstancesOverall & "]")


EndFunc   ;==>_IntermediaryFunction


Func OnAutoItExit()

    If $iMessage Then
        Local $a_hCall = DllCall("kernel32.dll", "hwnd", "CreateSemaphore", _
                "ptr", 0, _
                "int", 1, _
                "int", 999, _
                "str", "SomeUniqueString")

        If Not @error Then
            Local $hSemaphore = $a_hCall[0]

            Local $a_iCall = DllCall("kernel32.dll", "dword", "WaitForSingleObject", _
                    "hwnd", $hSemaphore, _
                    "dword", 0)

            If Not @error Then
                $a_iCall = DllCall("user32.dll", "int", "SendMessageCallback", _
                        "hwnd", 0xFFFF, _
                        "dword", $iMessage, _
                        "int", 0, _
                        "int", $iInstanceCurrent, _
                        "ptr", 0, _
                        "dword", 0)
            EndIf
        EndIf
    EndIf

EndFunc   ;==>OnAutoItExit

I've made ResHacker wannabe update. Newly attached script is fully implementing this method.

Link to that script

Check it out, it's totaly cool

Btw, I find moderator's (or whosever) act regarding this topic to be almost shallow. Code was posted if that was the reason to move this post. Very superficial.

edit:

topsy

Edited by trancexx

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites



@trancexx

A shame no one reacted on this.

I just saw it now. Looking REAL good!!

/Manko


Yes i rush things! (I sorta do small bursts inbetween doing nothing.) Things I have rushed and reRushed:* ProDLLer - Process manager - Unload viri modules (dll) and moore...* _WinAPI_ProcessListOWNER_WTS() - Get Processes owner list...* _WinAPI_GetCommandLineFromPID() - Get commandline of target process...* _WinAPI_ThreadsnProcesses() Much info if expanded - optional Indented "Parent/Child"-style Processlist. Moore to come... eventually...

Share this post


Link to post
Share on other sites

Wow. Very nice, I never would have thought to do this.


[left][sub]We're trapped in the belly of this horrible machine.[/sub][sup]And the machine is bleeding to death...[/sup][sup][/sup][/left]

Share this post


Link to post
Share on other sites

This is by using OpenSemaphore function (more proper maybe) to get handle to the semaphore object on exit (first post script is using CreateSemaphore again). Also messages here are sent by SendNotifyMessage since I'm not using callback function (option) of SendMessageCallback function.

New script:

Global $iInstanceCurrent, $iInstancesOverall

_NumInst("SomeUniqueString")

Global $iMessage = _RegisterWindowMessage("AnotherUniqueString")

Global $hGui = GUICreate("My Example script [" & $iInstanceCurrent & " of " & $iInstancesOverall & "]")


GUISetState(@SW_SHOW)

While 1
    If GUIGetMsg() = -3 Then ExitLoop
WEnd






Func _NumInst($sName)

    Local $a_hCall = DllCall("kernel32.dll", "hwnd", "CreateSemaphore", _
            "ptr", 0, _
            "int", 1, _ ; initial count
            "int", 999, _ ; max count
            "str", $sName) ; name of the semaphore object

    If @error Or Not $a_hCall[0] Then
        Return SetError(1, 0, "")
    EndIf

    Local $hSemaphore = $a_hCall[0]

    $a_iCall = DllCall("kernel32.dll", "int", "ReleaseSemaphore", "hwnd", $hSemaphore, "int", 1, "int*", 0)

    If @error Or Not $a_iCall[0] Then
        Return SetError(2, 0, "")
    EndIf

    $iInstanceCurrent = $a_iCall[3] ; global variable
    $iInstancesOverall = $a_iCall[3] ; this too, initially they are the same

    Return SetError(0, 0, 1)

EndFunc   ;==>_NumInst


Func _RegisterWindowMessage($sMessage)

    Local $a_iCall = DllCall("user32.dll", "dword", "RegisterWindowMessage", "str", $sMessage)

    If @error Or Not $a_iCall[0] Then
        Return SetError(1, 0, "")
    EndIf

    Local $iMessage = $a_iCall[0]

    $a_iCall = DllCall("user32.dll", "int", "SendNotifyMessage", _
            "hwnd", 0xFFFF, _ ; HWND_BROADCAST
            "dword", $iMessage, _
            "int", $iInstanceCurrent, _
            "int", 0)

    If @error Or Not $a_iCall[0] Then
        Return SetError(2, 0, "")
    EndIf

    GUIRegisterMsg($iMessage, "_IntermediaryFunction")

    Return SetError(0, 0, $iMessage)

EndFunc   ;==>_RegisterWindowMessage


Func _IntermediaryFunction($hHwnd, $iMsg, $wParam, $lParam)

    $wParam = BitOR($wParam, 0)
    $lParam = BitOR($lParam, 0)

    If $wParam > $iInstanceCurrent Then
        $iInstancesOverall += 1
    EndIf

    If $lParam Then
        $iInstancesOverall -= 1
        If $lParam <= $iInstanceCurrent Then
            $iInstanceCurrent -= 1
        EndIf
    EndIf

    WinSetTitle($hGui, 0, "My Example script [" & $iInstanceCurrent & " of " & $iInstancesOverall & "]")


EndFunc   ;==>_IntermediaryFunction


Func OnAutoItExit()

    If $iMessage Then

        Local $a_hCall = DllCall("kernel32.dll", "hwnd", "OpenSemaphore", _
                "dword", 0x100000, _ ; SYNCHRONIZE
                "int", 0, _
                "str", "SomeUniqueString") ; name of our semaphore

        If Not @error Then
            Local $hSemaphore = $a_hCall[0]

            Local $a_iCall = DllCall("kernel32.dll", "dword", "WaitForSingleObject", _
                    "hwnd", $hSemaphore, _
                    "dword", 0)

            If Not @error Then
                $a_iCall = DllCall("user32.dll", "int", "SendNotifyMessage", _
                        "hwnd", 0xFFFF, _ ; HWND_BROADCAST
                        "dword", $iMessage, _
                        "int", 0, _
                        "int", $iInstanceCurrent)
            EndIf
        EndIf
    EndIf

EndFunc   ;==>OnAutoItExit

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Interesting for sure, but what is it for ?

I understand it deals with multiple process, but what is the problem solved with this script ?

Without a concrete example it's hard figure out :)

BTW I'm interested : I try to have my plugins talking with a core server but the IPC part is rather hard to handle.

If this can help me, I'd be glad to know how ^_^

Apzo.

@trancexx

A shame no one reacted on this.

Very nicely done :)

regards,

ptrex

Share this post


Link to post
Share on other sites

Interesting for sure, but what is it for ?

I understand it deals with multiple process, but what is the problem solved with this script ?

Without a concrete example it's hard figure out :)

BTW I'm interested : I try to have my plugins talking with a core server but the IPC part is rather hard to handle.

If this can help me, I'd be glad to know how :)

Apzo.

There are three examples posted. I can't imagine more concrete examples. Just double click (run) each of that scripts more than once at the same time and observe the differences in window titles. Close some windows, run some more, etc...

About solved problems... I guess nothing is solved only few more problems possibly created.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Actually, I used this technique to solve a problem recently in a script I want to release. The problem was with system context menu integration. In the normal system context menu (NOT a shell extension) when you perform an action on more than one file, then an instance of the associated process is launched for each file selected. I needed a way to control this so that the first instance of the process would launch and perform its task, then the others would wait for it to be ready and ONE-AT-A-TIME pass some data using WM_COPYDATA to the initial instance for processing. Semaphores worked great.

The other challenge with the whole process is that in AutoIt you're not supposed to block a function called from GuiRegisterMessage() with anything that takes a long time (like from a WM_COPYDATA message). You're supposed to return to the main script as fast as possible. I solved that problem, but it was causing major synchronization problems with the multiple instances. Again, semaphores solved this nicely. Now each additional instance waits for its slot, then sends the message to the main instance, which procsses the message and returns immediately. Then the data is processed back in the main script, and after it is finished another semaphore is released to allow the next waiting instance to send its data.

Share this post


Link to post
Share on other sites

can you send other data between the scripts too? still catching on to working with dlls


Gnatwork Networks

Share this post


Link to post
Share on other sites

Depending on the method you can send any kind of data between processes. See my script HERE for an example using semaphores and WM_COPYDATA to communicate between script instances.

A more manual method would be physically allocating memory in another process, writing the data, and sending a message to the process to read the memory / data.

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