_WinGetControls() - Controls eines Fensters auslesen

  • Moin,

    weil es hier gerade nachgefragt worden ist und ich hier nichts wirklich passendes gefunden habe:

    AHK kennt für diesen Zweck die Anweisung WinGet, OutputVar, ControlList. Ich finde, das ist ein "nice to have", deshalb habe ich die Funktion _WinGetControls() erstellt:

    WinGetControls.au3
    [autoit]

    #include-once

    [/autoit] [autoit][/autoit] [autoit]

    ; #DEMOFUNCTION# ================================================================================================================
    ; Name...........: _WinGetControls
    ; Description ...: Liefert ein 2D-Array für die Controls eines Fensters.
    ; Das Array enhält für jedes Control die folgenden Informationen:
    ; |[0] HWND - HWND des Controls
    ; |[1] Class - Klasse des Controls
    ; |[2] NN - ClassNN des Controls
    ; |[3] ID - ID des Controls
    ; |[4] Visible - Sichtbar? 1 = ja, 0 = nein
    ; Das Feld Array[0][0] enthält die Anzahl der Controls.
    ; Syntax.........: _WinGetControls($hWnd)
    ; Parameters ....: $hWnd - HWND des Fensters (z.B. Rückgabewert von GUICreate)
    ; Return values .: Erfolg: Controlarray
    ; Fehler: False, @error = 1
    ; Author ........: Großvater (http://www.autoit.de)
    ; Modified.......:
    ; Remarks .......:
    ; Related .......:
    ; Link ..........:
    ; Example .......:
    ; ===============================================================================================================================
    Func _WinGetControls($hWnd)
    Local $hCB
    If Not IsHWnd($hWnd) Then Return SetError(1, 0, False)
    __WinGetControlsAddControl("Init", 0)
    $hCB = DLLCallbackRegister("__WinGetControlsAddControl", "Int", "HWND;LPARAM")
    DllCall("User32.dll", "Int", "EnumChildWindows", "HWND", $hWnd, "Ptr", DllCallbackGetPtr($hCB), "LPARAM", $hWnd)
    If @error Then Return SetError(DllCallbackFree($hCB), 0, False)
    DllCallbackFree($hCB)
    Return __WinGetControlsAddControl("Result", 0)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; #INTERNAL_USE_ONLY# ===========================================================================================================
    ; Name...........: __WinGetControlsAddControl
    ; Description ...: Callback-Funktion für DLLCall "EnumChildWindows" in _WinGetControls
    ; Remarks .......: Weil ich globale Variable für die Rückgabe von Funktionswerten nicht mag und die Implementierung
    ; der statischen Arrays recht rudimentär ist, sind die maximale Anzahl von Controls auf 1024
    ; und die maximale Anzahl von Klassen auf 256 begrenzt.
    ; ===============================================================================================================================
    Func __WinGetControlsAddControl($hWnd, $lParam)
    Local Static $UControls = 1024
    Local Static $UClasses = 256
    Local Static $aControls[$UControls + 1][5]
    Local Static $aClasses[$UClasses + 1][2]
    Local $aResult, $C, $Class, $ClassExist, $ID, $NN
    Switch $hWnd
    Case "Init"
    $aControls[0][0] = 0
    $aClasses[0][0] = 0
    Return
    Case "Result"
    $C = $aControls[0][0] + 1
    $aResult = $aControls
    Redim $aResult[$C][5]
    Return $aResult
    EndSwitch
    $aResult = DllCall("User32.dll", "Int", "GetClassNameW", "HWND", $hWnd, "WStr", "", "Int", 260)
    If @error Or $aResult[0] = 0 Then Return True
    $Class = $aResult[2]
    $aResult = DllCall("User32.dll", "Int", "GetDlgCtrlID", "HWND", $hWnd)
    If @error Or $aResult[0] = 0 Then Return True
    $ID = $aResult[0]
    $ClassExist = False
    $C = $aClasses[0][0]
    For $I = 1 To $C
    If $aClasses[$I][0] = $Class Then
    $NN = $aClasses[$I][1] + 1
    $aClasses[$I][1] = $NN
    $ClassExist = True
    ExitLoop
    EndIf
    Next
    If Not $ClassExist Then
    $NN = 1
    $C += 1
    If $C > $UClasses Then Return False
    $aClasses[0][0] = $C
    $aClasses[$C][0] = $Class
    $aClasses[$C][1] = $NN
    EndIf
    $C = $aControls[0][0] + 1
    If $C > $UControls Then Return False
    $aControls[0][0] = $C
    $aControls[$C][0] = $hWnd
    $aControls[$C][1] = $Class
    $aControls[$C][2] = $NN
    $aControls[$C][3] = $ID
    $aControls[$C][4] = ControlCommand(HWnd($lParam), "", $ID, "IsVisible", "")
    Return True
    EndFunc
    ; ===============================================================================================================================

    [/autoit]


    Nach einem Vorschlag von progandy (s.u.) folgt hier noch eine alternative Version, die ohne Callback-Funktion und ohne Beschränkungen hinsichtlich der Anzahl der Controls auskommt und deshalb auch etwas ressourcenschonender ist. Außerdem habe ich den Vorschlag von UEZ (s.u.) zur Ermittlung der AU3-Controlnamen in diese Version eingebaut:

    WinGetControls.au3 - alternative Version nach Vorschlägen von progandy und UEZ
    [autoit]

    #include-once

    [/autoit] [autoit][/autoit] [autoit]

    ; #DEMOFUNCTION# ================================================================================================================
    ; Name...........: _WinGetControls
    ; Description ...: Liefert ein 2D-Array für die Controls eines Fensters.
    ; Das Array enhält für jedes Control die folgenden Informationen:
    ; |[0] HWND - HWND des Controls
    ; |[1] Class - Klasse des Controls
    ; |[2] NN - ClassNN des Controls
    ; |[3] ID - ID des Controls
    ; |[4] AU3 - AU3-Bezeichnung des Controls
    ; |[5] Visible - Sichtbar? 1 = ja, 0 = nein
    ; Das Feld Array[0][0] enthält die Anzahl der Controls.
    ; Syntax.........: _WinGetControls($hWnd)
    ; Parameters ....: $hWnd - HWND des Fensters (z.B. Rückgabewert von GUICreate)
    ; Return values .: Erfolg: Controlarray
    ; Fehler: False, @error = 1
    ; Author ........: Großvater & progandy & UEZ (http://www.autoit.de)
    ; Modified.......:
    ; Remarks .......:
    ; Related .......:
    ; Link ..........:
    ; Example .......:
    ; ===============================================================================================================================
    Func _WinGetControls($hWnd)
    Local Const $GWL_STYLE = 0xFFFFFFF0
    Local Const $BS_AUTOCHECKBOX = 0x0003
    Local Const $BS_CHECKBOX = 0x0002
    Local Const $BS_GROUPBOX = 0x0007
    Local Const $BS_RADIOBUTTON = 0x0004
    Local Const $BS_AUTORADIOBUTTON = 0x0009
    Local Const $ES_MULTILINE = 4
    Local Const $ES_WANTRETURN = 4096
    Local Const $SS_ICON = 0x3
    Local Const $SS_BITMAP = 0xE
    Local Const $WS_VSCROLL = 0x00200000
    Local Const $ES_EDIT = BitOR($ES_WANTRETURN, $WS_VSCROLL)
    Local $UControls = 100
    Local $UClasses = 50
    Local $aControls[$UControls + 1][6]
    Local $aClasses[$UClasses + 1][2]
    Local $aResult, $AU3, $C, $Class, $ClassExist, $Handle, $ID, $NN, $Styles
    If Not IsHWnd($hWnd) Then Return SetError(1, 0, False)
    Local $sClasses = WinGetClassList($hWnd)
    Local $aSplit = StringSplit(StringStripWS($sClasses, 2), @LF)
    $aControls[0][0] = 0
    $aClasses[0][0] = 0
    For $I = 1 To $aSplit[0]
    $Class = $aSplit[$I]
    $ClassExist = False
    $C = $aClasses[0][0]
    For $J = 1 To $C
    If $aClasses[$J][0] = $Class Then
    $NN = $aClasses[$J][1] + 1
    $aClasses[$J][1] = $NN
    $ClassExist = True
    ExitLoop
    EndIf
    Next
    If Not $ClassExist Then
    $NN = 1
    $C += 1
    If $C > $UClasses Then
    $UClasses += 50
    ReDim $aClasses[$UClasses + 1][2]
    EndIf
    $aClasses[0][0] = $C
    $aClasses[$C][0] = $Class
    $aClasses[$C][1] = $NN
    EndIf
    $Handle = ControlGetHandle($hWnd, "", $Class & $NN)
    $aResult = DllCall("User32.dll", "Int", "GetDlgCtrlID", "HWND", $Handle)
    If @error Or $aResult[0] = 0 Then
    $ID = ""
    Else
    $ID = $aResult[0]
    EndIf
    $C = $aControls[0][0] + 1
    If $C > $UControls Then
    $UControls += 100
    ReDim $aControls[$UControls + 1][6]
    EndIf
    ; Ausgeliehen aus der WinAPI.au3
    Local $sFuncName = "GetWindowLongW"
    If @AutoItX64 Then $sFuncName = "GetWindowLongPtrW"
    $aResult = DllCall("User32.dll", "LONG_PTR", $sFuncName, "HWND", $Handle, "INT", $GWL_STYLE)
    $Styles = $aResult[0]
    ; Erweiterung nach Vorschlag von UEZ
    Switch $Class
    Case "Button"
    If BitAND($Styles, $BS_GROUPBOX) = $BS_GROUPBOX Then
    $AU3 = "Group"
    ElseIf BitAND($Styles, $BS_CHECKBOX) = $BS_CHECKBOX Then
    $AU3 = "Checkbox"
    ElseIf BitAND($Styles, $BS_AUTOCHECKBOX) = $BS_AUTOCHECKBOX Then
    $AU3 = "Checkbox"
    ElseIf BitAND($Styles, $BS_RADIOBUTTON) = $BS_RADIOBUTTON Then
    $AU3 = "Radio"
    ElseIf BitAND($Styles, $BS_AUTORADIOBUTTON) = $BS_AUTORADIOBUTTON Then
    $AU3 = "Radio"
    Else
    $AU3 = "Button"
    EndIf
    Case "Static"
    If BitAND($Styles, $SS_ICON) = $SS_ICON Then
    $AU3 = "Icon"
    ElseIf BitAND($Styles, $SS_BITMAP) = $SS_BITMAP Then
    $AU3 = "Pic"
    Else
    $AU3 = "Label"
    EndIf
    Case "Edit"
    If BitAND($Styles, $ES_EDIT) = $ES_EDIT Then
    $AU3 = "Edit"
    Else
    $AU3 = "Input"
    EndIf
    Case "SysAnimate32"
    $AU3 = "AVI"
    Case "ComboBox"
    $AU3 = "Combo"
    Case "SysDateTimePick32"
    $AU3 = "Date"
    Case "ListBox"
    $AU3 = "List"
    Case "SysListView32"
    $AU3 = "ListView"
    Case "SysMonthCal32"
    $AU3 = "MonthCal"
    Case "msctls_progress32"
    $AU3 = "Progress"
    Case "msctls_trackbar32"
    $AU3 = "Slider"
    Case "SysTabControl32"
    $AU3 = "Tab"
    Case "SysTreeView32"
    $AU3 = "TreeView"
    Case "msctls_updown32"
    $AU3 = "UpDown"
    Case Else
    $AU3 = ""
    EndSwitch
    $aControls[0][0] = $C
    $aControls[$C][0] = $Handle
    $aControls[$C][1] = $Class
    $aControls[$C][2] = $NN
    $aControls[$C][3] = $ID
    $aControls[$C][4] = $AU3
    $aControls[$C][5] = ControlCommand($hWnd, "", $ID, "IsVisible", "")
    Next
    $C = $aControls[0][0] + 1
    ReDim $aControls[$C][6]
    Return $aControls
    EndFunc ;==>_WinGetControls
    ; ===============================================================================================================================

    [/autoit]


    Eine Minidemo gibt es auch:

    Demo
    [autoit]

    #include "WinGetControls.au3"
    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Dim $aControls, $hWnd, $Title

    [/autoit] [autoit][/autoit] [autoit]

    Run("Notepad")
    $hNotepad = WinWaitActive("[CLASS:Notepad]", "", 2)
    If Not IsHWnd($hNotepad) Then
    MsgBox(0, "WinGetControls", "Notepad konnte nicht gestartet werden!")
    Exit
    EndIf
    $Title = WinGetTitle($hNotepad )
    $aControls = _WinGetControls($hNotepad)
    If Not IsArray($aControls) Then
    MsgBox(0, "ERROR", "Der Aufruf von _WinGetControls() ist fehlgeschlagen!")
    Else
    _ArrayDisplay($aControls, $Title, -1, 0, "", "|", "Zeile|HWND|Class|NN|ID|Visible")
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Exit

    [/autoit]


    Alternative Version von UEZ:
    Das Thema scheint ja wirklich Interesse zu wecken. UEZ hat hier eine erweiterte Version der ersten Funktion eingestellt, die für die Klasse der Buttons zusätzlich die in AU3 verwendeten Bezeichner (Button, Checkbox, Groupbox, Radio) ermittelt. Eine passende Demo hat er dazugetan. Ich habe seine Anregungen in die alternative Version übernommen.


    Vielleicht kann es ja jemand brauchen.

    Happy scripting!

    3 Mal editiert, zuletzt von Großvater (31. Mai 2011 um 07:06) aus folgendem Grund: Alternative Version nach dem Vorschlag von proandy eingefügt.

  • Praktisch, aber mit WinGetClassList geht es auch:
    Liste holen; Array sortieren; ClassNN erstellen; Handle, CtrlId, Sichtbarkeit abfragen ;)

  • Guten Morgen,

    @All:
    Danke für die positiven Rückmeldungen! :D

    sumsum:
    Nicht ganz. __WinAPI_EnumWindowsChild() nutzt für die Suche die API-Funktion GetWindow(). Die sucht die Childwindows in der Reihenfolge der "Z-Order", und die "Z-Order" kann sich im Laufe des Lebens eines Fensters durchaus ändern. Damit ist dann eine einfache Zuordnung der ClassNN nicht mehr möglich. In einer längeren Diskussion im AHK-Forum musste ich mich von kompetenter Seite überzeugen lassen, dass nur die API-Funktion EnumChildWindows() die Controls in der korrekten Reihenfolge der ClassNN liefert.

    @progandy:
    Da könntest Du Recht haben. Ich habe die Funktion WinGetClassList() (wahrscheinlich wegen meiner AHK-Vorbelastung) als "kaum brauchbar" in die innere Besenkammer geschoben. Sie würde die Sache aber viel einfacher machen. Ich bastele mal eine zweite Version. ;)

  • Du bist ja krass drauf 8o

    Da dies hier wunderbar hier rein passt passt, habe ich es in deinen Code mit eingebaut -> ist nicht vollständig, z.B. fehlen die statischen Controls, wie GUICtrlCreatePic, etc.

    _WinGetControls.au3

    Spoiler anzeigen
    [autoit]


    #include <ButtonConstants.au3>
    #include <Constants.au3>
    #include <EditConstants.au3>
    #include <StaticConstants.au3>
    #include <WinAPI.au3>
    #include-once

    [/autoit] [autoit][/autoit] [autoit]

    ; #DEMOFUNCTION# ================================================================================================================
    ; Name...........: _WinGetControls
    ; Description ...: Liefert ein 2D-Array für die Controls eines Fensters.
    ; Das Array enhält für jedes Control die folgenden Informationen:
    ; |[0] HWND - HWND des Controls
    ; |[1] Class - Klasse des Controls
    ; |[2] NN - ClassNN des Controls
    ; |[3] ID - ID des Controls
    ; |[4] Visible - Sichtbar? 1 = ja, 0 = nein
    ; Das Feld Array[0][0] enthält die Anzahl der Controls.
    ; Syntax.........: _WinGetControls($hWnd)
    ; Parameters ....: $hWnd - HWND des Fensters (z.B. Rückgabewert von GUICreate)
    ; Return values .: Erfolg: Controlarray
    ; Fehler: False, @error = 1
    ; Author ........: Großvater (http://www.autoit.de)
    ; Modified.......:
    ; Remarks .......:
    ; Related .......:
    ; Link ..........:
    ; Example .......:
    ; ===============================================================================================================================
    Func _WinGetControls($hWnd)
    Local $hCB
    If Not IsHWnd($hWnd) Then Return SetError(1, 0, False)
    __WinGetControlsAddControl("Init", 0)
    $hCB = DLLCallbackRegister("__WinGetControlsAddControl", "Int", "HWND;LPARAM")
    DllCall("User32.dll", "Int", "EnumChildWindows", "HWND", $hWnd, "Ptr", DllCallbackGetPtr($hCB), "LPARAM", $hWnd)
    If @error Then Return SetError(DllCallbackFree($hCB), 0, False)
    DllCallbackFree($hCB)
    Return __WinGetControlsAddControl("Result", 0)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; #INTERNAL_USE_ONLY# ===========================================================================================================
    ; Name...........: __WinGetControlsAddControl
    ; Description ...: Callback-Funktion für DLLCall "EnumChildWindows" in _WinGetControls
    ; Remarks .......: Weil ich globale Variable für die Rückgabe von Funktionswerten nicht mag und die Implementierung
    ; der statischen Arrays recht rudimentär ist, sind die maximale Anzahl von Controls auf 1024
    ; und die maximale Anzahl von Klassen auf 256 begrenzt.
    ; ===============================================================================================================================
    Func __WinGetControlsAddControl($hWnd, $lParam)
    Local Static $UControls = 1024
    Local Static $UClasses = 256
    Local Static $aControls[$UControls + 1][6]
    Local Static $aClasses[$UClasses + 1][2]
    Local $aResult, $C, $Class, $ClassExist, $ID, $NN
    Switch $hWnd
    Case "Init"
    $aControls[0][0] = 0
    $aClasses[0][0] = 0
    Return
    Case "Result"
    $C = $aControls[0][0] + 1
    $aResult = $aControls
    Redim $aResult[$C][6]
    Return $aResult
    EndSwitch
    $aResult = DllCall("User32.dll", "Int", "GetClassNameW", "HWND", $hWnd, "WStr", "", "Int", 260)
    If @error Or $aResult[0] = 0 Then Return True
    $Class = $aResult[2]
    $aResult = DllCall("User32.dll", "Int", "GetDlgCtrlID", "HWND", $hWnd)
    If @error Or $aResult[0] = 0 Then Return True
    $ID = $aResult[0]
    $ClassExist = False
    $C = $aClasses[0][0]
    For $I = 1 To $C
    If $aClasses[$I][0] = $Class Then
    $NN = $aClasses[$I][1] + 1
    $aClasses[$I][1] = $NN
    $ClassExist = True
    ExitLoop
    EndIf
    Next
    If Not $ClassExist Then
    $NN = 1
    $C += 1
    If $C > $UClasses Then Return False
    $aClasses[0][0] = $C
    $aClasses[$C][0] = $Class
    $aClasses[$C][1] = $NN
    EndIf
    $C = $aControls[0][0] + 1
    If $C > $UControls Then Return False
    $aControls[0][0] = $C
    $aControls[$C][0] = $hWnd
    $aControls[$C][1] = $Class
    $aControls[$C][2] = $NN
    $aControls[$C][3] = $ID
    $aControls[$C][4] = ControlCommand(HWnd($lParam), "", $ID, "IsVisible", "")
    $aControls[$C][5] = __myCtrlGetClass($hWnd)
    Return True
    EndFunc
    ; ===============================================================================================================================

    [/autoit] [autoit][/autoit] [autoit]

    Func __myCtrlGetClass($hHandle) ;code by SmOke_N, modified by Guiness
    Local Const $GWL_STYLE = -16
    Local $iLong, $sClass
    If IsHWnd($hHandle) = 0 Then
    $hHandle = GUICtrlGetHandle($hHandle)
    If IsHWnd($hHandle) = 0 Then
    Return SetError(1, 0, "Unknown")
    EndIf
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $sClass = _WinAPI_GetClassName($hHandle)
    If @error Then
    Return "Unknown"
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $iLong = _WinAPI_GetWindowLong($hHandle, $GWL_STYLE)
    If @error Then
    Return SetError(2, 0, 0)
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Switch $sClass
    Case "Button"
    Select
    Case BitAND($iLong, $BS_GROUPBOX) = $BS_GROUPBOX
    Return "Group"
    Case BitAND($iLong, $BS_CHECKBOX) = $BS_CHECKBOX
    Return "Checkbox"
    Case BitAND($iLong, $BS_AUTOCHECKBOX) = $BS_AUTOCHECKBOX
    Return "Checkbox"
    Case BitAND($iLong, $BS_RADIOBUTTON) = $BS_RADIOBUTTON
    Return "Radio"
    Case BitAND($iLong, $BS_AUTORADIOBUTTON) = $BS_AUTORADIOBUTTON
    Return "Radio"
    EndSelect

    [/autoit] [autoit][/autoit] [autoit]

    Case "Edit"
    Select
    Case BitAND($iLong, $ES_WANTRETURN) = $ES_WANTRETURN
    Return "Edit"
    Case Else
    Return "Input"
    EndSelect

    [/autoit] [autoit][/autoit] [autoit]

    Case "Static"
    Select
    Case BitAND($iLong, $SS_BITMAP) = $SS_BITMAP
    Return "Pic"
    Case BitAND($iLong, $SS_ICON) = $SS_ICON
    Return "Icon"
    Case BitAND($iLong, $SS_LEFT) = $SS_LEFT
    If BitAND($iLong, $SS_NOTIFY) = $SS_NOTIFY Then
    Return "Label"
    EndIf
    Return "Graphic"
    EndSelect

    [/autoit] [autoit][/autoit] [autoit]

    Case "ComboBox"
    Return "Combo"
    Case "ListBox"
    Return "List"
    Case "msctls_progress32"
    Return "Progress"
    Case "msctls_trackbar32"
    Return "Slider"
    Case "SysDateTimePick32"
    Return "Date"
    Case "SysListView32"
    Return "ListView"
    Case "SysMonthCal32"
    Return "MonthCal"
    Case "SysTabControl32"
    Return "Tab"
    Case "SysTreeView32"
    Return "TreeView"

    [/autoit] [autoit][/autoit] [autoit]

    EndSwitch
    Return $sClass
    EndFunc

    [/autoit]

    _WinGetControls Example 2.au3

    Spoiler anzeigen
    [autoit]


    #include "WinGetControls.au3"
    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    $sGUITitle1 = "Test Getting GUI Control Types"

    [/autoit] [autoit][/autoit] [autoit]

    $hGUI1 = GUICreate($sGUITitle1, 400, 500, -1, -1)

    [/autoit] [autoit][/autoit] [autoit]

    $x3 = GUICtrlCreateButton("button1", 10, 10, 60, 20) ; button, it's a Group
    $x4 = GUICtrlCreateButton("button2", 10, 40, 60, 20) ; button
    $x5 = GUICtrlCreateButton("button3", 10, 70, 60, 20) ; button
    $x6 = GUICtrlCreateEdit("Edit1", 10, 100, 60, 20) ; edit, it's a HScroll
    $x7 = GUICtrlCreateInput("Input1", 10, 130, 60, 20) ; edit
    $x8 = GUICtrlCreateLabel("Label1", 10, 160, 60, 20) ; static
    $x9 = GUICtrlCreateRadio("Radio1", 10, 190, 60, 20) ; button
    $x10 = GUICtrlCreateRadio("Radio2", 10, 220, 60, 20) ; button
    $x11 = GUICtrlCreateCheckbox("CheckBox1", 10, 250, 60, 20) ; button
    $x12 = GUICtrlCreateGroup("Group1", 10, 280, 80, 50) ; button, it's a Group
    $x13 = GUICtrlCreateGroup("", -99, -99, 1, 1) ;close group, it's a Group
    $x14 = GUICtrlCreateCombo("", 10, 340, 60, 20) ; ComboBox
    GUICtrlSetData(-1, "ComboItem1|Comboitem2|Comboitem3", "Comboitem3")
    $x15 = GUICtrlCreateList("List1", 10, 370, 60, 20) ; ListBox
    GUICtrlSetData(-1, "ListItem1|Listitem2|Listitem3", "Listitem2")

    [/autoit] [autoit][/autoit] [autoit]

    GUISetState(@SW_SHOW, $sGUITitle1)

    [/autoit] [autoit][/autoit] [autoit]

    Dim $aControls, $hWnd, $Title
    $Title = WinGetTitle($hGUI1 )
    $aControls = _WinGetControls($hGUI1)
    If Not IsArray($aControls) Then
    MsgBox(0, "ERROR", "Der Aufruf von _WinGetControls() ist fehlgeschlagen!")
    Else
    _ArrayDisplay($aControls, $Title, -1, 0, "", "|", "Zeile|HWND|Class|NN|ID|Visible|Controls")
    EndIf
    Sleep(1000)
    Exit

    [/autoit]

    Wie hast du das ganze Zeug gelernt?

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

    5 Mal editiert, zuletzt von UEZ (29. Mai 2011 um 23:09)

  • Moin UEZ,

    ich war so frei, Deine Anregungen oben in die "alternative Version" zu übernehmen und habe dabei versucht, alle nativen AU3-Controls zu berücksichtigen.

    Wie hast du das ganze Zeug gelernt

    Fast ausschließlich durch Suchen und Lesen im MSDN und Probieren. AHK hat leider (oder Gott sei Dank, je nachdem, wie man es sieht) keine thematisch geordneten und wohl gepflegten UDFs. Und wenn es "ans Eingemachte" geht, muss wie in AU3 immer wieder die "native" Funktion DllCall() ran. Bei AU3 liegt der DllCall in vielen Fällen "unsichtbar" innerhalb fertiger UDFs, und man kommt ziemlich weit, ohne sich überhaupt damit beschäftigen zu müssen. Bei AHK ist das nicht so, zumindest dann, wenn man mit seinen persönlichen Suchbegriffen so selten Treffer findet, wie es bei mir der Fall ist. Also: MSDN, do it yourself, try and error, und wenn man Glück hat, schnallt man das dann irgendwann. ;)

  • Die MSDN scheint ja die Bibel der Coder zu sein. Man muss nur daran glauben, suchen, finden und verstehen - und alles wird gut.

    Danke,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Hallo zusammen,

    ich habe vielleicht eine kleine Ungereimtheit gefunden. Hintergrund ist, dass ich für mein Script eine eindeutige Identifikation von Controls benötige.
    Ich habe mittels WinGetControls.au3 und einer kleinen Suchfunktion versucht mir den Handle meines gesuchten Controls auszulesen.
    Bei der Verwendung in ControlClick($HWnd,"","Left") etc. springt mir das Script immer in das falsch Control.
    Also Autoit Info auf und WinID auf und nachgeschaut. Das Ergebnis ist in den beiden Screenshots zu sehen.

    autoit.de/wcf/attachment/13622/


    autoit.de/wcf/attachment/13623/


    WinID und AutoIt Info sind sich bezüglich des Handles einig. Nur WinGetControls patzt hier.

    Ich verstehe zu wenig von Win-API, als dass ich das korrigieren könnte. Hier sind die wahren Spezialisten gefragt.

    Zusatzfrage: Autoit Info findet beim Edit control den Text nicht , WinID gibt ihn aus. Gibt es eine zuverlässige Lösung zum Ansprechen von Editcontrols, wenn der Text im Kontext garantiert eindeutig ist ? Also finde Control mit Text "01.01" , klicke rein, sende einen {TAB} und komme so definiert in mein nächstes Eingabefeld. siehe auch meinen Thread im Bereich Datenbanken.

    Grüße aus Griesheim

    MsDotz

  • Versuche mal Control Viewer von Yashied.

    Es wäre auch nützlich, wenn wir deinen Code sehen könnten. Am besten machst du dazu einen eigenen Beitrag auf.

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Hallo UEZ,

    habe ich mir gerade geholt und ausprobiert. Source auch gleich mit runter geladen und mal reingeschaut.
    Habe da allerdings nicht soviel verstanden. Fakt ist, dass CV die Controls richtig erkennt und auch den Text dazu. Da ich mich nur auf den Text im Edit verlassen kann und nicht auf die Instance des Controls bin ich immer noch am ausprobieren. Hier mein Testscript:

    Spoiler anzeigen

    Opt('MustDeclareVars', 1)
    Opt("WinDetectHiddenText", 1) ;0=don't detect, 1=do detect
    Opt("WinSearchChildren", 1) ;0=no, 1=search children also
    Opt("WinTextMatchMode", 1) ;1=complete, 2=quick
    Opt("WinTitleMatchMode", 2) ;1=start, 2=subStr, 3=exact, 4=advanced, -1 to -4=Nocase
    Opt("WinWaitDelay", 500)
    Opt("SendKeyDelay", 20) ;20 milliseconds
    Opt("SendKeyDownDelay", 20) ;20 milliseconds

    #include <ButtonConstants.au3>
    #include <EditConstants.au3>
    #include <GUIConstantsEx.au3>
    #include <StaticConstants.au3>
    #include <GUIEdit.au3>
    #include <WindowsConstants.au3>
    #include <_WinGetcontrols.au3>
    #include <Array.au3>
    #include <WinAPI.au3>

    ;######################################################################
    ;# Func ErmittleEditHandle($strFensterTitel, $Posnummer, $iColumn, ByRef $strClassName)
    ;# $strFensterTitel == String , Titel des Akltuellen Fensters
    ;# $Posnummer == Integer, Positionszeile der Eingabe
    ;# $iColumn == Integer, Spaltenummer innerhalb der Zeile
    ;# $strClassName == String, Rückgabe des Classname zu Testzecken
    ;# Return : $HWndEdit - Handle zum Control
    ;#######################################################################
    Func ErmittleEditHandle($strFensterTitel, $Posnummer, $iColumn, ByRef $strClassName)

    Local $EditBelegnummer = 0
    Local $strControlID = ""
    Local $strClass = ""
    Local $HWndEdit = ""

    $EditBelegnummer = ($Posnummer - 1) * 6 + 5 + $iColumn
    $strControlID = "Edit" & String($EditBelegnummer)
    $strClass = "[CLASS:Edit; INSTANCE:" & $EditBelegnummer & "]"
    MsgBox(0,"DEBUG", "strClass: -> " & $strClass , 2)

    $strClassName = $strClass

    $HWndEdit = ControlGetHandle($strFensterTitel, "", $strClass)

    If IsHWnd ( $HWndEdit) Then
    Return $HWndEdit
    Else
    MsgBox(0,"Fehler", "Nicht gefunden Control " & $strControlID & " -> " & $strClass)
    Return ""
    EndIf
    EndFunc

    ;######################################################################
    ;# Func ZeigeWincontrols($hWnd)
    ;# $hWnd == HWND des Akltuellen Fensters
    ;# Return : $aControls, Array aus WinGetcontrols
    ;#######################################################################
    Func ZeigeWincontrols($hWnd)

    Local $aControls
    Local $Title

    if IsHWnd($hWnd) Then
    $Title = WinGetTitle($hWnd )
    $aControls = _WinGetControls($hWnd)
    If Not IsArray($aControls) Then
    MsgBox(0, "ERROR", "Der Aufruf von _WinGetControls() ist fehlgeschlagen!")
    Else
    _ArrayDisplay($aControls, $Title, -1, 0, "", "|", "Zeile|HWND|Class|NN|ID|Visible|Controls|Text")
    Return $aControls
    EndIf
    EndIf
    EndFunc


    Local $ctlhandle = ""
    Local $hWnd = ""
    Local $aArray
    Local $x = 0
    Local $Title
    Local $strClassName = ""
    Local $strText = ""

    $hWnd = WinWait("Angebot", "", 10000)

    if IsHWnd($hWnd) Then
    $Title = WinGetTitle($hWnd )

    $aArray = ZeigeWincontrols($hWnd)

    MsgBox(0, "VERSUCH1", "Positionsnummer click")
    $ctlhandle = ControlGetHandle($Title, "01.01","[CLASS:Edit;ID:1]")
    If IsHWnd ( $ctlhandle) Then
    ControlClick($Title,"01.01","[CLASS:Edit; ID:1]", "Left")
    ;$strText = ControlGetText($Title,"","[CLASS:Edit; ID:1]")
    ;$strText = ControlGetText($Title,"",$ctlhandle)
    $strText = ControlGetText($ctlhandle, "", "")
    MsgBox(0, "VERSUCH1", "Positionsnummer 01.01 gefunden auf " & $Title & " == " & $strText & " Mit Handle: " & $ctlhandle)
    Else
    MsgBox(0, "VERSUCH1", "Positionsnummer 01.01 NICHT gefunden")
    EndIf

    $ctlhandle = ControlGetHandle($Title, "01.02","[CLASS:Edit;ID:1]")
    If IsHWnd ( $ctlhandle) Then
    ControlClick($Title,"01.02","[CLASS:Edit; ID:1]", "Left")
    $strText = ControlGetText($Title,"","[CLASS:Edit; ID:1]")
    MsgBox(0, "VERSUCH1", "Positionsnummer 01.02 gefunden auf " & $Title & " == " & $strText & " Mit Handle: " & $ctlhandle)
    Else
    MsgBox(0, "VERSUCH1", "Positionsnummer 01.02 NICHT gefunden")
    EndIf

    $ctlhandle = ControlGetHandle($Title, "01.03","[CLASS:Edit;ID:1]")
    If IsHWnd ( $ctlhandle) Then
    ControlClick($Title,"01.03","[CLASS:Edit; ID:1]", "Left")
    MsgBox(0, "VERSUCH1", "Positionsnummer 01.03 gefunden au " & $Title & " == " & $strText & " Mit Handle: " & $ctlhandle)
    Else
    MsgBox(0, "VERSUCH1", "Positionsnummer 01.03 NICHT gefunden")
    EndIf

    $ctlhandle = ControlGetHandle($Title, "01.04","[CLASS:Edit;ID:1]")
    If IsHWnd ( $ctlhandle) Then
    ControlClick($Title,"01.04","[CLASS:Edit; ID:1]", "Left")
    MsgBox(0, "VERSUCH1", "Positionsnummer 01.04 gefunden auf " & $Title & " == " & $strText & " Mit Handle: " & $ctlhandle)
    Else
    MsgBox(0, "VERSUCH1", "Positionsnummer 01.04 NICHT gefunden")
    EndIf

    ;#######################################################
    ; 2. Testszenario
    ;#######################################################
    While 1

    For $x = 1 To 5
    $ctlhandle = ErmittleEditHandle($Title, $x, 2, $strClassName)
    If IsHWnd($ctlhandle) Then
    MsgBox(0,"DEBUG", "Control gefunden Edit" & $x & " ->> " & $ctlhandle & " ==>> " & $strClassName)
    ControlFocus($Title,"",$ctlhandle)
    ControlClick($Title,"",$ctlhandle,"Left")
    Send("{TAB}")
    EndIf
    Next
    ExitLoop
    WEnd
    EndIf


    Das Ergebnis des ersten Test: $ctlhandle bleibt immer gleich, obwohl der Aufruf ControlGetHandle immer mit einem anderen Text ausgeführt wird.
    Gibt es noch alternative Schreibweisen, die dann auch den Text mit berücksichtigen ?
    Den Link zu meinem ursprünglichen Beitrag füge ich gleich noch an. hier der Link

    Gruß
    MsDotz

    Einmal editiert, zuletzt von msdotz (26. Juni 2011 um 19:27)

  • Hallo zusammen,

    habe das Problem in soweit gelöst, dass es funktioniert. Elegant ist anders. Etwas "zu Guttenberg" ist auch drin ;) .

    Spoiler anzeigen


    Func GetClassNameNN_FromControlID( $wndTitle, $winText, $id, $Text2beFound)
    Local $ControlsString = ""
    ; Derive the Window Class for the given Control ID
    Local $hWnd = ControlGetHandle( $wndTitle, $winText, $id )
    Local $ClassName = _WinAPI_GetClassName( $hWnd )
    ;MsgBox(0,"TEST 1 ", "Classname : " & $ClassName & " ID: ( " & $id & " ) , Gesuchter Text: " & $Text2beFound, 1 )


    ; Get an array of classes associated with the same window
    Local $ClassList = StringSplit( WinGetClassList( $wndTitle, $winText), @LF )
    Local $ClassCount = 0
    Local $iIndex = 0
    Local $i = 0

    ; Walk the array.

    ;For $i = 1 To $ClassList[0]
    ;MsgBox(0,"TEST 1 ", "ClassList[" & $i & "] : " & $ClassList[$i] ,1 )

    $iIndex = _ArraySearch($ClassList, $ClassName, $iStartAt, 0, 0, 1)
    If @error Then
    MsgBox(0, "Not Found", '"' & $ClassName & '" was not found in the array.')
    Else
    MsgBox(0, "Found", '"' & $ClassName & '" was found in the array at position ' & $iIndex & "." , 2)
    $iStartAt = $iIndex +1 ; Nächstes Mal ab dem gefunden Element weiter suchen
    $ClassCount += 1

    ; Obtain the Control ID using the ClassNameNN syntax
    Local $Test_ClassNameNN = $ClassName & $ClassCount
    Local $Test_hWnd = ControlGetHandle( $wndTitle, $winText, $Test_ClassNameNN )

    If _ArraySearch($aHandlesBenutzt, $Test_hWnd ) = -1 Then ; Handle noch nicht verarbeitet
    Local $Test_ID = GetControlID( $Test_hWnd )
    Local $Text = GetControlText( $Test_hWnd)

    ;MsgBox(0,"TEST 2 ", "ClassnameNN : " & $Test_ClassNameNN & " ID:( " & $Test_ID & " )", 1)
    ;MsgBox(0,"TEST 2 ", "ControlText->> " & $Text & " == " & $Text2beFound & " ?" , 2)

    ; Test if the ID's are the same


    ;If $id = $Test_ID And $Text = $Text2beFound then <<------ auskommentiert, da nur der Text eindeutig ist und sonst nichts

    ;MsgBox(0,"TEST2 ", "Teilstring: " & StringMid($Text,1 , StringLen($Text2beFound)), 2)


    If StringMid($Text,1 , StringLen($Text2beFound)) = $Text2beFound then
    $ControlsString = "[CLASS:" & $ClassName & "; INSTANCE:" & $ClassCount & ";ID:" & $Test_ID & "]"
    _ArrayAdd($aHandlesBenutzt, $Test_hWnd) ; Text gefunden, muss neues Control sein
    Return $ControlsString & "|" & $Test_ClassNameNN & "|" & $Test_ID & "|" & $Text & "|" & $Test_hWnd
    EndIf
    EndIf
    EndIf

    Return ""
    EndFunc


    Gehts auch eleganter und schneller ? Die Suche in der Classlist ist langsam. Lasse deshalb ab dem letzten gefundenen Control weitersuchen.
    Sonst könnte man in Urlaub fahren.

    L. G.

    MsDotz

  • Hallo Zusammen,

    die Beispiel oben, scheinen in der aktuellen AutoIt Version 3.3 nicht mehr zu laufen? (Siehe Screenshot)

    Mein eigentliches Problem: Ich komme aus der hardwarenahen Embedded-Programmierung (8051 Assember / C, sowie Webserver (PHP) und andere Scripte (Python)). Programmstrukturen und Datentypen sind mir also geläufig. Was ich dringend suche, wäre eine systematische Einführung in die Steuerfunktion von AutoIt für andere Fenster. Leider hören aber genau hier alle Tutorials, die ich bisher gefunden habe, auf (bzw. werden sehr dünn).

    Meine google Suche war bisher auch nicht sehr erfolgreich. Vermutlich ist mangels Vorwissen (d.h. die richtigen Suchbegriffe) der Suchraum einfach noch zu groß.

    Würde mich über ein paar Hinweise auf die richtigen Tutorials/Links/etc. sehr freuen,

    VG EGuhrm

    screenshot_20150906.jpg

  • Ich weiß nicht genau was du meinst, aber folgende Befehle können dir helfen:

    AutoIt
    _WinAPI_EnumChildWindows() ;Controls sind auch nur Windows
    ControlClick() ;wenn du das Handle des Controls hast
    ControlSend() ;kannst du den ControlID-Parameter als "" angeben (bei den Control... Funktionen)

    Wobei ich ehrlich gesagt bezweifle, das EnumChildWindows() dir helfen wird, wenn das AutoIt-Window-Info-Tool dir nicht geholfen hat.
    Im schlimmsten Fall wird da ein eigenes, nicht WinAPI-kompatibles GUI-Framework verwendet und die ganzen Standardlösungen sind dann für die Katz.

  • Danke für die Antwort!
    Ich habe leider nicht genau vestanden, was Du mir mit _WinAPI_EnumChildWindows() sagen wolltest. Auch die AutoIt Help bring mich mit hier nicht weiter, da fehlt mir noch der Hintergrund. Deshalb suche ich ja ein Tutorial, welches die Zusammenhänge schrittweise erklärt.

    Problem an dieser Stelle ist aber, dass das Skript von UEZ weiter oben, bei mir nicht läuft. Screenshot siehe oben.

  • Okay kleine Einführung zu Windows (nicht das OS)
    Ein Window (dt Fenster) wird in WinAPI immer mit dessen Handle (HWND = Handle Window) angesprochen.
    Du erstellst eines mit CreateWindow() bzw. in AutoIt mit _WinAPI_CreateWindow().
    So jz ist ein Window nicht nur ein Fenster wie zB ein Browser sondern zB auch ein Control wie ein Button.
    Unterschieden werden Windows durch deren Classes (WindowClass).
    Es gibt vordefinierte Windows zB BUTTON.
    Der große Unterschied zwischen Fenster-Windows und Control-Windows ist, dass ein Control-Window immer ein Parent-Window benötigt (auf dem es "liegt")

    D.h. ein Fenster ist ein Window und wenn es Control hat, dann hat es Child-Windows.
    Diese Children holst du dir alle mit der Funktion _WinAPI_EnumChildWindows().
    Das ganze Windowerstellen, und dann immer die richtige Klasse für ein Control auszuwählen, nimmt dir AutoIt mit Befehlen wie zB GUICreate(), GUICtrlCreateButton() ab, aber darunter vergibt sich immer noch die WinAPI.

  • Danke für die kurze Einführung! :thumbup: Das war mir so im Detail nicht klar, komme ja aus der Embedded programmierung, da gibt es höchstens mal ein LCD display.

    Werde mich also mal mit der Funktion spielen und sehen, was _WinAPI_EnumChildWindows() so ausspuckt.
    Ich nehme mal an, zu Windows-Gui gibt es allgemeingültige Literatur, die sich leicht auf AutoIt übertragen lässt?