Now that this works in 64-bit i'd like to share my changes, which also includes the various fixes from previous posters.
Most notable thing i've changed is that it no longer uses window handles, instead i'm using process name or id. Not sure how good idea this is but it appears to work just fine and no longer deals with GUI(s) or window handles. Also this one does handle the SendMessage more strictly, as i think it might be better to know whether the target process actually processed the message or not. For example if you are accidentally sending to a wrong process/window, one that is not handling the WM_COPYDATA message, then there was no feedback if the message was really received.
Another one is that instead of passing the window handle as the SendMessage wParam, i'm using the process id. According to
WM_COPYDATA it is suppose to be the window handle, but i've not seen any side effects of using the process id instead. Not sure if this such a good idea(?)
Also IPC_Send returns empty/false on success and error message string on failure.
I've tested mostly between two AHK programs and with the DotNet examples. However the DotNet ones do not work perfectly as it does not appear to use Unicode(??), at least the message is cut off after the first letter. Also since this version handles the return value of the SendMessage more strictly, AHK script will say that the send failed, when in fact the DotNet program did receive and display the message (well, part of it...).
I've update both the example script(s) and IPC.ahk.
IPC.ahk
Code:
/*
Function: Send
Send the message to another process (receiver).
Parameters:
PidOrName - Process name or ID
Data - Data to be sent, by default empty. Optional.
Port - Port, by default 100. Positive integer. Optional.
DataSize - If this parameter is used, Data contains pointer to the buffer holding binary data.
Omit this parameter to send textual messages to the receiver.
Remarks:
The data being passed must not contain pointers or other references to objects not accessible to the script receiving the data.
While this message is being sent, the referenced data must not be changed by another thread of the sending process.
The receiving script should consider the data read-only. The receiving script should not free the memory referenced by Data parameter.
If the receiving script must access the data after function returns, it must copy the data into a local buffer.
Returns:
Returns EMPTY / FALSE on success. Error message on failure.
*/
IPC_Send(PidOrName, Data="", Port=100, DataSize="") {
static WM_COPYDATA = 74, INT_MAX=2147483647
if Port not between 0 AND %INT_MAX%
return A_ThisFunc "> Port number is not in a positive integer range: " Port
Process, Exist, %PidOrName%
if (!PidOrName || !(PID := ErrorLevel))
return A_ThisFunc "> Process not found: " PidOrName
if (DataSize = "")
DataSize := (StrLen(Data)+1) * (!!A_IsUnicode + 1), pData := &Data, Port := -Port ;use negative port for textual messages
else pData := Data
VarSetCapacity(COPYDATA, 3*A_PtrSize)
, NumPut(Port, COPYDATA, 0, "Int")
, NumPut(DataSize, COPYDATA, A_PtrSize, "UInt")
, NumPut(pData, COPYDATA, 2*A_PtrSize, "Ptr")
PrevDetectHiddenWindows := A_DetectHiddenWindows
DetectHiddenWindows, On
SendMessage, WM_COPYDATA, DllCall("GetCurrentProcessId"), ©DATA,, ahk_pid %PID%
DetectHiddenWindows, %PrevDetectHiddenWindows%
return ErrorLevel="FAIL" || !ErrorLevel ? A_ThisFunc "> SendMessage failed, ErrorLevel=" ErrorLevel : false
}
/*
Function: SetHandler
Set the data handler.
Parameters:
Handler - Function that will be called when data is received.
Handler:
> Handler(PID, Data, Port, DataSize)
PID - Process ID of the process passing data.
Data - Data that is received.
Port - Data port.
DataSize - If DataSize is not empty, Data is pointer to the actuall data. Otherwise Data is textual message.
*/
IPC_SetHandler( Handler ){
static WM_COPYDATA = 74
if !IsFunc( Handler )
return A_ThisFunc "> Invalid handler: " Handler
OnMessage(WM_COPYDATA, "IPC_onCopyData")
IPC_onCopyData(Handler, "")
}
IPC_onCopyData(WParam, LParam) {
static Handler
if Lparam =
return Handler := WParam
port := NumGet(Lparam+0, 0, "Int"), data := NumGet(Lparam+2*A_PtrSize, 0, "Ptr")
if port < 0
data := DllCall("MulDiv", "Int", data, "Int",1, "Int", 1, "str"), port := -port
else size := NumGet(LParam+A_PtrSize, 0, "UInt")
%handler%(WParam, data, port, size)
return 1
}
Script1.ahk / Script2.ahkRemember to change the "target" variable!
Code:
#SingleInstance, off ;allow multiple instances
target := "Script2"
;target := "Script1"
stress := 1000, x:=300
;========================
Gui, +LastFound +AlwaysOnTop
CPID := DllCall("GetCurrentProcessId")
Gui, Font, s10
Gui, Add, Edit, vMyMsg w200 , Message to %target%
Gui, Add, Edit, x+0 vMyPort w50, 100
Gui, Font, s8
Gui, Add, Button, x+5 gOnSend , Send
Gui, Add, Button, x+5 gOnSendBinary , Send Binary
Gui, Add, Button, x+5 gOnStress , Stress
Gui, Add, ListBox,xm w440 h300 vMyLB,
Gui, Show, x%x% AutoSize
GuiControl, , MyLB, This script PID: %CPID%
IPC_SetHandler("OnData")
return
OnData(PID, Data, Port, Size) {
global myLB
if Size =
s = %Port% PID: %PID% Message: %Data%
else {
x := NumGet(Data+0), y := NumGet(Data+4)
s = %Port% PID: %PID% Binary Data: POINT (%x%, %y%) DataSize: %Size%
}
GuiControl, , MyLB, %s%
}
OnSend:
Gui, Submit, NoHide
WinGet, TPID, PID, %target%
if IPC_Send(TPID, MyMsg, MyPort)
MsgBox Sending failed
return
OnStress:
Gui, Submit, NoHide
WinGet, TPID, PID, %target%
if !(TPID)
MsgBox Host process doesn't exist
loop, %stress%
IPC_Send(TPID, MyMsg " : " A_Index, MyPort)
return
OnSendBinary:
Gui, Submit, NoHide
VarSetCapacity(POINT, 8), NumPut(2000, POINT), NumPut(8000, POINT, 4)
WinGet, TPID, PID, %target%
if IPC_Send(TPID, &POINT, MyPort, 8)
MsgBox Sending failed
return
GuiClose:
ExitApp
return
#include ..\IPC.ahk
Again, feedback is much appreciated!