MainWindowHandle und dann die childs ?

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von Mabbi.

    MainWindowHandle und dann die childs ?

    Hi,

    ich muss eine andere App fernsteuern.
    Zum Finden der zugehörigen Fenster benutze ich folgenden code:

    VB.NET-Quellcode

    1. Public Function GetWindowHandle(ByVal partialTitle As String, ByRef Windowtitle As String) As IntPtr
    2. For Each p As Process In Process.GetProcesses()
    3. If p.MainWindowTitle.IndexOf(partialTitle, 0, StringComparison.CurrentCultureIgnoreCase) > -1 Then
    4. Windowtitle = p.MainWindowTitle
    5. Return p.MainWindowHandle
    6. End If
    7. Next
    8. Return IntPtr.Zero 'No Match Found
    9. End Function



    INFO:
    Da ich nur einen Teil von manchen Window-Titeln (dynamisch generierte Fenster) im voraus kenne benutze ich partialstring im Vergleich.

    Läuft einwandfrei mit Option Explicit On und Option Strict On.

    Nun scheitere ich aber an einer Stelle. Offensichtlich macht eins der Fenster die ich ansteuere ein Unterfenster (CHILD ?) auf.
    Die oben gezeigte Routine findet dies nicht.
    Ich habe dazu einiges gelesen und bin auf

    EnumWindows
    EnumChildWindows

    gestossen.

    Dazu habe ich einige Stunden Infos gesammelt und mehrere Ansätze probiert, ich bekomm es aber einfach nicht zum Laufen.

    Ich finde mein "Hauptfenster" und das(?) richtige Handle dazu, danach scheitere ich aber am Zugriff auf das Kind-Fenster.
    Wie bau ich das in die vorhandene Routine ein ?

    Vielen Dank im voraus für Eure Hilfe.

    MfG...Mabbi

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

    Mabbi schrieb:

    ich muss eine andere App fernsteuern.
    Bei dieser anderen App handelt es sich um ein natives Window oder eine .NET App?
    Für ersteres gugst Du Andere Programme fernsteuern
    für letzteres hier: externe .Net-Programme auslesen und manipulieren
    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!

    Mabbi schrieb:

    Warenwirtschaft aus den 1990ern.
    Das halte ich für nicht zielführent.
    Hast Du ein Betriebssystem, auf dem das und Dein Programm läuft?
    Was ist der Plan?
    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!
    Probier mal späßleshalber meinen GuiSpy, vielleicht kriegt der was zu fassen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @ROD:Win7, ich komme an alle Fenster die von der Warenwirtschaft aufgemacht werden ran mit dem Code aus dem 1. Post.

    Wenn ich mit dem AUTOIT SPY Tool schaue sehen die in der Regel so aus:

    Quellcode

    1. >>>> Window <<<<
    2. Title: Stammdatendruck
    3. Class: DialogClass32
    4. Position: 700, 276
    5. Size: 526, 516
    6. Style: 0x9CC80084
    7. ExStyle: 0x00010101
    8. Handle: 0x00931580


    Nur genau hier, beim Stammdatendruck kommt ein anderes Fenster, ich vermute es ist ein CHild von Stammdatendruck, bitte korrigiert mich wenn es falsch ist.
    Da sieht die summary so aus:

    Quellcode

    1. >>>> Window <<<<
    2. Title: Information
    3. Class: #32770
    4. Position: 863, 486
    5. Size: 242, 161
    6. Style: 0x94C803C5
    7. ExStyle: 0x00010101
    8. Handle: 0x002019FA


    Hier ein Bild dazu:

    <img src="https://i.ibb.co/1vT0hK8/Class32770t.jpg">

    Ich habe nun aus Deinem Code und einem Projekt zum Auslesen von Skype etwas gebastelt, das durch die Childs von den "Stammdatendruck" sucht.
    Da bekomme ich faktisch alle "Label" und "Textblöcke" und sogar versteckte Elemente angezeigt, aber an das eigentliche "Information" Fenster komme ich immer noch nicht ran.
    Ein Bild hierzu, da sieht die Routine die oberste Combo-Box und Ihren Inhalt. Ich hole das handle und benutze Gettext zusammengepackt als String in eine msgbox:





    Weiß jemand wie man an diese Class #32770 rankommt ?
    Diese Zeile hier aus dem ersten Post sieht den Partout nicht:

    VB.NET-Quellcode

    1. For Each p As Process In Process.GetProcesses()


    Laut https://docs.microsoft.com/en-us/windows/win32/winmsg/about-window-classes ist das eine Dialog Box.

    @VaporiZed: Vielen Dank für den Tip. Wenn ich hier wirklich scheitern sollte schaue ich mir GUISPY genau an.
    Ich vermute aber derzeit, das ich dicht an einer Lösug bin, vor Bäumen aber nur den Wald nicht sehe.

    Vielen Dank im voraus für Eure Unterstützung.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Mabbi“ ()

    @Mabbi Das ist also ein natives Programm (C, C++, nicht aber .NET).
    Öffne das Fenster und gehe mit aktiviertem Spy++ per HOver über die Fenster, da bekommst Du die relevanten Informationen angezeigt:
    Andere Programme fernsteuern
    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!
    @Rod.

    Ich habe idZ. noch etwas weiter getestet mit einer Lösung zu Dialogboxen von hier https://stackoverflow.com/questions/40607191/vb-net-find-messagebox-in-another-application-using-findwindowex

    Da sieht bei mir nun so aus:

    VB.NET-Quellcode

    1. hWnd = GetWindowHandle("Stammdaten", Windowtitle)
    2. 'Fenster erfolgreich gefunden?
    3. If hWnd <> IntPtr.Zero Then
    4. MsgBox(Windowtitle & " " & hWnd.ToString)
    5. Dim hWndChild1 As IntPtr = FindWindowEx(hWnd, IntPtr.Zero, "#32770 (Dialog)", "Information")
    6. MsgBox(hWndChild1.ToString & " Information")
    7. Dim hWndChild1Button As IntPtr = FindWindowEx(hWndChild1, IntPtr.Zero, "Button", "OK")
    8. MsgBox(hWndChild1Button)
    9. If hWndChild1Button <> IntPtr.Zero Then
    10. SetForegroundWindow(hWndChild1Button)
    11. SendKeys.SendWait("{Enter}")
    12. System.Threading.Thread.Sleep(2000)
    13. End If
    14. End If


    Der Code findet erfolgreich die "Stammdaten" aber die Dialog-Box nicht ?
    Ich vermute so langsam, das der Dialog gar nicht von den "Stammdaten" aufgemacht wird....

    EDIT: OH, wer weiter gelesen hätte wüsste dann auch, das der Code dafür nicht geht....ROFL, sorry für die Irritation

    -> Ich schau mal Spy++ an und gebe Rückmeldung

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Mabbi“ ()

    Soo, kleines Update.
    Meine VS Version ist leider Express und hat kein Spy++

    Ich bin aber trotzdem wieder etwas weiter:

    VB.NET-Quellcode

    1. Sub Dialog_Finder(ByRef i As Integer)
    2. Dim Windowtitle As String = ""
    3. Dim hwnd = GetWindowHandle("Stammdaten", Windowtitle)
    4. If hwnd <> IntPtr.Zero Then
    5. Dim pid As Integer
    6. Dim tid = GetWindowThreadProcessId(hwnd, pid)
    7. If tid <> 0 Then
    8. Dim buf As New System.Text.StringBuilder(256)
    9. GetClassName(hwnd, buf, 256)
    10. EnumThreadWindows(tid, AddressOf ClickOkButton, Nothing)
    11. End If
    12. End If
    13. End Sub
    14. Private Function ClickOkButton(hWnd As IntPtr, lp As IntPtr) As Boolean
    15. '' Verify the class name is #32770
    16. Dim buf As New System.Text.StringBuilder(256)
    17. GetClassName(hWnd, buf, 256)
    18. If buf.ToString <> "#32770" Then Return True
    19. '' Find the OK button (control ID 2)
    20. Dim okbutton = GetDlgItem(hWnd, 2)
    21. If okbutton = IntPtr.Zero Then Return True
    22. '' Activate the dialog, just in case
    23. SetActiveWindow(hWnd)
    24. '' Click the button
    25. SendMessage(okbutton, WM_SETTEXT, IntPtr.Zero, okbutton.ToString())
    26. Return False
    27. End Function


    Damit finde ich die richtige Dialog-Box und auch den zugehörigen OK-Button.
    Jetzt bekomme ich aber die Meldung "Sendmessage" sei nicht in User32.dll

    Hier die Deklaration:

    VB.NET-Quellcode

    1. Public Declare Function SendMessage Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam A String) As IntPtr
    2. Public Const WM_SETTEXT As Integer = &HC

    Den Teil habe ich aus dem Beispiel zum "Fernsteuern" von ROD portiert.

    Natürlich könnte ich hier einfach einen positionierten Mouse-Click auslösen, wäre aber Pfusch und nicht prozesssicher für die Zukunft.

    Mit Pinvoke komme ich da irgendwie nicht recht weiter.

    UPDATE:
    Es läuft mit ""user32.dll" Alias "SendMessageA""
    Dann noch die Sendmessage korrekt als Click auf den OK-Button umgeschrieben, alles geht nun so wie es soll.
    :thumbsup:

    Danke für Eure Hilfe (zur Selbsthilfe) ...wieder einiges gelernt.

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

    Nachtrag:

    Das Tool kann nun so gar mehr als erwartet....
    Der Stammmdatendruck nach der "Information" DialogBox wird von List&Label generiert mit einem echt schrägen Fenster Klassen Namen "L&LViewer".

    Sogar den findet die Routine nun nach 2 kleinen Änderungen :D

    Also nochmals,

    Vielen Dank, dass Ihr so hilfsbereit seit.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Mabbi“ ()