Mai 16, 2021, 23:07:00

Neuigkeiten:

Ist euer Problem gelöst, dann bitte den Knopf "Thema gelöst" drücken!


"Sub oder Function nicht definiert" trotz gesetzter Referenz?

Begonnen von sirtet, April 10, 2021, 15:56:26

⏪ vorheriges - nächstes ⏩

sirtet

Ich versuche in Access 2016 diesem howto zu ui automation zu folgen:
https://www.youtube.com/watch?v=ANTIGgs7YM4

Aber ich bekomme diesen "undefined" Fehler für die Funktion WalkEnabledElements.
Es ist der erste Funktionsaufruf nach DIM ... as...
Ich bekomme einen anderen Fehler wenn die Referenz auf UI Automation nicht gesetzt ist, also scheint's nicht daran zu liegen...

Ich gehe davon aus, dass die bemängelte Funktion in der referenzierten DLL sitzt, und darum verwendbar sein sollte...
Ich habe zu VBA eher nur eine Ahnung als echtes Wissen, also geht gerne mal von einem Anfänger aus beim Antworten ;-)

Wo/wie könnte ich weiter nach dem Problem suchen?
Ich hab etwa nach etwas wie einem klassen-browser gesucht, wo man nach momentan verfügbaren Klassen, Funktionen etc. suchen könnte, und mit dem Objektkatalog scheinbar auch sowas gefunden, aber der zeigt nichts von UI Automation... Findet man da nur was zur "Basis" gehört und nichts aus Referenzen, oder verstehe/bediene ich da einfach etwas falsch?

Private Sub uiAutomate_Click()
 'testing https://www.youtube.com/watch?v=7pYanOx3tcE
Dim oAutomation As New CUIAutomation
Dim MyElement1 As UIAutomationClient.IUIAutomationElement
Dim MyElement2 As UIAutomationClient.IUIAutomationElement

Dim oInvokePattern As UIAutomationClient.IUIAutomationInvokePattern
Dim oPattern As UIAutomationClient.IUIAutomationLegacyIAccessiblePattern

Set MyElement1 = WalkEnabledElements(oAutomation.GetRootElement, "M2SYS BioPlugin Snap-On Adapter")

Falls jemand fragt was ich denn erreichen will mit meinem Code:
Via Button auf einem Access-Fromular ein in das System-Tray minimiertes Programmfenster öffnen und darin einen Button klicken (um ein Unterfenster dieses Programms zu öffnen).
Da bin ich natürlich offen für andere Lösungsansätze.

PhilS

Zitat von: sirtet am April 10, 2021, 15:56:26Aber ich bekomme diesen "undefined" Fehler für die Funktion WalkEnabledElements.
[...]
Ich gehe davon aus, dass die bemängelte Funktion in der referenzierten DLL sitzt, und darum verwendbar sein sollte...
Die Funktion ist eine benutzerdefinierte VBA-Funktion im Beispielprojekt. An dieser Stelle des Videos kannst du es sehen: https://youtu.be/ANTIGgs7YM4?t=103
Access DevTools - Find and Replace
Komfortables Suchen und Ersetzen in den Entwurfseigenschaften von Access-Objekten. In Abfragen, Formularen, Berichten und VBA-Code - Überall und rasend schnell!

sirtet

Danke. Ich dachte (ämm... eher meinte ohne denken) die Funktion wäre in der DLL... aber in der ist natürlich kein VBA Sourcecode [facepalm]

Jetzt habe ich aber das nächste Problem:





Diesmal habe ich den Code möglichst 1:1, ohne mein eigentliches Ziel.
Einziger Unterschied den ich sehe, ich habe die ersten 3 Zeilen auch in der Sub...


Private Sub uiAutomate_Click()
 'trying to follow a UIAutomation howto
 'Part 1 https://www.youtube.com/watch?v=7pYanOx3tcE
 'Part 2 https://www.youtube.com/watch?v=ANTIGgs7YM4
 
Dim oAutomation As New CUIAutomation8
Dim MyElement1 As UIAutomationClient.IUIAutomationElement
Dim MyElement2 As UIAutomationClient.IUIAutomationElement

Dim oInvokePattern As UIAutomationClient.IUIAutomationInvokePattern
Dim oPattern As UIAutomationClient.IUIAutomationLegacyIAccessiblePattern


Set MyElement1 = WalkEnabledElements(oAutomation.GetRootElement, "Skype")

Set MyElement2 = MyElementl.FindFirst(TreeScope_Children, PropCondition(oAutomation, "TLoginAppControl", "ClsName"))

Set MyElement1 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "Shell Embedding", "ClsName"))

Set MyElement2 = MyElement1.FindFirst(TreeScope_Children, PropCondition(oAutomation, "Shell DocObject View", "ClsName"))

Set MyElement1 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "Internet Explorer_Server", "clsName"))

Set MyElement2 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "sign in with Skype or Microsoft account", "Name"))

