Seltsames Verhalten der Klasse Webbrowser

  • VB.NET

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von Kangaroo.

    Seltsames Verhalten der Klasse Webbrowser

    Hallo
    Ich habe vor ein Programmzu schreiben, bei dem ich einen Webbrowser dazu "missbrauche" einen Login durchzuführen, um danach auf eine vorher definierte Seite zu navigieren, um von dort spezielle Links auszulesen.
    Nun habe ich einige Fragen und Probleme, die mir aufgefallen sind.

    in der textbox in der ich die Url angebe, fange ich den Enter Key um den Browser anzuleiten zu navigieren.
    hierbei ertönt dieser nervige Windows Error Ton. Beim Click auf den Button passiert dies nicht.
    Auch wenn ich bei druck auf Enter den Button_Click event aufrufe, kommt dieses "Ding"

    Ich habe zum anderen das Problem, das das Webbrowser_Document_Completed event mehr als einmal ausgelößt wird, und ich so nicht ordentlich reagieren kann, was wahrscheinlich an mehreren frames liegt. Bisher ist mein Code

    VB.NET-Quellcode

    1. Private Sub wbr_DocumentCompleted(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles wbr.DocumentCompleted
    2. bloaded = True
    3. End Sub
    4. Private Sub waitforBrowser()
    5. Do While Not bloaded
    6. System.Windows.Forms.Application.DoEvents()
    7. Loop
    8. bloaded = false;
    9. End Sub

    der dazu dient, festzustellen, ob der Browser fertig geladen hat. gibt es eine bessere alternative, die nur einmal durchläuft? WebBrowser readyState_Completed oder so?

    das komischste ist, dass ich das Problem habe, dass der Browser bei auslesen von links seltsam reagiert:
    Ich habe eine Prozedur (capturing()), die mir die Links rausfischt, die ich brauche, welche einwandfrei funktioniert.
    Nur bekomme ich vom Webbrowser ein seltsames Ergebnis

    VB.NET-Quellcode

    1. Private Sub NavigateAndCapture(ByVal myuri as Uri)
    2. wbr.navigate(myuri)
    3. Call waitforBrowser()
    4. 'Ich bin definitiv auf der richtigen Seite, der Browser hat fertig geladen
    5. Call capturing()
    6. 'macht garnichts
    7. End Sub
    8. Private Sub btnCap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCap.Click
    9. Call capturing()
    10. End Sub


    Wenn ich direkt danach auf den btnCap klicke funktioniert call capturing einwandfrei...

    bin für jeden hilfreichen Tipp Dankbar
    Also WebBrowser als Bot missbrauchen hatten wie hier scohn öfters, da hättest auch genügend Codes gefunden denke ich, du musst das mit dem ReadyState vom Browser arbeiten

    VB.NET-Quellcode

    1. WebBrowser1.Navigate("url")
    2. While Not WebBrower1.State = ReadyState
    3. Application.DoEvents()
    4. End While


    Das KEIN Code zum kopieren, wie das Member für den Status genau heißt weiß ich nicht ausm Kopf, aber es soll dir den weg weisen nach was du suchen musst.
    Danke, das hatte ich inzwischen auch gefunden.
    Damit es auch richtig hier steht:

    VB.NET-Quellcode

    1. While Not WebBrowser1.ReadyState = WebbrowserReadyState.Complete
    2. System.Windows.Forms.Application.DoEvents()
    3. End While


    Jetzt habe ich noch das Problem mit dem ErrorTon, welches aber nicht Soo wichtig ist. Es nervt halt.

    Das Größte Problem hab ich aber mit dem Capturing()
    ich habe nicht im Ansatz eine Idee, was ich dort noch versuchen kann, um nicht jeden Capture Vorgang manuell durch einen button Auslösen zu müssen.
    Es ergibt für mich einfach keinen Sinn, dass eine Prozedur Programmatisch aufgerufen nicht funktioniert, und nach dem Druck auf einen Button schon.
    natürlich nach dem Einloggen. Vorher gibt es (jedenfalls für mich) nichts auszulesen!
    Nach dem login - der auf jedenfall klappt - steuere ich eine Seite an und warte, bis der Browser fertig geladen hat. Danach rufe ich capturing auf, welches in der Entwicklungsphase eine textbox mit den von mir gewünschten Links füllt und ein wenig formatiert. Nur funktioniert das nicht. Wenn ich unmittelbar nach dem laden einen Button manuell betätige, der Capturing aufruft, füllt sich die Textbox aber.

    Ruffstaarr schrieb:

    natürlich nach dem Einloggen. Vorher gibt es (jedenfalls für mich) nichts auszulesen!

    Nun, da Du die URL nicht nennen willst, kann man nur spekulieren. Aber die häufigste Ursache für ein wiederholtes DocumentComplete Event ist normalerweise das Nachladen von Seitennhalten durch AJAX, also durch Javascript.

    Insofern kann es passieren, dass Deine Links erst nachgeladen werden, also nach dem ersten Readystate.Complete oder dem DocumentCompleted Event noch garnicht in Deinem HTMLDocument enthalten sind. Wenn Du allerdings den Button benutzt , so wird genug Zeit vergangen sein um den vollständigen Inhalt der Seite geladen zu haben.

    Teste das mal, indem Du Dir mit dem Debugger oder durch Abspeichern des Documents in ein File den Quellcode anschaust.

    Abhilfe kann man da nur schaffen, indem man entweder künstlich Zeit einfügt (unsauber) oder die Existenz eines HTML Elements abfragt, welches auf den vollständigen Inhalt des Seiteninhalts hinweist.

    Edit: was immer Du mit dem Errorton meinst (navigieren des Webbrowsers ?), den würde ich in per Systemsteuerung->Sounds einfach abstellen.
    Danke für die schnelle Hilfe.
    Es geht um die Seite Dropbox
    Wenn ich auf den readystate completed "lausche" klappt der Login nicht mehr.
    Ich denke das liegt daran dass in Application.DoEvents() das Event document _completed mehrmals hintereinander aufgerufen wird, und danach erst in die Überprüfung der While Schleife fortfährt, während ReadyState.Complete keinen Event auslößt.
    Das mit dem Künstlich Zeit einfügen habe ich schon versucht

    VB.NET-Quellcode

    1. Public Sub sleep(ByVal Seconds As Integer)
    2. ' Sekunden in Millisekunden umrechnen
    3. Dim mSecond As Integer = Seconds * 1000
    4. Threading.Thread.Sleep(mSecond)
    5. End Sub

    und die Sleep Prozedur vorher aufgerufen. Kein Erfolg, selbst bei 20 sec Wartezeit.
    Theoretisch müsste ich den selben Effekt ja auch auslösen können, wenn ich im Debugger einen Breakpoint vor dem Aufrufen von capturing setzte, und ein wenig warte. Aber auch hier hatte ich keinen Erfolg. Die Html Property des Browsers verändert sich nicht weiter. Drücke ich dann auf den Button, hat sie sich dann geändert.

    Zum Errorton:

    VB.NET-Quellcode

    1. Private Sub txtURL_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles txtURL.KeyDown
    2. If e.KeyCode = Keys.Enter Then
    3. Call doNavigate()
    4. End If
    5. End Sub
    6. Private Sub btnBrowse_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBrowse.Click
    7. Call doNavigate()
    8. End Sub
    9. Private Sub doNavigate()
    10. If Not txtURL.Text = String.Empty Then
    11. wbr1.Navigate(txtURL.Text)
    12. Call wait()
    13. End If
    14. End Sub

    hierbei ertönt ein Erroton. Die Lösung die Errortöne auszuschalten bekämpft ja nur das Symptom, und wenn jemand anders das Programm benutzen will, muss er das auch machen... das kann es ja nicht sein
    Versuchs mal mit folgender Sleep Function, da ich nicht sicher bin ob bei Thread.Sleep der WebBrowser weiterarbeitet:

    VB.NET-Quellcode

    1. '------------------------------------------
    2. '-- Sleep() ---
    3. '------------------------------------------
    4. Public Sub mySleep(ByVal ms As Integer)
    5. Dim tOut As Date = Now().AddMilliseconds(ms)
    6. While True
    7. System.Threading.Thread.Sleep(10)
    8. Application.DoEvents()
    9. ' check timeOut
    10. If tOut < Now() Then Return
    11. End While
    12. End Sub

    Kangaroo schrieb:


    Versuchs mal mit folgender Sleep Function, da ich nicht sicher bin ob bei Thread.Sleep der WebBrowser weiterarbeitet:
    '------------------------------------------
    '-- Sleep() ---
    '------------------------------------------
    Public Sub mySleep(ByVal ms As Integer)
    Dim tOut As Date = Now().AddMilliseconds(ms)
    While True
    System.Threading.Thread.Sleep(10)
    Application.DoEvents()
    ' check timeOut
    If tOut < Now() Then Return
    End While
    End Sub




    Yeah das hat es voll gebracht... anscheinend arbeitet der Webbrowser bei der anderen Sleep Function nicht weiter.

    aber wie du eben schon gesagt hast, ist das Unsauber... jetzt muss ich wohl doch noch mal schauen, ob ich nicht herausfinden kann, wann der Browser WIRKLICH fertig geladen hat.
    muss mal in die debugausgabe vom webbrowser im Document_Completed Event schauen, ob ich dort nicht einen Eindeutigen Wert auslesen kann. Oder würdest du anders vorgehen?
    nun, Du kannst den Originalquellcode ja direkt in jedem Browser anschauen (Ansicht->Quellcode). Den Quellcode im fertig geladenen Zustand kannst Du im Gegensatz dazu ja per Button-Click und dem Streamreader in eine HTML-Datei schreiben. Nun kannst Du vergleichen auf welches WebElement Du nach den Document-Completed Events prüfen musst ...
    ja, ok aber mal ne blöde Frage: ändert sich das Html nicht mit jeder Seite?
    Mein Problem ist, dass die Links die ich auslese nicht im Quelltext stehen, ich sie aber über wbr.Document.Links bekomme. nun kann es aber auch vorkommen, dass auf einer Seite keine Links zum auslesen da sind, und ich so auch nicht die Links überprüfen kann.
    Ich muss also anders herausfinden, ob der Browser fertig geladen hat.
    Ich glaub dass ist auch was du meintest, aber ich steh gerade voll auf dem Schlauch.
    Könntest du das nochmal ein wenig weiter ausführen?
    Ich versuche gerade, mir die Webbrowser in eine Liste zu schreiben. Also den letzten Stand vom Document Completed Event und den nach dem Klick auf den Button. der hat ja sooo viele Eigenschaften. Hast du vielleicht sonst noch Tipps, auf welche ich besonders schauen könnte?
    Edit:
    habe mal geschaut, also der wbr.DocumentText entspricht dem bei der die Seite noch nicht fertig geladen ist, wbr.DocumentStream jedoch nicht. Muss wahrscheinlich ein Sample Stream Speichern, und Hoffen, dass es der selbe auf einer anderen Seite ist.

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

    ja, ok aber mal ne blöde Frage: ändert sich das Html nicht mit jeder Seite?

    Auf normalen Seiten nicht, aber in diesem Fall ja. Das ist das Grundprinzip von Ajax, und auch die Basis des gesamten Web 2.0.

    Quellcode

    1. Ich muss also anders herausfinden, ob der Browser fertig geladen hat.

    Nun, wann hat die Seite "fertig geladen", unter Umständen eben nie, da Javascript per Timer halt regelmässig neues HTML ( und damit neue Links) anfordern kann. Zum Beispiel bei einem Javascript Chat, es kommt halt immer auf die Seite an und ob sie sich /bzw. wie oft sie sich erneuert.

    Bei manchen geschieht es nur am Anfang des Aufrufs, um dem Benutzer schnell etwas anzeigen zu können, andere Teile werden darauf im Hintergrund nachgeladen.

    Will man den geänderten Code abspeichern, so würde ich statt des DocumentText (buggy) eher WebBrowser1.Document.Body.OuterHtml verwenden.

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

    Kangaroo schrieb:

    Nun, wann hat die Seite "fertig geladen", unter Umständen eben nie, da Javascript per Timer halt regelmässig neues HTML ( und damit neue Links) anfordern kann. Zum Beispiel bei einem Javascript Chat, es kommt halt immer auf die Seite an und ob sie sich /bzw. wie oft sie sich erneuert.
    Das kann ich auf jedenfall ausschließen. Ich bin in eine Endlosschleife von while True Apllication.DoEvents()
    gegangen um eben das herauszufinden. nach einer Zeit wird das document_completed Event nicht mehr aufgerufen.


    Kangaroo schrieb:

    Will man den geänderten Code abspeichern, so würde ich statt des DocumentText (buggy) eher WebBrowser1.Document.Body.OuterHtml verwenden.
    danke, das werde ich noch versuchen
    Also das mit Body.OuterHtml geht auch nicht... Weil ein Ordner ja wie gesagt auch leer sein kann - Somit hab ich halt ne Leere Seite, was ja kein Fehler wäre.

    ich habs jetzt mit halbwegs aktzeptabler Ladezeit so gemacht:

    VB.NET-Quellcode

    1. Private Sub wbr_DocumentCompleted(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles wbr.DocumentCompleted
    2. mytimeout = Now().AddMilliseconds(550)
    3. bloaded = True
    4. End Sub
    5. Private Sub myWait(ByVal ms As Integer)
    6. mytimeout = Now().AddMilliseconds(ms)
    7. While True
    8. System.Threading.Thread.Sleep(10)
    9. Application.DoEvents()
    10. If mytimeout < Now() Then
    11. Return
    12. End If
    13. End While
    14. End Sub
    15. Public Sub myNavigate(ByVal Url as String)
    16. wbr.navigate()
    17. Call wait()
    18. Call myWait(1000)
    19. End Sub


    Ich hab mir das Prinzip deiner Wartefunktion zu nutzen gemacht, und mit einer zusätzlichen Wartezeit von ner halben Sekunde kann ich leben; Wenn es mich auch nicht wirklich Glücklich stimmt.
    Muss doch irgendwie der Zeitpunkt rauszufinden sein, wann dieser olle Browser fertig geladen hat.

    Ruffstaarr schrieb:

    Ich hab mir das Prinzip deiner Wartefunktion zu nutzen gemacht, und mit einer zusätzlichen Wartezeit von ner halben Sekunde kann ich leben; Wenn es mich auch nicht wirklich Glücklich stimmt.
    Muss doch irgendwie der Zeitpunkt rauszufinden sein, wann dieser olle Browser fertig geladen hat.


    Das mit der Wartezeit war wirklich nur für Testzwecke gedacht, um mal schnell rauszufinden ob denn nun wirklich etwas geladen wurde. NOrmal würde ich wieder auf das DocumentCompleted Event zurückgehen und dort auf Existenz des Documents und der Links ( oder was auch immer im fertig geladenen Zustand drin stehen sollte) abprüfen.
    Ja weiss ich. Wie du siehst benutze ich ja das document_completed event, um die Wartezeit zu erhöhen. - soll heissen, selbst wenn eine Startwartezeit von 10 sec Eingestellt wird, wrid sie beim ersten completed auf ne halbe Sec verringert, und bei jedem weiterem wieder auf ne halbe Sekunde gesetzt.
    Also gehe ich quasi davon aus, dass das Document fertig geladen hat, sobald das 2 document_completed Event nicht eine halbe Sekunde nach dem ersten getriggert wird
    Wie gesagt, kann ich dort kein HTML Auslesen, da genau dies ja durch das Ajax script generiert wird. Wenn ich in einen leeren Ordner klicke, gibts dort auch nix zu generieren. Das ist aber zugleich auch das Einzige, was sich bei nem Request verändert.
    Ich glaube, das ist ein Dilemma, aus dem ich mit HTML auslesen nicht herauskomme. Gibt es nicht ne Möglichkeit, den Ajax Request, bzw die Response manuell auszulesen?

    Ruffstaarr schrieb:

    Wie gesagt, kann ich dort kein HTML Auslesen, da genau dies ja durch das Ajax script generiert wird.

    Sorry, aber durch Javascript werden nur HTML-Elemente in das aktuelle Dokument nachgeladen, das Gesamtdokument wird dadurch zwar verändert aber enthält nicht nur 1 Element wie Du annimst.. Es kann allerdings beim DocumentCompleted Event passieren das Webbrowser1.Document=Nothing ist, weil der Browser immer noch am Navigieren ist, das sollte also immer Deine erste Abfrage sein.