I am writing a function that would loop imagesearch but I am having trouble figuring out how to pass a dynamic variable with the options allowed with Arrays (Such as Array0 which retrieves the total count of records in array, and Array%A_Index% which when used with a Loop displays each name as it goes through the list)
arrowList = C:\AHK\LeftArrow.png|C:\AHK\LeftArrow1.png|C:\AHK\GreenLeftArrow.png
StringSplit, arrowArray, arrowList, |
buildList = C:\AHK\build1.png|C:\AHK\build2.png|C:\AHK\build3.png|C:\AHK\build4.png|C:\AHK\build5.png
StringSplit, buildArray, buildList, |
SearchArray("arrowArray","buildArray")
SearchArray(ByRef x, ByRef y)
{
Loop, %x%
{
x2get := %xA_Index%
ImageSearch, imageX, imageY, 0, 0, A_ScreenWidth, A_ScreenHeight, *25 %x2get%
tooltip, searching for %x2get% , 0, 0
If ErrorLevel = 0
{
Loop, % y%0%
{
y2get := % y%A_Index%
ImageSearch, imageX, imageY, 0, 0, A_ScreenWidth, A_ScreenHeight, *25 %y2get%
tooltip, searching for %y2get% , 0, 0
If ErrorLevel = 0
{
MouseClick, Left, imageX, imageY,
Sleep 1000
}
}
}
}
}
You have some problems with how you're calling the variables. You're not actually using "real" arrays there, you're using what's called "pseudo-arrays". You can read about them in the documentation, here.
They're an old way AHK handled arrays, and I strongly suggest you try to move over to using "real" arrays in AHK. You should also update your version of AHK to the latest if you haven't already - http://ahkscript.org/download/.
I changed how the script called some variables, and it should work now, try this, note I've commented the lines I changed:
arrowList = C:\AHK\LeftArrow.png|C:\AHK\LeftArrow1.png|C:\AHK\GreenLeftArrow.png
StringSplit, arrowArray, arrowList, |
buildList = C:\AHK\build1.png|C:\AHK\build2.png|C:\AHK\build3.png|C:\AHK\build4.png|C:\AHK\build5.png
StringSplit, buildArray, buildList, |
SearchArray("arrowArray", "buildArray")
SearchArray(ByRef x, ByRef y)
{
Loop, % %x%0 ; Changed this line
{
x2get := %x% A_Index ; Changed this line
ImageSearch, imageX, imageY, 0, 0, %A_ScreenWidth%, %A_ScreenHeight%, *25 %x2get% ; Changed this line
tooltip, searching for %x2get% , 0, 0
If ErrorLevel = 0
{
Loop, % %y%0 ; Changed this line
{
y2get := % %y% A_Index ; Changed this line
ImageSearch, imageX, imageY, 0, 0, %A_ScreenWidth%, %A_ScreenHeight%, *25 %y2get% ; Changed this line
tooltip, searching for %y2get% , 0, 0
If ErrorLevel = 0
{
MouseClick, Left, %imageX%, %imageY% ; Changed this line
Sleep 1000
}
}
}
}
}
If you're interested in how a solution using "real" arrays would look, here's an example of that. Just make sure you're running the latest version of AHK before you try it, otherwise it might fail.
arrowList := "C:\AHK\LeftArrow.png|C:\AHK\LeftArrow1.png|C:\AHK\GreenLeftArrow.png"
arrowArray := StrSplit(arrowList, "|")
buildList := "C:\AHK\build1.png|C:\AHK\build2.png|C:\AHK\build3.png|C:\AHK\build4.png|C:\AHK\build5.png"
buildArray := StrSplit(buildList, "|")
SearchArray(arrowArray, buildArray)
SearchArray(firstArray, secondArray) {
; Iterate through the first array
for outerIndex, outerValue in firstArray {
; outerIndex = Index of the current element; 1, 2, etc...
; outerValue = The value of the string at that index
Tooltip, Searching for %outerValue%, 0, 0
ImageSearch, imageX, imageY, 0, 0, %A_ScreenWidth%, %A_ScreenHeight%, *25 %outerValue%
if (ErrorLevel = 0) {
; Iterate through the second array
for innerIndex, innerValue in secondArray {
; innerIndex = Index of the current element; 1, 2, etc...
; innerValue = The value of the string at that index
Tooltip, Searching for %innerValue%, 0, 0
ImageSearch, imageX, imageY, 0, 0, %A_ScreenWidth%, %A_ScreenHeight%, *25 %innerValue%
if (ErrorLevel = 0) {
MouseClick, Left, %imageX%, %imageY%
Sleep, 1000
}
}
}
}
}
Related
I'm trying to get my AHK Script to Work.
Basically i want to find a row of x Pixels with the Color 0x26FDFD (BGR). But i don't know the AHK Scripting language well wnough to think about a smart, clean and easy way to program that loop, that the starting point will be modified based on the last found coordinates.
However, here's what i got so far:
SysGet, VirtualWidth, 78
SysGet, VirtualHeight, 79
;Farbe nach der gesucht wird:
ColorVar := 0x26FDFD
i:=0
while i < 10
{
PixelSearch, FoundX, FoundY, 0, 0, VirtualWidth, VirtualHeight, %ColorVar%, 3, Fast
if (ErrorLevel = 2)
{
MsgBox Could not conduct the search.
return
}
else if (ErrorLevel = 1)
{
MsgBox Color could not be found on the screen.
return
}
else
{
MouseMove, %FoundX%, %FoundY%
MsgBox Found a Pixel at %FoundX%x%FoundY%.
;return
}
i++
}
Kinda stupid and basic question, but somehow i can't figure it out.
I just needed to store the X and Y coordinates at the end of each loop, then set the new startpoint accordingly.
here's the code:
i:=0
while i < 5
{
PixelSearch, FoundX, FoundY, startX%A_Index%, startY%A_Index%, VirtualWidth, VirtualHeight, %ColorVar%, 3, Fast
Switch ErrorLevel
{
Case 1:
MsgBox Website ueberpruefen!
return
Case 2:
MsgBox Makro ueberpruefen!
return
Default:
MouseMove, %FoundX%, %FoundY%
;MsgBox Found a Pixel at %FoundX%x%FoundY%.
nextLoop := A_Index + 1
startX%nextLoop% := FoundX + 1
startY%nextLoop% := FoundY
}
i++
}
;msgbox % "found 5 matches!, first at: " startX2 "x" startY2
return
Those if doesn't work, because test() doesn't take value from outside, but I have no clue how I should write code if I put it inside...
Any suggestions?
SetTimer, test, 2000 ;I started off with 1000 then worked up to this number to confirm this was happening
number := 1
test:
test()
return
test(){
if WinActive("program") {
ImageSearch, foundX, foundY, 2000, 0, 2560, 1440, target.png
if (ErrorLevel = 1) {
if (number = 1) {
CheckMap(z := "star.png")
number := 2
}
if (number = 2) {
CheckMap(z := "like.png")
number := 3
}
if (number = 3) {
CheckMap(z := "question.png")
number := 4
}
if (number = 4) {
CheckMap(z := "cross.png")
number := 1
}
}
}
}
CheckMap(x){
ImageSearch, foundX, foundY, 2000, 0, 2560, 1440, %x%
if (ErrorLevel = 0) {
MouseGetPos, StartX, StartY
Click, %foundX%, %foundY%
MouseMove, StartX, StartY
}
}
*[::
Suspend
Pause, 1
return
*]::
ExitApp
return
Found some help, that I needed static number, also fixed if to else if, now works perfect
SetTimer, test, 2000 ;
test:
test()
return
test(){
static number := 1
if WinActive("program") {
ImageSearch, foundX, foundY, 2000, 0, 2560, 1440, target.png
if (ErrorLevel = 1) {
if (number = 1) {
CheckMap(z := "star.png")
number := 2
} else if (number = 2) {
CheckMap(z := "like.png")
number := 3
} else if (number = 3) {
CheckMap(z := "question.png")
number := 4
} else if (number = 4) {
CheckMap(z := "cross.png")
number := 1
}
}
}
}
CheckMap(x){
ImageSearch, foundX, foundY, 2000, 0, 2560, 1440, %x%
if (ErrorLevel = 0) {
MouseGetPos, StartX, StartY
Click, %foundX%, %foundY%
MouseMove, StartX, StartY
}
}
*[::
Suspend
Pause, 1
return
*]::
ExitApp
return
I know how to capture mouse movement in general, relative to the screen, but what about in games where mouse movement is being used independently from the mouse's position on the screen?
For example, in a game your cursor is hidden but you can keep moving your mouse to the left infinitely and turn in circles, far further than there's room for your mouse to move on your screen. The game might lock your invisible mouse into the center, or let it move until it hits the edge of the window, but at that point any attempt to record the mouse's movement relative to the screen is useless.
So how can I capture / send raw mouse input. For example if I want to tell the player to turn 1000 degrees to the left via mouse input and record the mouse input from a player turning 1000 degrees to the left, how can I do both of those things?
My goal is ultimately to record a player's various control including mouse input to create one of those systems which record and play back user input. I've searched all over the ahk docs and Google and found nothing about capturing and sending raw mouse input.
This has been taken care of for you by a fantastic user on the AHK forums named evilC.
It's called mouse delta and tracks changes in raw mouse input.
I've posted one of the few different variants of his mouse delta script. Make sure you follow the link above to see all the different ones he has done.
Again, this is not my work.
; Instantiate this class and pass it a func name or a Function Object
; The specified function will be called with the delta move for the X and Y axes
; Normally, there is no windows message "mouse stopped", so one is simulated.
; After 10ms of no mouse movement, the callback is called with 0 for X and Y
Class MouseDelta {
State := 0
__New(callback){
;~ this.TimeoutFn := this.TimeoutFunc.Bind(this)
this.MouseMovedFn := this.MouseMoved.Bind(this)
this.Callback := callback
}
Start(){
static DevSize := 8 + A_PtrSize, RIDEV_INPUTSINK := 0x00000100
; Register mouse for WM_INPUT messages.
VarSetCapacity(RAWINPUTDEVICE, DevSize)
NumPut(1, RAWINPUTDEVICE, 0, "UShort")
NumPut(2, RAWINPUTDEVICE, 2, "UShort")
NumPut(RIDEV_INPUTSINK, RAWINPUTDEVICE, 4, "Uint")
; WM_INPUT needs a hwnd to route to, so get the hwnd of the AHK Gui.
; It doesn't matter if the GUI is showing, it still exists
Gui +hwndhwnd
NumPut(hwnd, RAWINPUTDEVICE, 8, "Uint")
this.RAWINPUTDEVICE := RAWINPUTDEVICE
DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
OnMessage(0x00FF, this.MouseMovedFn)
this.State := 1
return this ; allow chaining
}
Stop(){
static RIDEV_REMOVE := 0x00000001
static DevSize := 8 + A_PtrSize
OnMessage(0x00FF, this.MouseMovedFn, 0)
RAWINPUTDEVICE := this.RAWINPUTDEVICE
NumPut(RIDEV_REMOVE, RAWINPUTDEVICE, 4, "Uint")
DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
this.State := 0
return this ; allow chaining
}
SetState(state){
if (state && !this.State)
this.Start()
else if (!state && this.State)
this.Stop()
return this ; allow chaining
}
Delete(){
this.Stop()
;~ this.TimeoutFn := ""
this.MouseMovedFn := ""
}
; Called when the mouse moved.
; Messages tend to contain small (+/- 1) movements, and happen frequently (~20ms)
MouseMoved(wParam, lParam){
Critical
; RawInput statics
static DeviceSize := 2 * A_PtrSize, iSize := 0, sz := 0, pcbSize:=8+2*A_PtrSize, offsets := {x: (20+A_PtrSize*2), y: (24+A_PtrSize*2)}, uRawInput
static axes := {x: 1, y: 2}
; Get hDevice from RAWINPUTHEADER to identify which mouse this data came from
VarSetCapacity(header, pcbSize, 0)
If (!DllCall("GetRawInputData", "UPtr", lParam, "uint", 0x10000005, "UPtr", &header, "Uint*", pcbSize, "Uint", pcbSize) or ErrorLevel)
Return 0
ThisMouse := NumGet(header, 8, "UPtr")
; Find size of rawinput data - only needs to be run the first time.
if (!iSize){
r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", iSize, "UInt", 8 + (A_PtrSize * 2))
VarSetCapacity(uRawInput, iSize)
}
sz := iSize ; param gets overwritten with # of bytes output, so preserve iSize
; Get RawInput data
r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", &uRawInput, "UInt*", sz, "UInt", 8 + (A_PtrSize * 2))
x := 0, y := 0 ; Ensure we always report a number for an axis. Needed?
x := NumGet(&uRawInput, offsets.x, "Int")
y := NumGet(&uRawInput, offsets.y, "Int")
this.Callback.(ThisMouse, x, y)
;~ ; There is no message for "Stopped", so simulate one
;~ fn := this.TimeoutFn
;~ SetTimer, % fn, -50
}
;~ TimeoutFunc(){
;~ this.Callback.("", 0, 0)
;~ }
}
I'm trying to toggle mouse trails on and off with a keypress (windows key + q) using an autohotkey script, but can't get it working.
This is my code-
#q::
if DllCall("SystemParametersInfo", 94) < 2
{
DllCall("SystemParametersInfo", UInt, 0x005E, UInt, 0, UInt, 9, UInt, 0)
}else {
DllCall("SystemParametersInfo", UInt, 0x005E, UInt, 0, UInt, 0, UInt, 0)
}
return
Are you able to help?
First read the value using SPI_GETMOUSETRAILS into a variable by reference (intP) then set the new value using SPI_SETMOUSETRAILS:
#q::
DllCall("SystemParametersInfo", int,SPI_GETMOUSETRAILS:=0x5E, int,0, intP,length, int,0)
length := length > 1 ? 0 : 9
DllCall("SystemParametersInfo", int,SPI_SETMOUSETRAILS:=0x5D, int,length, int,0, int,0)
return
I am trying to get the contents of a listview control in another application using AHK. I tried using
ControlGet, List, List,, ListViewWndClass1, WindowTitle
in AutoHotKey but this did not work. Is there a way to get listview control contents using AHK PostMessage / SendMessage or DllCall( "SendMessage"?
Try this:
GetListViewItemText(item_index, sub_index, ctrl_id, win_id)
{
;const
MAX_TEXT = 260
VarSetCapacity(szText, MAX_TEXT, 0)
VarSetCapacity(szClass, MAX_TEXT, 0)
ControlGet, hListView, Hwnd, , %ctrl_id%, ahk_id %win_id%
DllCall("GetClassName", UInt,hListView, Str,szClass, Int,MAX_TEXT)
if (DllCall("lstrcmpi", Str,szClass, Str,"SysListView32") == 0 || DllCall("lstrcmpi", Str,szClass, Str,"TListView") == 0)
{
GetListViewText(hListView, item_index, sub_index, szText, MAX_TEXT)
}
return %szText%
}
GetListViewText(hListView, iItem, iSubItem, ByRef lpString, nMaxCount)
{
;const
NULL = 0
PROCESS_ALL_ACCESS = 0x001F0FFF
INVALID_HANDLE_VALUE = 0xFFFFFFFF
PAGE_READWRITE = 4
FILE_MAP_WRITE = 2
MEM_COMMIT = 0x1000
MEM_RELEASE = 0x8000
LV_ITEM_mask = 0
LV_ITEM_iItem = 4
LV_ITEM_iSubItem = 8
LV_ITEM_state = 12
LV_ITEM_stateMask = 16
LV_ITEM_pszText = 20
LV_ITEM_cchTextMax = 24
LVIF_TEXT = 1
LVM_GETITEM = 0x1005
SIZEOF_LV_ITEM = 0x28
SIZEOF_TEXT_BUF = 0x104
SIZEOF_BUF = 0x120
SIZEOF_INT = 4
SIZEOF_POINTER = 4
;var
result := 0
hProcess := NULL
dwProcessId := 0
if lpString <> NULL && nMaxCount > 0
{
DllCall("lstrcpy", Str,lpString, Str,"")
DllCall("GetWindowThreadProcessId", UInt,hListView, UIntP,dwProcessId)
hProcess := DllCall("OpenProcess", UInt,PROCESS_ALL_ACCESS, Int,false, UInt,dwProcessId)
if hProcess <> NULL
{
;var
lpProcessBuf := NULL
hMap := NULL
hKernel := DllCall("GetModuleHandle", Str,"kernel32.dll", UInt)
pVirtualAllocEx := DllCall("GetProcAddress", UInt,hKernel, Str,"VirtualAllocEx", UInt)
if pVirtualAllocEx == NULL
{
hMap := DllCall("CreateFileMapping", UInt,INVALID_HANDLE_VALUE, Int,NULL, UInt,PAGE_READWRITE, UInt,0, UInt,SIZEOF_BUF, UInt)
if hMap <> NULL
lpProcessBuf := DllCall("MapViewOfFile", UInt,hMap, UInt,FILE_MAP_WRITE, UInt,0, UInt,0, UInt,0, UInt)
}
else
{
lpProcessBuf := DllCall("VirtualAllocEx", UInt,hProcess, UInt,NULL, UInt,SIZEOF_BUF, UInt,MEM_COMMIT, UInt,PAGE_READWRITE)
}
if lpProcessBuf <> NULL
{
;var
VarSetCapacity(buf, SIZEOF_BUF, 0)
InsertIntegerSL(LVIF_TEXT, buf, LV_ITEM_mask, SIZEOF_INT)
InsertIntegerSL(iItem, buf, LV_ITEM_iItem, SIZEOF_INT)
InsertIntegerSL(iSubItem, buf, LV_ITEM_iSubItem, SIZEOF_INT)
InsertIntegerSL(lpProcessBuf + SIZEOF_LV_ITEM, buf, LV_ITEM_pszText, SIZEOF_POINTER)
InsertIntegerSL(SIZEOF_TEXT_BUF, buf, LV_ITEM_cchTextMax, SIZEOF_INT)
if DllCall("WriteProcessMemory", UInt,hProcess, UInt,lpProcessBuf, UInt,&buf, UInt,SIZEOF_BUF, UInt,NULL) <> 0
if DllCall("SendMessage", UInt,hListView, UInt,LVM_GETITEM, Int,0, Int,lpProcessBuf) <> 0
if DllCall("ReadProcessMemory", UInt,hProcess, UInt,lpProcessBuf, UInt,&buf, UInt,SIZEOF_BUF, UInt,NULL) <> 0
{
DllCall("lstrcpyn", Str,lpString, UInt,&buf + SIZEOF_LV_ITEM, Int,nMaxCount)
result := DllCall("lstrlen", Str,lpString)
}
}
if lpProcessBuf <> NULL
if pVirtualAllocEx <> NULL
DllCall("VirtualFreeEx", UInt,hProcess, UInt,lpProcessBuf, UInt,0, UInt,MEM_RELEASE)
else
DllCall("UnmapViewOfFile", UInt,lpProcessBuf)
if hMap <> NULL
DllCall("CloseHandle", UInt,hMap)
DllCall("CloseHandle", UInt,hProcess)
}
}
return result
}
; *********************************
; Required functions - ExtractInteger, InsertInteger
; - original versions from Version 1.0.44.06 of the AutoHotkey help file
; by Chris Mallett
; // Renamed in case someone is using a modified version of these functions
; // somewhere else in their code
; *********************************
ExtractIntegerSL(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4)
; pSource is a string (buffer) whose memory area contains a raw/binary integer at pOffset.
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
; pSource must be ByRef to avoid corruption during the formal-to-actual copying process
; (since pSource might contain valid data beyond its first binary zero).
{
Loop %pSize% ; Build the integer by adding up its bytes.
result += *(&pSource + pOffset + A_Index-1) << 8*(A_Index-1)
if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
return result ; Signed vs. unsigned doesn't matter in these cases.
; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
return -(0xFFFFFFFF - result + 1)
}
; *********************************
InsertIntegerSL(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity. To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}
; *********************************
Should work somewhat like this:
WinGet, hWnd, ID, My WinTitle
GetListViewItemText(1, 1, "ListViewWndClass1", hWnd)
source: http://www.autohotkey.com/board/topic/18299-reading-listview-of-another-app/