'Set UserID
Set MyElement1 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "unifiedUsername", "AutoID"))
Set oPattern = MyElementl.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
oPattern.SetValue ("MyUserID")

'Set Password
Set MyElement1 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "unifiedPassword", "AutoID"))
Set oPattern = MyElement1.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
oPattern.SetValue ("Password")

'Click Sign In button
Set MyElement1 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "M2SYS BioPlugin Snap-On Adapter (Waiting..)", "AutoID"))
Set oInvokePattern = MyElement1.GetCurrentPattern(UIAutomationClient.UIA_InvokePatternId)
oInvokePattern.Invoke
MsgBox "invoked"
End Sub



Function WalkEnabledElements(element As UIAutomationClient.IUIAutomationElement, strWindowName As String) As UIAutomationClient.IUIAutomationElement
    Dim walker As UIAutomationClient.IUIAutomationTreeWalker
       
    Set walker = oAutomation.ControlViewWalker
    Set element = walker.GetFirstChildElement(element)

    Do While Not element Is Nothing
        Debug.Print element.CurrentName
       
        If InStr(1, element.CurrentName, strWindowName) > 0 Then
            Set WalkEnabledElements = element
            Exit Function
        End If
   
        Set element = walker.GetNextSiblingElement(element)
    Loop
End Function


Function PropCondition(UiAutomation As CUIAutomation, Requirement As String, IdType As String) As UIAutomationClient.IUIAutomationCondition 'UIAutomationClient.IUIAutomationCondition
    Select Case IdType
        Case "Name"
            Set PropCondition = UiAutomation.CreatePropertyCondition(UIAutomationClient.UIA_NamePropertyId, Requirement)
        Case "AutoID"
            Set PropCondition = UiAutomation.CreatePropertyCondition(UIAutomationClient.UIA_AutomationIdPropertyId, Requirement)
        Case "ClsName"
            Set PropCondition = UiAutomation.CreatePropertyCondition(UIAutomationClient.UIA_ClassNamePropertyId, Requirement)
        Case "LoczCon"
            Set PropCondition = UiAutomation.CreatePropertyCondition(UIAutomationClient.UIA_LocalizedControlTypePropertyId, Requirement)
    End Select
End Function

Screengrab:

PhilS

Zitat von: sirtet am April 20, 2021, 10:15:22Diesmal habe ich den Code möglichst 1:1, ohne mein eigentliches Ziel.
Einziger Unterschied den ich sehe, ich habe die ersten 3 Zeilen auch in der Sub...
Auch oder nur?
In jedem Fall: Warum?

Du hast mit dem "auch" in einem Code zwei verschiedene Variablen mit demselben Name oAutomation, aber verschiedenen Gültigkeitsbereichen. Welche der beiden du mit dem Namen erwischt, hängt davon ab. in welcher Prozedur du dich gerade befindest. - Das kann evtl. funktionieren, aber Programmierfehler sind damit extrem wahrscheinlich.

Mit dem "nur" hast du gar keine gültige Objektreferenz auf oAutomation innerhalb von WalkEnabledElements.

Beide Situation könnten evtl. den Fehler produzieren, den du siehst.
Access DevTools - Find and Replace
Komfortables Suchen und Ersetzen in den Entwurfseigenschaften von Access-Objekten. In Abfragen, Formularen, Berichten und VBA-Code - Überall und rasend schnell!

sirtet

Sorry. "Auch" im Sinn von "so wie der restliche Code". Also ja, NUR...
Ah klar, darum "Objekt erforderlich".

Warum hatte ich es dort? Ich meinte irgendwie, bei den Klassenobjekten dürften nur Funktionen stehen... Wusste/weiss nicht was ist der Unterschied ist zwischen Modulen und Klassenobjekten, resp. wie sie zusammenhängen.

Jetzt habe ich die drei Funktionen in "Modul1"


Dim oAutomation As New CUIAutomation
Dim MyElement1 As UIAutomationClient.IUIAutomationElement
Dim MyElement2 As UIAutomationClient.IUIAutomationElement

Sub UIAutomationDemo()
 
Dim oInvokePattern As UIAutomationClient.IUIAutomationInvokePattern
Dim oPattern As UIAutomationClient.IUIAutomationLegacyIAccessiblePattern

Set MyElement1 = WalkEnabledElements(oAutomation.GetRootElement, "Skype")

Set MyElement2 = MyElementl.FindFirst(TreeScope_Children, PropCondition(oAutomation, "TLoginAppControl", "ClsName"))

Set MyElement1 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "Shell Embedding", "ClsName"))

Set MyElement2 = MyElement1.FindFirst(TreeScope_Children, PropCondition(oAutomation, "Shell DocObject View", "ClsName"))

Set MyElement1 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "Internet Explorer_Server", "clsName"))

Set MyElement2 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "sign in with Skype or Microsoft account", "Name"))

