How to send a string between scripts using sendmessage?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

How to send a string between scripts using sendmessage?

Post by fabricio234 » 19 Oct 2021, 20:04

At the sendmessage docs it says:
A string may be sent via wParam or lParam by specifying the address of a variable. The following example uses the address operator (&) to do this:
SendMessage, 0xC, 0, &MyVar, ClassNN, WinTitle ; 0XC is WM_SETTEXT
I tried on the first script:

Code: Select all

MyVar := "Hello"
SendMessage, 0x99, &MyVar, &MyVar,, test
Return
and in the second script:

Code: Select all

OnMessage(0x99, "Msg")
Gui, Show, w200 h200, test
return

Msg(wParam, lParam) {
  MsgBox wParam %wParam% lParam: %lParam%
}
but the output of the msgbox is only numbers, am i doing something wrong?

colt
Posts: 291
Joined: 04 Aug 2014, 23:12
Location: Portland Oregon

Re: How to send a string between scripts using sendmessage?

Post by colt » 21 Oct 2021, 15:52

I am no expert but this is how I understand it.
What you are sending through sendmessage is the pointer to the memory that stores the word hello. Hello is stored in memory at &MyVar. To see this in action see this test case.

Code: Select all

var := "TEST"

ptr := &var

msgbox % *ptr

msgbox % chr(*ptr) ;convert byte at pointer to character

loop % strLen(var) ;parse the variable using pointers
{
	msgbox % chr(*(ptr+A_index)) ;increment pointer to next char offset
}
I don't think that numget can read the memory of another process though. At least it was crashing for me.
So what you can do instead is send the pid and memory location through you message. Then you can use ReadProcessMemory to get the value at your address.

Code: Select all

MyVar := "TEST"
MyPid := DllCall("GetCurrentProcessId")
SendMessage, 0x99, &MyVar, MyPid ,, test
Return

Code: Select all

OnMessage(0x99, "Msg")
Gui, Show, w200 h200, test
return

Msg(wParam, lParam) 
{	
	MsgBox memory address %wParam% sender pid: %lParam%
	loop 4
	{
		msgbox % chr(ReadMemory(wParam+A_index,lParam)) ;convert byte to character
	}		
}

ReadMemory(MADDRESS,pid) ;https://www.autohotkey.com/board/topic/33888-readmemory-function/
{
	;modified to read single char
	VarSetCapacity(MVALUE,1,0)
	ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", pid, "UInt")
	DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Str",MVALUE,"UInt",1,"UInt *",0)	
	return *(&MVALUE)	
}
But all this is probably not what you want anyway. If you want to send variables between scripts then you probably want to use wm_copydata.
Example here https://www.autohotkey.com/board/topic/73977-process-communication-wm-copydata/

ahk7
Posts: 574
Joined: 06 Nov 2013, 16:35

Re: How to send a string between scripts using sendmessage?

Post by ahk7 » 21 Oct 2021, 16:26

Have a look at ObjRegisterActive by Lexikos, very easy to use viewtopic.php?t=6148


lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to send a string between scripts using sendmessage?

Post by lexikos » 22 Oct 2021, 03:34

When you send a WM_SETTEXT message as in the quoted example, the system knows that it must copy the string between processes. Similarly, if you use WM_COPYDATA, the system knows that it must copy the data referenced by lParam (a struct containing a number and a pointer to data of specified length) between processes.

If you use some other arbitrary message which doesn't have predefined string parameters, the target window will just get whatever number you specified. While that may be the address of a string within the source process, interpreting it as an address within the target process is not safe and will not work.

Even if you are using a system message which copies the parameter between processes for you, OnMessage still passes the parameter value to you as a number. If that number is the address of a string, you can use StrGet to retrieve its value. Chr with the deref (* prefix) operator will also work if used correctly, but note that *address reads a byte, which is never a complete character unless you are using an ANSI version of AutoHotkey.

WM_COPYDATA is designed specifically for sending data between processes, but it is a little more complex than needed for just one string.

This would work:

Code: Select all

Gui, Show, w200 h200, test
OnMessage(0xC, "Msg")  ; WM_SETTEXT := 0xC
Msg(wParam, lParam) {
    MsgBox % StrGet(lParam)
    return 0  ; This prevents the GUI title from changing.
}

Code: Select all

MyVar := "Hello"
SendMessage, 0xC, 0, &MyVar,, test
If you prefer not to "overload" the WM_SETTEXT message, since it already has a purpose (change the window title), you can use some other system message which has a string parameter. One such message is EM_REPLACESEL, which normally has meaning only to the Edit control. You can just replace 0xC with 0xC2 in the example above (and return 0 can be removed or left as is).

There is no need to show a visible window for this if the sending script can identify the receiver by script name. For example:

Code: Select all

; receiver.ahk
OnMessage(0xC2, "Msg")
Msg(wParam, lParam) {
    MsgBox % StrGet(lParam)
}

Code: Select all

MyVar := "Hello"
DetectHiddenWindows, On
SetTitleMatchMode, 2
SendMessage, 0xC2, 0, &MyVar,, receiver.ahk ahk_class AutoHotkey

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to send a string between scripts using sendmessage?

Post by teadrinker » 22 Oct 2021, 06:10

lexikos wrote: This would work
In some cases it won't work, for example if user open menu and at this moment the WM_SETTEXT arrives, the window title will be changed.

iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: How to send a string between scripts using sendmessage?

Post by iseahound » 22 Oct 2021, 13:13

For larger data a handle to a filemapping object is recommended.

Sharing Files and Memory

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to send a string between scripts using sendmessage?

Post by lexikos » 22 Oct 2021, 22:02

teadrinker wrote:
22 Oct 2021, 06:10
In some cases it won't work, for example if user open menu and at this moment the WM_SETTEXT arrives, the window title will be changed.
If you use EM_REPLACESEL, the window title will not change but neither will the script detect the message (if there is a menu open).

This is a limitation of AutoHotkey v1 relating to all messages that start new threads, and applies equally to the WM_COPYDATA technique.

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to send a string between scripts using sendmessage?

Post by teadrinker » 24 Oct 2021, 05:53

Yeah, but I meant that using WM_SETTEXT may cause side effects unlike WM_COPYDATA.

fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: How to send a string between scripts using sendmessage?

Post by fabricio234 » 24 Oct 2021, 14:38

@lexikos do you know any message in which your example could work using PostMessage?

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to send a string between scripts using sendmessage?

Post by lexikos » 24 Oct 2021, 23:38

I do not believe there are any. In order for the system to marshal a struct or string, it must be able to guarantee that the struct or string will remain in memory at the address it is given until it is done, and must be able to free the memory after the recipient is done processing the message. There is no way for anyone but the recipient to know when they are finished with a posted message's parameters.

Messages which are intended to be posted therefore always have parameters that are passed directly by value (no more than 32 or 64 bits each).

teadrinker wrote:Yeah, but I meant that using WM_SETTEXT may cause side effects unlike WM_COPYDATA.
Your post had other misleading implications, hence my reply.

Post Reply

Return to “Ask for Help (v1)”