MsgBox %FADDRESS%
returns the current value of the variable stored at the memory address.
You need to simply add/+ the last offset, not perform a readmemory on it.
I.e
FADDRESS := ReadMemory(ReadMemory(getProcessBaseAddress(PROGRAM) + MADDRESS, PROGRAM) + Offset_1, PROGRAM) + Offset_2
Here is a function based on yours which can write to an address with as many offsets as you wish - or none at all.
WriteMemory(program, value, address, size := 4, aOffsets*)
{
winget, pid, PID, %PROGRAM%
address += getProcessBaseAddress(program)
if aOffsets.maxIndex()
{
lastOffset := aOffsets.Remove() ;remove the highest key so can use pointer() to find final memory address (minus the last offset)
address := pointer(program, address, aOffsets*) + lastOffset
}
ProcessHandle := DllCall("OpenProcess", "int", 2035711, "Int", 0, "UInt", PID, "UInt")
DllCall("WriteProcessMemory", "UInt", ProcessHandle, "UInt", address, "ptr*", value, "Uint", size, "Uint*", bytesWritten)
DllCall("CloseHandle", "int", ProcessHandle)
return bytesWritten
}
Pointer function
; Can pass an array of offsets by using *
; eg, pointer(game, base, [0x10, 0x30, 0xFF]*)
; or a := [0x10, 0x30, 0xFF]
; pointer(game, base, a*)
; or just type them in manually
pointer(game, base, offsets*)
{
For index, offset in offsets
{
if (index = offsets.maxIndex() && A_index = 1)
pointer := offset + ReadMemory(base, game)
Else
{
IF (A_Index = 1)
pointer := ReadMemory(offset + ReadMemory(base, game), game)
Else If (index = offsets.MaxIndex())
pointer += offset
Else pointer := ReadMemory(pointer + offset, game)
}
}
Return ReadMemory(offsets.maxIndex() ? pointer : base, game)
}
Also this version of getBaseAddress() will work with 64bit versions of AHK:
; Return values:
; Null The process's window couldn't be found.
; 0 The GetWindowLong or GetWindowLongPtr call failed.
; Non-Zero The base address of the process (success).
getProcessBaseAddress(WindowTitle, windowMatchMode := "3") ;WindowTitle can be anything ahk_exe ahk_class etc
{
if (windowMatchMode && A_TitleMatchMode != windowMatchMode)
{
mode := A_TitleMatchMode ; This is a string and will not contain the 0x prefix
StringReplace, windowMatchMode, windowMatchMode, 0x ; remove hex prefix as SetTitleMatchMode will throw a run time error. This will occur if integer mode is set to hex and matchmode param is passed as an number not a string.
SetTitleMatchMode, %windowMatchMode% ;mode 3 is an exact match
}
WinGet, hWnd, ID, %WindowTitle%
if mode
SetTitleMatchMode, %mode% ; In case executed in autoexec
if !hWnd
return ; return blank failed to find window
return DllCall(A_PtrSize = 4 ; If DLL call fails, returned value will = 0
? "GetWindowLong"
: "GetWindowLongPtr"
, "Ptr", hWnd, "Int", -6, A_Is64bitOS ? "Int64" : "UInt")
; For the returned value when the OS is 64 bit use Int64 to prevent negative overflow when AHK is 32 bit and target process is 64bit
; however if the OS is 32 bit, must use UInt, otherwise the number will be huge (however it will still work as the lower 4 bytes are correct)
; Note - it's the OS bitness which matters here, not the scripts/AHKs
}
Im a bit bored, so I might write a memory class which wraps all the read/write memory functions into one thing and includes strings as well.
Cheers.
Edit: Fixed an error in the pointer/write memory function.