I was interested to try a couple different approaches, and I found that the poor performance of ahklerner's function with greater number of controls was
not due to the use of Loop. Maybe no method using a scripted Loop will be as efficient as SKAN's method, but in ahklerner's case something else makes a much bigger difference: the conversion of each ClassNN back into a HWND, I believe.
Anyway, my first idea was to use EnumChildWindows. This is used internally by AutoHotkey to list controls and convert between HWND and ClassNN. The order in which EnumChildWindows enumerates controls determines the NN value and the order of controls in the list. The basic premise is to copy this in script, counting how many controls are encountered with the same class before the target control is found.
I think the main problem with that idea was the overhead of the callback required to enumerate controls. My second idea replaces the oft called script callback with a parsing loop, letting AutoHotkey do the control enumeration - i.e. WinGet,v,ControlListHwnd.
Each run of the benchmark begins with 16 controls. Each iteration calls each method 1000 times to retrieve the ClassNN of the last control, then shows the results. The average time in ms of each method and the factor by which the time increased are shown. Before continuing to the next iteration, the number of controls is doubled.
Code:
#NoEnv
DetectHiddenWindows, On
SetBatchLines, -1
SetFormat, FloatFast, 0.3
Gui, +LastFound
hWnd := WinExist()
i = 1000 ; iterations
n = 16 ; initial number of controls
Loop %n%
Gui, Add, Text, hwndhCtrl
Loop
{
TC := A_TickCount
Loop %i%
eControl_GetClassNN( hWnd,hCtrl )
TC1 := ( A_TickCount - TC ) / i
TC := A_TickCount
Loop %i%
lControl_GetClassNN( hWnd,hCtrl )
TC2 := ( A_TickCount - TC ) / i
TC := A_TickCount
Loop %i%
sControl_GetClassNN( hWnd,hCtrl )
TC3 := ( A_TickCount - TC ) / i
TC := A_TickCount
Loop %i%
aControl_GetClassNN( hWnd,hCtrl )
TC4 := ( A_TickCount - TC ) / i
; OutputDebug % n "`tE " TC1 " (" TC1/pTC1 ")`tL " TC2 " (" TC2/pTC2 ")`tS " TC3 " (" TC3/pTC3 ")`tA " TC4 " (" TC4/pTC4 ")"
MsgBox % "Controls: " n "`n`n"
. "E`t" TC1 "`t=prev*" TC1/pTC1 "`n"
. "L`t" TC2 "`t=prev*" TC2/pTC2 "`n"
. "S`t" TC3 "`t=prev*" TC3/pTC3 "`n"
. "A`t" TC4 "`t=prev*" TC4/pTC4 "`n"
Loop %n%
Gui, Add, Text, hwndhCtrl
n *= 2
pTC1 := TC1, pTC2 := TC2, pTC3 := TC3, pTC4 := TC4
}
; Lexikos: Find ClassNN by enumerating child controls and counting
; the number of controls of the target class.
eControl_GetClassNN(hWnd, hCtl)
{
static sCtl, sCls, sProc, sNN, sRet
if (A_EventInfo = sProc)
{
WinGetClass, cls, ahk_id %hWnd%
if (cls == sCls)
{
sNN += 1
if (hWnd = sCtl)
{
sRet := sCls sNN
return false ; stop
}
}
return true ; continue
}
if !sProc
sProc := RegisterCallback(A_ThisFunc,"F")
sCtl := hCtl
WinGetClass, sCls, ahk_id %sCtl%
sNN := 0
sRet := ""
DllCall("EnumChildWindows", "uint", hWnd, "uint", sProc, "uint", 0)
return sRet
}
; Lexikos: Find ClassNN by retrieving list of HWNDs and counting
; the number of controls of the target class.
lControl_GetClassNN(hwnd, hctl)
{
WinGet, hlist, ControlListHwnd, ahk_id %hwnd%
WinGetClass, tclass, ahk_id %hctl%
Loop, Parse, hlist, `n
{
WinGetClass, lclass, ahk_id %A_LoopField%
if (lclass == tclass)
{
nn += 1
if A_LoopField = %hctl%
return tclass nn
}
}
}
; SKAN
sControl_GetClassNN( hWnd,hCtrl ) { ; SKAN: www.autohotkey.com/forum/viewtopic.php?t=49471
WinGet, CH, ControlListHwnd, ahk_id %hWnd%
WinGet, CN, ControlList, ahk_id %hWnd%
LF:= "`n", CH:= LF CH LF, CN:= LF CN LF, S:= SubStr( CH, 1, InStr( CH, LF hCtrl LF ) )
StringReplace, S, S,`n,`n, UseErrorLevel
StringGetPos, P, CN, `n, L%ErrorLevel%
Return SubStr( CN, P+2, InStr( CN, LF, 0, P+2 ) -P-2 )
}
; ahklerner
aControl_GetClassNN(hWndWindow,hWndControl){
WinGet, ClassNNList, ControlList, ahk_id %hWndWindow%
Loop, PARSE, ClassNNList, `n
{
ControlGet, hwnd, hwnd,,%A_LoopField%,ahk_id %hWndWindow%
if (hWnd = hWndControl)
return A_LoopField
}
}
SKAN's method still wins, but my enumeration and looping methods come close with a reasonable number of controls.
Also note that SKAN's benchmark script fails completely on Windows 7.
SciCalc -> CalcFrame