Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Function - Control_GetClassNN() - Get a control ClassNN


  • Please log in to reply
13 replies to this topic
ahklerner
  • Members
  • 1386 posts
  • Last active: Oct 08 2014 10:29 AM
  • Joined: 26 Jun 2006
I searched in vain for something to do this...
So here it is.
Gets the ClassNN for a given hWnd
Some commands allow usage of ClassNN for a control, however there is not a function to get the ClassNN of a control. all base functions return ClassName only.

i borrowed part of this function from SKAN....

;Control_GetClassNN()
;by ahklerner
;

Control_GetClassNN(hWndWindow,hWndControl){
	DetectHiddenWindows, On
	WinGet, ClassNNList, ControlList, ahk_id %hWndWindow%
	Loop, PARSE, ClassNNList, `n
	{
		ControlGet, hwnd, hwnd,,%A_LoopField%,ahk_id %hWndWindow%
		if (hWnd = hWndControl)
			return A_LoopField
	}
}

;return

;Test script
Gui, +LastFound
hWindow := WinExist()
Gui, Add, Text, hwndhWnd, some text
Gui, Show
WinGetClass, ClassNN, ahk_id %hWnd%
MsgBox % Control_GetClassNN(hWindow,hWnd)
ExitApp

Posted Image
ʞɔпɟ əɥʇ ʇɐɥʍ

VxE
  • Moderators
  • 3622 posts
  • Last active: Dec 24 2015 02:21 AM
  • Joined: 07 Oct 2006
There is another way too...

ahklerner
  • Members
  • 1386 posts
  • Last active: Oct 08 2014 10:29 AM
  • Joined: 26 Jun 2006
i was interested in the dllcall version, but it just gets classname and not classNN :(
Posted Image
ʞɔпɟ əɥʇ ʇɐɥʍ

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Avoiding LOOP...

Control_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
  • Members
  • 1386 posts
  • Last active: Oct 08 2014 10:29 AM
  • Joined: 26 Jun 2006
Very Nice! mine takes fewer query performance counter increments. at least here
freq := 0
Gui, +LastFound
hWindow := WinExist()
Gui, Add, Text, hwndhWnd, some text
Gui, Show
WinGetClass, ClassNN, ahk_id %hWnd%
;MsgBox % Control_GetClassNN(hWindow,hWnd)
;ExitApp

Loop 1000
{
	freq += 100
	DllCall("QueryPerformanceCounter", "Int64 *", CounterBefore1)
	Loop %freq%
		hWnd := Control_GetClassNN(hWindow,hWnd)
	DllCall("QueryPerformanceCounter", "Int64 *", CounterAfter1)
	DllCall("QueryPerformanceCounter", "Int64 *", CounterBefore2)
	Loop %freq%
		hWnd2 := sControl_GetClassNN(hWindow,hWnd)
	DllCall("QueryPerformanceCounter", "Int64 *", CounterAfter2)
	DllCall("QueryPerformanceFrequency","int64*",f)
	avg1 := (CounterAfter1 - CounterBefore1) / f
	avg2 := (CounterAfter2 - CounterBefore2) / f
	MsgBox % "queryperformancecounterfrequency: " . f. "`nloops: " . freq . "`navg QPC time is ahklerner:" . avg1 . "`navg QPC time is skan:" . avg2
}
Control_GetClassNN(hWndWindow,hWndControl){
   DetectHiddenWindows, On
   WinGet, ClassNNList, ControlList, ahk_id %hWndWindow%
   Loop, PARSE, ClassNNList, `n
   {
      ControlGet, hwnd, hwnd,,%A_LoopField%,ahk_id %hWndWindow%
      if (hWnd = hWndControl)
         return A_LoopField
   }
}


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 )
}

Posted Image
ʞɔпɟ əɥʇ ʇɐɥʍ

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

mine takes fewer query performance counter increments. at least here

; ...
hWindow := WinExist()
[color=red]Gui, Add, Text, hwndhWnd, some text[/color]
Gui, Show
; ...


You are creating only a single control. :roll:
In real world, the control list could be long where the Loop command can never match the speeds of String manipulation commands.

SetBatchLines -1
Run Calc
WinWait, Calculator ahk_class SciCalc
hWnd := WinExist( "Calculator ahk_class SciCalc" )
SendMessage, 0x111, 304,,, ahk_id %hWnd% ; MenuSelect : View > Scientific
ControlGet, hCtrl, Hwnd,, Button72, ahk_id %hWnd%

TC := A_TickCount
Loop 1000
  sControl_GetClassNN( hWnd,hCtrl )
TC1 := ( A_TickCount - TC ) / 1000

TC := A_TickCount
Loop 1000
  Control_GetClassNN( hWnd,hCtrl )
TC2 := ( A_TickCount - TC ) / 1000

MsgBox, 0, in Milliseconds, % "skan:`t"TC1 "`nahkl:`t" TC2

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%
 Clipboard := CN
 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 )
}

Control_GetClassNN(hWndWindow,hWndControl){
   DetectHiddenWindows, On
   WinGet, ClassNNList, ControlList, ahk_id %hWndWindow%
   Loop, PARSE, ClassNNList, `n
   {
      ControlGet, hwnd, hwnd,,%A_LoopField%,ahk_id %hWndWindow%
      if (hWnd = hWndControl)
         return A_LoopField
   }
}


ghee22
  • Members
  • 36 posts
  • Last active: Nov 12 2009 04:44 PM
  • Joined: 04 Jul 2009
Micahs's script may be useful.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
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.
#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

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
@Lexikos: Thanks for the info and the interesting benchmark. :)

nick
  • Members
  • 549 posts
  • Last active: Jul 03 2010 09:31 PM
  • Joined: 24 Aug 2005
FYI:
GetClassNN(HW, HC) { ; HW : Window's HWND, HC : Control's HWND
   VarSetCapacity(CL,256,0), HA := 0
   , DllCall("GetClassName", UInt,HC, Str,CL, Int,255)
   While HA := DllCall("FindWindowEx", UInt,HW, UInt,HA, UInt,&CL, UInt,0)
      If (HA = HC)
         Return CL . A_Index
   Return False
}

nick :wink: