Externes Programm GUI auslesen

  • VB.NET

Es gibt 40 Antworten in diesem Thema. Der letzte Beitrag () ist von Gonger96.

    Externes Programm GUI auslesen

    Hallo liebe Gemeinde,

    ich versuche seit tagen ein Programm auszulesen (textboxen, Caption etc.. Dieses Programm ist mehrfach gestartet (16x), ist also zu 95 % gleich, nur die Textboxen, Captions und inhalte ändern sich und genau diese möchte ich sortiert nach Handle auflisten...

    Laut meinem Verständnis muss ich zuerst das Handle vom gesuchten Programm erhalten und kann darauf zugreifen? ' ist das so korrekt?!

    ich habe mich mit FindWindow und FindWindowEx probiert:

    VB.NET-Quellcode

    1. Public Class
    2. Dim selectedTitlee As String
    3. Dim PZID = Process.GetProcessById(ProgrammID)
    4. selectedTitle = PZID.MainWindowTitle.Replace("programm xy", "") ' holt aus der ProgrammID den Title und speichert es in selectedTitle
    5. Label1.Text = selectedTitle ' Kontolle welches Programm er gefunden hat
    6. Dim Hauptfenster As IntPtr = FindWindow("Hauptclasse des Programms", selectedTitle) 'Finde programm nach Title
    7. ' ElternWnD, LEER, Class, Fenster Titel
    8. Dim Nebenfenster As IntPtr = FindWindowEx(Hauptfenster, IntPtr.Zero, "class", "title") 'Finde Child über Classe und oder Title
    9. Dim Nebenfenster1 As IntPtr = FindWindowEx(Nebenfenster, IntPtr.Zero, "class", "title") 'Finde Child über Classe und oder Title + hole krämpel vom vorherigen Nebenfenster?!
    10. End Class

    mein Problem besteht darin, dass ich z.B Captions ohne class nicht ausgelesen bekomme, er findet den handle nicht oder findet generell nix.

    Meine frage: gibt es eine schönere art, Funktion oder ähnliches ein Programm (GUI) auszulesen?

    :S

    Dieser Beitrag wurde bereits 9 mal editiert, zuletzt von „intrepidklasse“ ()

    Ich bin mal so dreist und mach mal wieder Eigenwerbung: externe .Net-Programme auslesen und manipulieren
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Danke erstmal für die Antworten und die genommene zeit für mein Problem :)

    Ich habe mir beides angeschaut und wenn der Tipp funktionieren soll, müsste ich noch erfahren wie ich UIAutomationClient als Namespace oder DLL einbinden kann, den die Referenz habe ich leider nicht gefunden.

    Als Vorbereitung werden über die Verweise die folgenden DLLs in das Projekt eingebunden: UIAutomationClient, UIAutomationTypes


    auch

    VB.NET-Quellcode

    1. System.Windows.Automation kann ich leider nicht finden ;-/
    wird wohl für c# sein

    ich denke das ich noch einmal den wink mit nem Zaunpfahl benötige :)

    womöglich in dieser art?

    VB.NET-Quellcode

    1. Private Declare Function FindFirst Lib "UIAutomationCore.dll" _
    2. Alias "FindFirst" (
    3. ByVal name As String,
    4. ByVal lpWindowName As String) As Long

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „intrepidklasse“ ()

    Willkommen im Forum. :thumbup:

    intrepidklasse schrieb:

    Dieses Programm ist mehrfach gestartet (16x),
    Wer startet denn das Programm?
    Wenn Du es selbst machst und dazu die Process-Klasse verwendest, hast Du doch jedes einzelne Handle.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo Voyager Intrepidklasse.

    intrepidklasse schrieb:

    wird wohl für c# sein
    Nee, ich arbeite in VB.Net.

    intrepidklasse schrieb:

    womöglich in dieser art?
    Ohje, nein, das wär ja nahe an VB6 dran. In VS2019CE kann man das quasi automatisch hinzufügen lassen. Einfach auf das unbekannte (rotgekringelt-unterstrichene) Element klicken und dann links auf die Gegenvorschläge:

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Das mit dem Einbinden hat tatsächlich geklappt und ja, ich bin VB6 gebrandmarkt xD

    VB.NET-Quellcode

    1. ​ListBox1.Items.Add(AutomationElement.RootElement.FindAll(TreeScope.Children, Condition.TrueCondition))


    Es erscheint nur Sammlung in der Listbox1. Eigentlich sollten doch alle Prozesse eingetragen werden?!
    Na, so geht das aber nicht. Du fütterst die ListBox mit allen laufenden Programmen. Und es ergibt sich eine Auflistung von AutomationElements. Das sind hochkomplexe Objekte. Woher soll da die ListBox wissen, was von den vielen Eigenschaften, die tw. auch verschachtelt sind, angezeigt werden soll? Die Frage ist daher, was Du tatsächlich in der ListBox aufgelistet haben willst.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Ich habe folgendes Zenario:

    - 16x Title eines Programmes zb "Notepad 1" , "Notepad 2" der Titel ist variabel aber fast gleich.
    - Ich muss also erstmal alle Fenster nach dem Titel durchsuchen die einen String enthalten müssen z.B "Notepad".
    - Habe ich die Fenster gefunden, möchte ich sodann damit beginnen, diese auszulesen.

    Wie sich das mit der Automations Geschichte verwirklichen lässt habe ich noch nicht herausgefunden, den letztendlich muss ich ja mit dem gefundenen Handle weiter machen.
    Ich klammere mich sehr an Deine Postings, die ich finden kann zu diesem Thema, bisher bin ich aber noch nicht auf einen grünen Zweig gekommen mit dem Tutorial von dir :)

    intrepidklasse schrieb:

    den letztendlich muss ich ja mit dem gefundenen Handle weiter machen
    Nein. Das Handle (oder Pointer/Zeiger) der Anwendung ist ein UI-Automation-Detail, dass man verwenden kann, aber nicht muss. Das liegt daran, dass das Handle eine sehr variable Sache ist, nach der Du nicht fix suchen kannst. Du kannst also nicht in Deinen Code schreiben: »Wenn ich auf den [Suchen]-Button klicke, soll mir mein Programm die App mit dem Handle &H98765432 suchen.«
    Der Anfang war schon richtig:

    VB.NET-Quellcode

    1. AutomationElement.RootElement.FindAll(TreeScope.Children, Condition.TrueCondition)
    aber das undifferenzierte Auflisten in einer ListBox nicht. Du willst also nach einem Namen suchen. Dann z.B.

    VB.NET-Quellcode

    1. Dim AllApps = AutomationElement.RootElement.FindAll(TreeScope.Children, Condition.TrueCondition)
    2. Dim AllAppNames As New List(Of String)
    3. For Each App As AutomationElement In AllApps
    4. AllAppNames.Add(App.Current.ClassName)
    5. Next
    6. For Each AppName In AllAppNames
    7. If AppName = "Notepad" Then MessageBox.Show(AppName)
    8. Next

    kürzer geht's auch dank LINQ:

    VB.NET-Quellcode

    1. AutomationElement.RootElement.FindAll(TreeScope.Children, Condition.TrueCondition).Cast(Of AutomationElement).Select(Function(x) x.Current.ClassName).Where(Function(x) x = "Notepad").ToList.ForEach(Sub(x) MessageBox.Show(x))

    oder zum Erhalten der passenden Apps:

    VB.NET-Quellcode

    1. Dim AllFittingApps = AutomationElement.RootElement.FindAll(TreeScope.Children, Condition.TrueCondition).Cast(Of AutomationElement).Where(Function(x) x.Current.ClassName = "Notepad")


    ACHTUNG: Der Begriff Notepad ist ein Klassenname und hat nicht zwangsläufig was mit dem Namen der Exe zu tun. Daher solltest Du gut überdenken/recherchieren, womit Du beim Auffinden Deiner Ziel-Apps arbeitest. Klassenname? Titelleistentext? Was ganz anderes?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

    VB.NET-Quellcode

    1. Dim AllApps = AutomationElement.RootElement.FindAll(TreeScope.Children, Condition.TrueCondition)
    2. Dim AllAppNames As New List(Of String)
    3. For Each App As AutomationElement In AllApps
    4. AllAppNames.Add(App.Current.Name & App.Current.ProcessId)
    5. Next
    6. For Each AppName In AllAppNames
    7. If AppName.Contains("Programm Title") = True Then
    8. ComboBox1.Items.Add(AppName)
    9. End If
    10. Next


    ich liebe .contains :) es hat geklappt.

    Jetzt stehe ich vor dem Problem: ausgelesen habe ich alle notwendigen Programme, nun möchte ich diese aus der Listbox anklicken und dazu die Informationen auslesen.
    Kann ich den durch App.Current.ProcessId und anhand der ID nun die App weiter auslesen?

    erstmal vielen dank für deine Antworten und die von dir investierte Zeit

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „intrepidklasse“ ()

    :cursing:
    Ich wiederhole mich: Handles, ProcessID, ... sind alles Details. Diese weiter zu nutzen, um das Programm auszulesen, ergibt keinen Sinn. Lies bitte mein Tutorial. Sobald Du die richtige App am Haken hast, machst Du damit weiter. Denn diese App ist dann vom Typ AutomationElement. Und damit geht's dann auch weiter. Also mit DeineApp.FindFirst(...) oder DeineApp.FindAll(...). Machen wir es mal etwas konkreter: Was genau willst Du aus Deiner Zielapp herholen, auslesen, ändern?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Ich hatte vor kurzem eine ähnliche Fragestellung Kann man den Browser mit einem VB.Net Programm "steuern" // Audio Stumm/An & Fenster Position und Größe

    Ich konnte alles mit Hilfe des Links von Vaporized lösen (s.o.).


    Ich hab mir erstmal ein Programm geschrieben, das nicht exakt meine "Sachen" macht sondern das sich von "oben nach unten" durch das GUI durcharbeitet.

    Also erst alles RunningApps anzeigt (alle Programme die ich steuern könnte) -> dann eins auswählen und von diesem dann alle "fassbaren" Kontroll-Elemente anzeigen (Button, Tabs, etc.) -> dann ein Kontroll-Element auswählen und sich alle möglichen Patterns anzeigen lassen (z.B. invoke = Knopf drücken ist mit dem Kontroll-Element möglich).

    Na und ab da läuft es dann...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „nogood“ ()

    hi Nogood,

    ja habe dein Thread verfolgt und gelesen, genau so habe ich mir das gedacht, nur bei der Umsetzung hapert es noch, habe es dank VaporiZed geschafft alle auszulesen (hab alle in einer Listbox, jetzt krieg ich dieses doofe value binding gedönse nicht hin um mit nem handle weiterarbeiten zu können *ggg*) , jetzt tue ich mich schwer damit das ausgelesene zu selektieren und zu verarbeiten. Möchte aber hier im Forum niemanden auf die nerven gehen, weil ichs noch nicht so ganz ralle, Diese FIndWindows und FindWindowsEX war mir irgendwie lieber, auch wenn ich damit mein ziel nicht erreicht habe :D .

    Ich persönlich finde es sehr schade das es dazu kein Tutorial, Bücher o.Ä gibt, das einzigste was es dazu gibt ist für C++, nicht mal YouTube ist in diesem Fall eine hilfe :/ Ich muss auch ganz ehrlich gestehen das ich aus VB6 komme und zuvor noch nie mit Net gearbeitet habe, die Neuerungen sind wirklich enorm.

    Daher kommt es auch vereinzelt vor das ich VB6 Code schreibe und direkt was aufs Dach bekomme, aber Learning by Doing ... xD


    ich geb nicht auf ..

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „intrepidklasse“ ()

    intrepidklasse schrieb:

    ich geb nicht auf ..
    Ich auch nicht.
    Also: Brauchst Du zwangsläufig alle passenden Apps in einer ListBox oder ist ein vorheriges Filtern möglich? Brauchst Du eine App-Auswahlmöglichkeit auf Deinem Form oder willst Du die passenden Apps alle nur irgendwo zwischenspeichern? Das Auflisten in einer ListBox ist möglich, aber nur über einen Umweg sinnvoll (den ich erst erkläre, wenn er wirklich nötig ist, da das eher selten der Fall ist)
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Hi VaporiZed,

    die ListBox, CoboBox sollte übersichtshalber schon sein. Ich habe mir es folgendermaßen vorgestellt

    (Siehe Anhang)

    Wenn man auf alle Apps klickt(kann auch ein Button sein), soll er alle Apps mit vorherig festgelegten/auszulesenden Werten anzeigen und diese ebenfalls stets aktualisieren.

    Klickt man auf die einzeln ausgelesenen Apps, soll er rechts verschiedene werte einlesen und diese dann auch aktuell halten

    Aufbau der App

    Quellcode

    1. >>> Window <<<<
    2. Title: [Zeppix] Programm Titel
    3. Class: wxWindowClassNR
    4. Position: 972, 69
    5. Size: 830, 710
    6. Style: 0x16CF0000
    7. ExStyle: 0x00000100
    8. Handle: 0x0079065A
    9. >>>> Control <<<<
    10. >>>> Mouse <<<<
    11. >>>> StatusBar <<<<
    12. >>>> ToolsBar <<<<
    13. >>>> Visible Text <<<<
    14. ID_PANEL1
    15. ID_PANEL1
    16. ID_PANEL3
    17. ID_TOP_HPBAR
    18. ID_TOP_MPBAR
    19. ID_TOP_XPBAR
    20. usw

    Er soll dann inhalte von ID Panel 1 Auslesen

    So war der Plan, macht es die Sache wesentlich komplizierter ?
    Bilder
    • bsp.jpg

      60,58 kB, 500×600, 29 mal angesehen

    Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „intrepidklasse“ ()

    @intrepidklasse Warum verwendest Du nicht die Dateianhangs-Funktionalität des Forums?
    Erweiterte Antwort => Dateianhänge => Hochladen
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Also gut. Schieb Dir ne ListBox auf Dein Form und klicke oben rechts auf den [>]-SmartTag, setze den Haken und klick in der DatenquellencomboBox auf |Projektdatenquelle hinzufügen|. Dann auf [Objekt] -> UIAutomationClient -> System.Windows.Automation -> AutomationElement -> AutomationElementInformation anhaken -> [Fertig stellen]
    Dadurch wird eine BindingSource erstellt und Du teilst BindingSource und ListBox mit, dass in der Liste nun alle möglichen Infos über ein AutomationElement drinstehen werden. Nun kannst Du bei |Member anzeigen| aussuchen, was von einem AutomationElement angezeigt wird. Wenn es der Titel sein soll, klick auf |Name|.
    Dadurch werden die ListBox und die BindingSource auf eine Anzeige vorbereitet, aber es würden noch keine Daten drinstehen, wenn das Programm gestartet wird.
    Woher bekommt die BindingSource nun ihre tatsächlichen Daten? Anders gefragt: Was ist ihre Datenquelle? Das musst Du durch Programmcode festlegen, z.B. mit einem Button, der bei Klick folgenden Code ausführt:

    Raubtier
    BindingSource-Fütterung:

    VB.NET-Quellcode

    1. AutomationElementInformationBindingSource.DataSource = AutomationElement.RootElement.FindAll(TreeScope.Children, Condition.TrueCondition).Cast(Of AutomationElement).Select(Function(x) x.Current)

    Also: Die bekannte Auflistung aller Programme, nur dass jetzt die Datenquelle für die BindingSource nicht eine Liste von AutomationElements ist, sondern deren Current-Eigenschaft.* Denn dort stehen alle AutomationElementInformation-Daten drin. Und darauf ist die ListBox ja eingestellt. Wenn alles funktioniert, solltest Du nach Ausführen des Codes erstmal alle Namen der laufenden Programme aufgelistet bekommen.

    * Habe es über den Designer probiert, also ohne Zusatzcode, leider ohne Erfolg. Aber wurscht.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Hat geklappt, er listet mir nun alle Laufenden Programme auf, nun soll er als ValueMember noch die Handle ID erhalten ?! um damit weiter arbeiten zu können, habe das bei [>]-SmartTag unter WerteMember -> AutomationsID so eingetragen wenn es richtig war? ValueMember gibt jedoch nen leeren String aus.