Jump to content

Another Inter Script communication


ChrisL
 Share

Recommended Posts

Some of this code in the message handler was taken from piccasso and some of it was nicked from Martin. :P

I've glued it all together and added some functions to hopefully make it simpler to talk to another script.

In this example it is probably easier to compile them rather than trying to run 2 from Scite.

Run Script1 (nothing exciting will happen at this point)

Then Run script 2, enter some string in to the input box and OK it. The string will get sent to script 1 then script 1 will sned it back to script 2.

It doesn't sound that exciting but it did just make it quite easy to send the string backwards and forwards between the 2 scripts.

The important bits....

$Local_ReceiverID_Name = "MyMessageWindowTitle"

_SetAsReceiver($Local_ReceiverID_Name) ;means that this script will accept messages and this it the identifier to use for the other script to send a message to me on

_SetReceiverFunction("_MyFunc") ;this is the function you want to call when data is received, you could do absolubtly anything with it I just chose a message box for ease ideally it should do something very quickly and return

to send a message to the above you would use...

$vText = "My text I want to send"

$Remote_ReceiverID_Name = "MyMessageWindowTitle" ;(The other scripts message window title)

_SendData($vText,$Remote_ReceiverID_Name)

Of course you can use both/all scripts to send data between themselves just give each script a unique title in _SetAsReceiver("Unique title")

;Script1
#include "MessageHandler.au3"

$Local_ReceiverID_Name = "Script1sReceiverID";This is the ID that the other script will use to send data
$Remote_ReceiverID_Name = "Script2sReceiverID";This is the ID of the script we want to send data too

$hwnd = _SetAsReceiver($Local_ReceiverID_Name)
ConsoleWrite("hwnd of the Local_ReceiverID_Name is " & $hwnd & @crlf)
$myFunc = _SetReceiverFunction("_MyFunc2")
ConsoleWrite("My data receiver function is " & $myFunc & @crlf)


While 1
    Sleep(1000)
WEnd

Func _MyFunc2($vText)
    Msgbox(0,@ScriptName,"I am " & @ScriptName & " I have received some data" & @crlf & @crlf & $vText & @crlf & @crlf & "And now I'm sending the data back")
    $iSent = _SendData($vText,$Remote_ReceiverID_Name)
    Exit
EndFunc

;Script2
#include "MessageHandler.au3"

$Local_ReceiverID_Name = "Script2sReceiverID";This is the ID that the other script will use to send data
$Remote_ReceiverID_Name = "Script1sReceiverID";This is the ID of the script we want to send data too

$hwnd = _SetAsReceiver($Local_ReceiverID_Name)
ConsoleWrite("hwnd of the Local_ReceiverID_Name is " & $hwnd & @crlf)
$myFunc = _SetReceiverFunction("_MyFunc2")
ConsoleWrite("My data receiver function is " & $myFunc & @crlf)


$Str = InputBox(@ScriptName," I am " & @ScriptName & @crlf & "Enter some data to be sent to the other script")

$iSent = _SendData($Str,$Remote_ReceiverID_Name)

While 1
    sleep(100)
WEnd


Func _MyFunc2($vText)
    Msgbox(0,@ScriptName,@ScriptName & " has received a message" & @crlf & $vText)
    Exit
EndFunc

New version attached which now has a better message handler system which will not stop the sending script from pausing allowing a quick return to the script.

Old version had 105 downloads

MessageHandler.au3

Edited by ChrisL
Link to comment
Share on other sites

Some of this code in the message handler was taken from piccasso and some of it was nicked from Martin.

I've glued it all together and added some functions to hopefully make it simpler to talk to another script.

In this example it is probably easier to compile them rather than trying to run 2 from Scite.

Run Script1 (nothing exciting will happen at this point)

Then Run script 2, enter some string in to the input box and OK it. The string will get sent to script 1 then script 1 will sned it back to script 2.

It doesn't sound that exciting but it did just make it quite easy to send the string backwards and forwards between the 2 scripts.

The important bits....

_SetMaxStringLen(256) ;Must be the same in both scripts or you could run into memory issues

$myWindowTitle = "MyMessageWindowTitle"

