InvalidCastException z WebBrowser.IsBusy lub ReadyState (VB .NET)
Bawiłem się metodą zaproponowaną jako odpowiedź na inne moje pytanie (Zautomatyzuj logowanie do witryny i wypełnianie formularzy?) i zauważyłem coś ciekawego.
Odpowiedzią na powyższe pytanie było użycie serii wywołań javascript jako adresów URL w celu wypełnienia formularza internetowego i przesłania go. Próbowałem to zrobić automatycznie w programie VB .NET bez powodzenia.
Oryginalny przykład, który mi podano, nie działa, prawdopodobnie dlatego, że czekasz na ten sam wątek, w którym działa formant WebBrowser:
WebBrowser1.Navigate("http://www.google.com")
Do While WebBrowser1.IsBusy OrElse WebBrowser1.ReadyState <> WebBrowserReadyState.Complete
Threading.Thread.Sleep(1000)
Application.DoEvents()
Loop
WebBrowser1.Navigate("javascript:function%20f(){document.forms[0]['q'].value='stackoverflow';}f();")
Threading.Thread.Sleep(2000) 'wait for javascript to run
WebBrowser1.Navigate("javascript:document.forms[0].submit()")
Threading.Thread.Sleep(2000) 'wait for javascript to run
Jeśli w ogóle nie czekasz, to oczywiście nie działa. Adres URL, do którego pierwotnie przeglądasz, został przerwany. Co ciekawe, nie można również bezzwłocznie wykonywać „nawigacji” do wywołań javascript.
Próbowałem więc dwóch innych metod: za pomocą zdarzenia DocumentCompleted poczekać na przejście do adresu URL gniazda, dopóki przeglądarka nie zakończy ładowania strony. Niestety, DocumentCompleted nie zawsze uruchamia się i wydaje się, że nie uruchamia się po każdym javascriptowym URL.
Drugą metodą, którą próbowałem, było umieszczenie oczekiwania w osobnym wątku:
Private Delegate Sub SetTextDelegate(ByVal TheText As String)
Private Sub delSetText(ByVal TheText As String)
WebBrowser1.Navigate(TheText)
End Sub
Private Sub BrowseTo(ByVal URL As String)
If WebBrowser1.InvokeRequired Then
Me.BeginInvoke(New SetTextDelegate(AddressOf delSetText), URL)
Else
WebBrowser1.Navigate(URL)
End If
End Sub
Private Sub TargetURL()
BrowseTo("http://www.google.com")
End Sub
Private Sub TypeSomethingIn()
BrowseTo("javascript:function%20f(){document.forms[0]['g'].value='test';}f();")
End Sub
Private Sub SubmitForm()
BrowseTo("javascript:document.forms[0].submit()")
End Sub
Private Sub Wait()
While True
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then Exit Sub
Threading.Thread.Sleep(100)
End While
End Sub
Private Sub AutoBrowse()
TargetURL()
Wait()
TypeSomethingIn()
Wait()
SubmitForm()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim t As Threading.Thread
t = New Threading.Thread(AddressOf AutoBrowse)
t.Start()
End Sub
Ciekawostką jest to, że sprawdzenie ReadyState (lub IsBusy, o to chodzi) w pętli wait czasami rzuca wyjątek InvalidCastException. Przypuszczalnie połączenia z tymi nie są bezpieczne dla wątków? Nie mam pojęcia. Jeśli umieściłem obraźliwe wywołanie w bloku Try, pętla oczekiwania nie działa. W rzeczywistości wydaje się, że wyjątek „utrzymuje się”, aby wszystko spieprzyć, ponieważ nawet przejście przez kod przy pomocy bloku próbnego Visual Studio zawiesza się na dobre 10 do 20 sekund (robi to samo bez bloku try).
Jakieś pomysły?