views:

914

answers:

2

I was playing around with the method that was suggested as an answer to another of my questions (http://stackoverflow.com/questions/1731752/automate-website-log-in-and-form-filling), and noticed something curious.

The answer to the above question was to use a series of javascript calls as URL's in order to fill in a web form and submit it. I have been trying to do this automatically inside a VB .NET program with no success.

The original example I was given doesn't work, presumably because you are waiting on the same thread as that in which the WebBrowser control is working:

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

If you don't wait at all, it doesn't work either, of course. The URL you originally browse to is interrupted. But interestingly, you cannot perform the "navigations" to the javascript calls without delay, either.

So I've tried two other methods: using the DocumentCompleted event to wait to browse to the nest URL until the browser has finished loading the page. Unfortunately, DocumentCompleted does not always fire, and doesn't seem to fire after each javascript URL.

The second method I tried was to put the wait in a separate thread:

 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

The curious thing is that the check of ReadyState (or IsBusy, for that matter) in the wait loop will sometimes throw a InvalidCastException. Presumably calls to these are not thread safe? I have no idea. If I put the offending call inside a Try block, the wait loop just fails to work. In fact, it even seems the exception "persists" to screw everything up, because even stepping through the code with the try block Visual Studio freezes for a good 10 to 20 seconds (it does the same without the try block).

Any ideas?

A: 

Following msdn article: "There are four methods on a control that are thread safe to call: Invoke, BeginInvoke, EndInvoke and CreateGraphics and InvokeRequired property"

Therefore call to WebBrowser1.ReadyState in Sub Wait is not thread safe

volody