_SetAsReceiver($myWindowTitle) ;means that this script will accept messages and this it the title to use for the other script to send a message to me on

_SetReceiverFunction("_MyFunc") ;this is the function you want to call when data is received, you could do absolubtly anything with it I just chose a message box for ease

to send a message to the above you would use...

_SetMaxStringLen(256)

$vText = "My text I want to send"

$RecieverTitleRemote = "MyMessageWindowTitle" ;(The other scripts message window title)

_SendData($vText,$RecieverTitleRemote)

Of course you can use both/all scripts to send data between themselves just give each script a unique title in _SetAsReceiver("Unique title") make sure each and every script has the same _SetMaxStringLen(Value) and remember which script "Unique title" needs which data.

;Script1
#include "MessageHandler.au3"

$ReceiverTitleLocal = "Script1sWindowTitle";Must be the same in the sender and the receiver
$RecieverTitleRemote = "Script2sWindowTitle"
_SetMaxStringLen(256)
_SetAsReceiver($ReceiverTitleLocal)
_SetReceiverFunction("_MyFunc2")


While 1
    Sleep(250)
WEnd

Func _MyFunc2($vText)
    Msgbox(0,@ScriptName,"I am " & @ScriptName & " I have received some data" & @crlf & @crlf & $vText & @crlf & @crlf & "And now I'm sending the data back")
    _SendData($vText,$RecieverTitleRemote)
    Exit
EndFunc

;Script2
#include "MessageHandler.au3"

$ReceiverTitleRemote = "Script1sWindowTitle";Must be the same in the sender and the receiver
$ReceiverTitleLocal = "Script2sWindowTitle"

_SetMaxStringLen(256)
_SetAsReceiver($ReceiverTitleLocal)
_SetReceiverFunction("_MyFunc2")

$Str = InputBox(@ScriptName," I am " & @ScriptName & @crlf & "Enter some data to be sent to the other script")

$sent = _SendData($Str,$ReceiverTitleRemote)

While 1
    sleep(100)
WEnd


Func _MyFunc2($vText)
    Msgbox(0,@ScriptName,@ScriptName & " has received a message" & @crlf & $vText)
    Exit
EndFunc
Looks good ChrisL.

It seems a pity to have to set the max string length. You send the length of the string so maybe you could have

$vs_msg = DllStructCreate("char[" & DllStructGetData($vs_cds, 2) +1 & "];int", DllStructGetData($vs_cds, 3))

instead of

$vs_msg = DllStructCreate($STRUCTDEF_AU3MESSAGE, DllStructGetData($vs_cds, 3))

I wonder if using Title in your receiver and sender references, eg

_SetAsReceiver($ReceiverTitleLocal)

might confuse some people. I would expect some users might think that their gui title should go there and then you have a problem. Maybe you could call it

_SetAsReceiver($ReceiverID_Name)

and maybe in the function test WInExists($vtitle) then some error, and possibly something to allow someone to mistakenly call that function twice without an error.

I'm being very picky though.

The receiver doesn't use var1 in the data struct, are you thinking of adding some other options?

I am a bit concerned about the idea of calling a user defined function from the message handler. If the function takes a long time to execute I think there will be problems. It might be better to set a global variable somehow and get out quickly. Perhaps have a message queue, and if the queue got too long then the message handler could return some error code. The sender could wait some time and try again. Then you could have a timer function which acts on the global variable. (A timer function rather than AdLibEnable in case the user has his own AdLibEnable.)

Serial port communications UDF Includes functions for binary transmission and reception.printing UDF Useful for graphs, forms, labels, reports etc.Add User Call Tips to SciTE for functions in UDFs not included with AutoIt and for your own scripts.Functions with parameters in OnEvent mode and for Hot Keys One function replaces GuiSetOnEvent, GuiCtrlSetOnEvent and HotKeySet.UDF IsConnected2 for notification of status of connected state of many urls or IPs, without slowing the script.
Link to comment
Share on other sites

Looks good ChrisL.

It seems a pity to have to set the max string length. You send the length of the string so maybe you could have

$vs_msg = DllStructCreate("char[" & DllStructGetData($vs_cds, 2) +1 & "];int", DllStructGetData($vs_cds, 3))