'Set UserID
Set MyElement1 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "unifiedUsername", "AutoID"))
Set oPattern = MyElementl.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
oPattern.SetValue ("MyUserID")

'Set Password
Set MyElement1 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "unifiedPassword", "AutoID"))
Set oPattern = MyElement1.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
oPattern.SetValue ("Password")

'Click Sign In button
Set MyElement1 = MyElement2.FindFirst(TreeScope_Children, PropCondition(oAutomation, "M2SYS BioPlugin Snap-On Adapter (Waiting..)", "AutoID"))
Set oInvokePattern = MyElement1.GetCurrentPattern(UIAutomationClient.UIA_InvokePatternId)
oInvokePattern.Invoke
MsgBox "invoked"
End Sub



Function WalkEnabledElements(element As UIAutomationClient.IUIAutomationElement, strWindowName As String) As UIAutomationClient.IUIAutomationElement
    Dim walker As UIAutomationClient.IUIAutomationTreeWalker
       
    Set walker = oAutomation.ControlViewWalker
    Set element = walker.GetFirstChildElement(element)

    Do While Not element Is Nothing
        Debug.Print element.CurrentName
       
        If InStr(1, element.CurrentName, strWindowName) > 0 Then
            Set WalkEnabledElements = element
            Exit Function
        End If
   
        Set element = walker.GetNextSiblingElement(element)
    Loop
End Function


Function PropCondition(UiAutomation As CUIAutomation, Requirement As String, IdType As String) As UIAutomationClient.IUIAutomationCondition 'UIAutomationClient.IUIAutomationCondition
    Select Case IdType
        Case "Name"
            Set PropCondition = UiAutomation.CreatePropertyCondition(UIAutomationClient.UIA_NamePropertyId, Requirement)
        Case "AutoID"
            Set PropCondition = UiAutomation.CreatePropertyCondition(UIAutomationClient.UIA_AutomationIdPropertyId, Requirement)
        Case "ClsName"
            Set PropCondition = UiAutomation.CreatePropertyCondition(UIAutomationClient.UIA_ClassNamePropertyId, Requirement)
        Case "LoczCon"
            Set PropCondition = UiAutomation.CreatePropertyCondition(UIAutomationClient.UIA_LocalizedControlTypePropertyId, Requirement)
    End Select
End Function


und den Aufruf im Code zum Formular


Und schon bin ich einen Schritt weiter, beim nächsten Fehler  ???


Wenn ich die Funktionen beim Formular habe, schaut's gleich aus, selber Fehler.
Module sind also wohl vor allem da um den Code zu organisieren und es macht hier keinen Unterschied?

PhilS

Ich kann mit den Informationen keine Fehlerursache erkennen.

Ist CUIAutomation evtl. mehrfach irgendwo deklariert?
Access DevTools - Find and Replace
Komfortables Suchen und Ersetzen in den Entwurfseigenschaften von Access-Objekten. In Abfragen, Formularen, Berichten und VBA-Code - Überall und rasend schnell!

sirtet

Danke erneut.
Ich habe den oben geposteten Code in einer neuen leeren DB eingefügt, die Referenz auf UIAutomationCore gesetzt, und... es läuft.
Also ja, irgend sowas scheint das Problem gewesen zu sein, genau geschaut hab ich aber noch nicht.

Jedenfalls, ich kann jetzt im Fenster von meinem Ziel-Programm einen Button "anklicken", nach dem ich wie im Video beschrieben mit Inspect.exe die passenden Eigenschaften ermittle.

Was ich noch nicht hinbekommen habe, ist das Fenster zu öffnen via seinem Symbol im System-Tray.
Das sollte auch gehen, muss man scheinbar aber etwas anders ansprechen als ein schon offenes Fenster.
Da bin ich noch auf der Suche.
Vielleicht gäbe es noch einen anderen Weg, festzustellen ob der gesuchte Prozess läuft, und dann sein Hauptfenster zu öffnen...

PhilS

Zitat von: sirtet am April 27, 2021, 09:39:52Was ich noch nicht hinbekommen habe, ist das Fenster zu öffnen via seinem Symbol im System-Tray.
Das sollte auch gehen, muss man scheinbar aber etwas anders ansprechen als ein schon offenes Fenster.
An jedem Icon im Systray hängt auch ein unsichtbares Fenster des Programms zu dem es gehört. Wenn du in Inspect einstellst, dass auch unsichtbare Fenster angezeigt werden, kannst du die finden. Über die Windows Api wäre es wahrscheinlich möglich, dass Kontextmenü eines solchen Fensters auszulesen und einen der Einträge darin auszuführen.

Ob das so auch über deine UIAutomation-Library möglich ist, weiß ich nicht.
Access DevTools - Find and Replace
Komfortables Suchen und Ersetzen in den Entwurfseigenschaften von Access-Objekten. In Abfragen, Formularen, Berichten und VBA-Code - Überall und rasend schnell!