instead of

$vs_msg = DllStructCreate($STRUCTDEF_AU3MESSAGE, DllStructGetData($vs_cds, 3))

But... and maybe I have this arse about face doesn't the sender and the receiver need to have the same struct size put asside before hand I wasn't sure it was safe.

The receiver doesn't use var1 in the data struct, are you thinking of adding some other options?

I wasn't sure if I could use var1 to send the size of the string to make it more dynamic as per above but I hadn't figured it out. I posted this up because someone had asked in chat about messaging between scripts. It definately needs more work on it

Edit nope that was var2, var1 was something I think picasso did to say it was a string or a number I left it in

I wonder if using Title in your receiver and sender references, eg

_SetAsReceiver($ReceiverTitleLocal)

might confuse some people. I would expect some users might think that their gui title should go there and then you have a problem. Maybe you could call it

_SetAsReceiver($ReceiverID_Name)

and maybe in the function test WInExists($vtitle) then some error, and possibly something to allow someone to mistakenly call that function twice without an error.

I'm being very picky though.

Yeah I agree with you there

I am a bit concerned about the idea of calling a user defined function from the message handler. If the function takes a long time to execute I think there will be problems. It might be better to set a global variable somehow and get out quickly. Perhaps have a message queue, and if the queue got too long then the message handler could return some error code. The sender could wait some time and try again. Then you could have a timer function which acts on the global variable. (A timer function rather than AdLibEnable in case the user has his own AdLibEnable.)

That sounds like a good idea too, I did something similar with a Hotkey queue which buffered hotkeys so they ran in order. Edited by ChrisL
Link to comment
Share on other sites

But... and maybe I have this arse about face doesn't the sender and the receiver need to have the same struct size put asside before hand I wasn't sure it was safe.

No, you are actually creating the structure after receiving the information of where the data is and how much. Send creates the structure to suit the length of the string then sends that length with the message, so the receiver can just create the struct to be the same size. I tried it and it works ok with the advantage that you no longer need the functions _GetMaxLen or _SetMaxStringLen.

Func _GUIRegisterMsgProc($hWnd, $MsgID, $WParam, $LParam)
    If $MsgID = $WM_COPYDATA Then
       ; We Recived a WM_COPYDATA Message
       ; $LParam = Poiter to a COPYDATA Struct
        $vs_cds = DllStructCreate($StructDef_COPYDATA, $LParam)
       ; Member No. 3 of COPYDATA Struct (PVOID lpData;) = Pointer to Custom Struct
        $vs_msg = DllStructCreate("char[" & DllStructGetData($vs_cds, 2) +1 & "]", DllStructGetData($vs_cds, 3))
       ; Call the function to accept the recived data
        Call($SendDataToFunc,DllStructGetData($vs_msg, 1))
    EndIf
EndFunc  ;==>_GUIRegisterMsgProc

I also removed the second element of the struct ";int" because I thought it wasn't used.

Serial port communications UDF Includes functions for binary transmission and reception.printing UDF Useful for graphs, forms, labels, reports etc.Add User Call Tips to SciTE for functions in UDFs not included with AutoIt and for your own scripts.Functions with parameters in OnEvent mode and for Hot Keys One function replaces GuiSetOnEvent, GuiCtrlSetOnEvent and HotKeySet.UDF IsConnected2 for notification of status of connected state of many urls or IPs, without slowing the script.
Link to comment
Share on other sites

@ChrisL, or anyone,

I realize that I don't understand something here so maybe you can help me out.

If you send a message which just sends the pointer to a struct so that the receiving program can read it by creating a struct at the same address then AutoIt will crash. Say do did this

$stringtoSend = "it's sunny today"
$sStruct = dllStructCreate("char[" & stringlen($stringtosend + 1 & "]")
dllstructsetdata($sStruct,1,$stringtoSend)
_SendMessage($hWnd,$msgNum,DllStructGetPtr($sStruct),StringLen($stringtoSend))

and the receiving program did this

FUnc getmsg($hWnd, $iMsg, $iwParam, $ilParam)
   $rStruct = dllstructcreate("char[" & $ilparam + 1 & "]",$iwParam);crashes
   $newmsg = DllstructGetData($rStruct,1)
endfunc

The crash is because (I believe) the receiving program is trying to use the memory of the sending program. I have always dealt with this by using memory functions to read the data from the struct in the sending program using the pointer passed in the message as the place to read from.

So I assume that windows does something special with WM_COPYDATA and sets up a temporary situation to allow the receiving program access to the senders memory space. Do you think that's correct? If not I am a bit lost with this at the moment.

EDIT.- The ANSWER

I understand it now. In case anyone else didn't know and is interested here's my explanation.

The sending program uses the COPYDATA structure to pass the length of the data to be sent and the address of where the data is.

If you add a msgbox to the sending function you can see what the address of the data is.

The program which receives the message can read the COPYDATA structure so it knows where in memory the data is and how long it is. If you add a MsgBox to the receiving function you can again see what the memory address of the data is. But they are not the same! That is how the receiving program can read the data without having access to the sending program's memory address space.

What windows does is this. It knows from the COPYDATA structure what the address of the data is and how much data. So it copies that data to the receiving program's memory space and changes the value of lparam to suit the new memory address.

I found a good explanation here.

So WM_COPYDATA is very useful for sending data one way, but its not like having a shared memory area where programs can communicate by writing to and reading from an area of memory.

Edited by martin
Serial port communications UDF Includes functions for binary transmission and reception.printing UDF Useful for graphs, forms, labels, reports etc.Add User Call Tips to SciTE for functions in UDFs not included with AutoIt and for your own scripts.Functions with parameters in OnEvent mode and for Hot Keys One function replaces GuiSetOnEvent, GuiCtrlSetOnEvent and HotKeySet.UDF IsConnected2 for notification of status of connected state of many urls or IPs, without slowing the script.
Link to comment
Share on other sites

Nice one martin good find it was no something I couldn't have answered anyway I've just been trying to pick this up from reading other posts on here.

I've updated the first post with some modifications as you have suggested (Thanks). It is definately more simple now.

I haven't implimented the message queuing system at the moment, I think it needs alot more thinking about.

Link to comment
Share on other sites

I haven't implimented the message queuing system at the moment, I think it needs alot more thinking about.

Yes, it's easy to say it's needed but making it work is different.

I like the way you've done the _SetAsReceiver function. Looks pretty safe now.

Serial port communications UDF Includes functions for binary transmission and reception.printing UDF Useful for graphs, forms, labels, reports etc.Add User Call Tips to SciTE for functions in UDFs not included with AutoIt and for your own scripts.Functions with parameters in OnEvent mode and for Hot Keys One function replaces GuiSetOnEvent, GuiCtrlSetOnEvent and HotKeySet.UDF IsConnected2 for notification of status of connected state of many urls or IPs, without slowing the script.
Link to comment
Share on other sites

  • 2 weeks later...

Yep, nice idea and nice work. Thanks!

Actually theres a few other functions in user32.dll for sending messages with different queuing: http://msdn.microsoft.com/en-us/library/ms644950(VS.85).aspx

...but my testings faild with all others and dont really have much time to play with these ;(

PS: Important note from that link:

"The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message."

"...and does not return until the window procedure has processed the message..."

So, when the data is sent to the default or user definied function, the sender stops here:

; Call the function to accept the received data
Call($SendDataToFunc,DllStructGetData($vs_msg, 1))

... until the the 'SendDataToFunc' returns (on the receiver side).

Edited by AdamusTorK

[quote name='PsaltyDS']so what is your ilegitimate purpose here?[/quote]

Link to comment
Share on other sites

OMG i'm such a dork, this is what happens when you have too much coffee.... helps if you have the _SendData variables around the right way.

This is cool thanks ;)

I'm trying to integrate it into one of my scripts (which I will credit you for of course) and have an interesting use for it, but its not playing...

Ive created a handler for certain audio files and integrated into the file right click menu (via adding the advanced file type info to the the registry ), problem is when you shift select multiple files, windows opens a new instance of the script for each one. (Incidentally the sendto folder doesn't do it that way, it passes all the file names to a single instance).

So what I want to achieve is a script that identifies the first running instance of itself, passes the file name its been given to the first instance and closes itself, which is what I came up with below, needs to be compiled to start.

Any ideas appreciated

CODE
_RunCheck()

Func _RunCheck()

$ll = ProcessList ( @ScriptName )

If $ll[1][1] <> @AutoItPID Then

;~ MsgBox ( 16, "INFO TO SEND", $CmdLine[1])

$ree = _SendData("MrCC", "Test")

MsgBox ( 16, "Error", @error)

Exit

Else

$MEM = _SetAsReceiver("MrCC")

MsgBox ( 16, "RECEIVERIS", "hwnd of the Local_ReceiverID_Name is "&$MEM )

_SetReceiverFunction("_Caught")

EndIf

EndFunc

Func _Caught($Catch)

MsgBox ( 16, "Caught", $Catch)

EndFunc

While 1

Sleep (1000)

WEnd

Edited by DK12000
Link to comment
Share on other sites

What about using _Singleton?

#include "Misc.au3"
if _Singleton("test123456",1) = 0 Then
    Msgbox(0,"","An occurence of test is already running")
   ;Do stuff here that you want to send to the first running script
    _SendData("MrCC", "Test")
    Exit;exit because we have done our part of the job
EndIf


Msgbox(0,"OK","the first occurence of script is running")
;do the job of the main script here
$MEM = _SetAsReceiver("MrCC")
MsgBox ( 16, "RECEIVERIS", "hwnd of the Local_ReceiverID_Name is "&$MEM )
_SetReceiverFunction("_Caught")

While 1
Sleep (1000)
WEnd


Func _Caught($Catch)
MsgBox ( 16, "Caught", $Catch)
EndFunc
Link to comment
Share on other sites

This is wonderfull. inspired me to create a debugger for compiled programs. This way I can enable a debugger to help me debug the scripts already in use. I dont always release the code of my files.

I will post this back in a few days.

Kind regards, Linux

Edit: I have a question. If what I intend to do is send a string, with always de lenght of 4 like: '0000', but send it a lot of times per second, how show I tweak your function get more speed and less compatible string lenght?

I tested this function with success to make another script where I will give you the credits of this. The bad part is that its DAM SLOW when it takes to send the data.

My script runs 100 times slower with a _send in after half of the lines.

Kind regards, Linux

Edited by Linux
You can help! Donate to AutoIt! or, visit ClimatePREDICTION.netMy posts:Travian Bot Example (100+ servers) BETAHow to Host you code/app for free! (unlimited team number) (Public or Private)"Sir, we're surrounded!" "Excellent. We can attack in any direction!"
Link to comment
Share on other sites

DAM SLOW when it takes to send the data

Something I was tring to tell you a few post b4. The sender is as fast as the reciver process the message. So my suggestion, If this is the only queue type of the many windows messaging, then try to create a simple message processer in the receiver main loop.

So again: the Sender only sends the data. Theres nothing wrong, except that its not going to return form the _SendMessage() function until the receiver returned from the ReveiveMSG() function. Thats why the receiver should only put the message into it's own message processing queue system, like an array, and return as fast as possible.

Heres what I'm thinking:

While 1
   sleep(100)
   If $aMessage[0]>=1 Then _ProcessMessages()
Wend

Func _ProcessMessages()
   While $aMessage[0]>=1
      Msgbox(0,@ScriptName,@ScriptName & " has received a message" & @crlf & $aMessage[1])
      _arraydelete($aMessage,1)
      $aMessage[0]-=1
   WEnd
EndFunc

Func _MyFunc2($vText)
   _ArrayAdd($aMessage,$vText)
   $aMessage[0]+=1
EndFunc


...instead of...


While 1
sleep(100)
Wend

Func _MyFunc2($vText)
    Msgbox(0,@ScriptName,@ScriptName & " has received a message" & @crlf & $vText)
    Exit
EndFunc

Dunno if its working its just a theoretical idea.

I'm using ChrisL's Code in my mPlayer's external lyrics downloader and only allowing 1 queued message (actual played song) and since I've put the message processing out of the receiving fuction I didnt meet any delay problem (you can imagine how disturbing would be when an external scripts stops the media player while its searching for the lyrics).

[quote name='PsaltyDS']so what is your ilegitimate purpose here?[/quote]

Link to comment
Share on other sites

  • 1 month later...

Martin did point this out too in the 3rd post, and I see where your coming from in your example code

While 1
   sleep(100)
   If $aMessage[0]>=1 Then _ProcessMessages()
Wend

Func _ProcessMessages()
   While $aMessage[0]>=1
      Msgbox(0,@ScriptName,@ScriptName & " has received a message" & @crlf & $aMessage[1])
      _arraydelete($aMessage,1)
      $aMessage[0]-=1
   WEnd
EndFunc

Func _MyFunc2($vText)
   _ArrayAdd($aMessage,$vText)
   $aMessage[0]+=1
EndFunc

When Martin suggested it previously I couldn't get my head around queuing the messages and using them without having some sort of manaul implementation like you have done i.e. having to use _ProcessMessages() in your own loop.

It really needs to do as you have done but automatically and that is where I'm not sure of the best route :P

Link to comment
Share on other sites

Hello to all,

I have made some modifications like:

1 - When the data is sent to the default or user definied function, the sender no more stops until the 'SendDataToFunc' returns (on the receiver side).

2 - You can set unlimited amount of Receiver functions

Need this: Enhanced Adlib function

I've already sussed out number one, similar principle but I'm using a callback to control an adlib function rather than using AutoIts built in adlib (which is what the Enhanced Adlib functions do), what I'm not sure of is which gets priority, if AutoIts adlib would have to wait if using a message box in your receiver function it might have to wait still for the user to close it. With the callback it doesn't. I know this because if I send 5 messages in a loop I can get 5 messgae boxes on my screen at once using the callback method.

I haven't tried your method with that senario but I have to go to work in a minute but I'll try and get a look later.

Number 2 sounds cool though :mellow:

Good work though!!

Link to comment
Share on other sites

Hi,

In this point, I agree with you... Please, you can put your example with callback method?

Edited: of turn for first version.

The first post has already been updated with a new include file which uses the callback

Edit: Download file has been updated because I left a #include in that wasn't needed

Edited by ChrisL
Link to comment
Share on other sites

  • 4 months later...

This is kick a** guys, and one I can actually understand how to use lol

Can't count how many times I could have used this.

In fact i'm about to use it now lol

Thanks :P

-Kenny

Edited by ken82m

 "I believe that when we leave a place, part of it goes with us and part of us remains... Go anywhere, when it is quiet, and just listen.. After a while, you will hear the echoes of all our conversations, every thought and word we've exchanged.... Long after we are gone our voices will linger in these walls for as long as this place remains."

Link to comment
Share on other sites

hmm I was hoping this would just work. Any simple way to send a 2-d array?

Without having to turn everything into strings and then reassemble it anyway?

Thanks,

Kenny

 "I believe that when we leave a place, part of it goes with us and part of us remains... Go anywhere, when it is quiet, and just listen.. After a while, you will hear the echoes of all our conversations, every thought and word we've exchanged.... Long after we are gone our voices will linger in these walls for as long as this place remains."

Link to comment
Share on other sites

hmm I was hoping this would just work. Any simple way to send a 2-d array?

Without having to turn everything into strings and then reassemble it anyway?

Thanks,

Kenny

ChrisL has used the WM_COPYDATA to sent the data but he has restricted his udf to strings. He could have sent different types of data and used the WPARAM to indicate the type of data being sent. If you examine how he has done it, and search for other examples of using WM_COPYDATA, you will find that you are not limited to strings. It might be easier to convert the array to strings and use this udf as you suggest but it depends on what you need to send and how much.

Serial port communications UDF Includes functions for binary transmission and reception.printing UDF Useful for graphs, forms, labels, reports etc.Add User Call Tips to SciTE for functions in UDFs not included with AutoIt and for your own scripts.Functions with parameters in OnEvent mode and for Hot Keys One function replaces GuiSetOnEvent, GuiCtrlSetOnEvent and HotKeySet.UDF IsConnected2 for notification of status of connected state of many urls or IPs, without slowing the script.
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...