1

I'm creating a program dashboard and one feature is that the user is automatically logged into a website site using stored credentials in the program (no need to open chrome or FF).

In the program, await task delay is working, I see the username and password fields populate before submission (click), but when it tries to submit, the browser, built into the form, acts like the page is empty and no credentials have been entered? I should mention that I can see the username and password entered in the form, but the page acts like nothing has been entered. What am I doing wrong here?

Side note: The button on the site we're connecting to doesn't have an element ID, only a type is shown...hence the workaround for Invokemember("Click")

Any help is appreciated.

    Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Label3.Visible = False
End Sub
Private Function login_thesite() As Task

    WebBrowser1.Document.GetElementById("username").SetAttribute("value", "Username")
    WebBrowser1.Document.GetElementById("Password").SetAttribute("value", "Password")


    Dim allelements As HtmlElementCollection = WebBrowser1.Document.All
    For Each webpageelement As HtmlElement In allelements
        If webpageelement.GetAttribute("type") = "submit" Then
            webpageelement.InvokeMember("click")
        End If
    Next

End Function

Private Property pageready As Boolean = False

    #Region "Page Loading Functions"
Private Sub WaitForPageLoad()
    AddHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
    While Not pageready
        Application.DoEvents()
    End While
    pageready = False
End Sub

Private Sub PageWaiter(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
    If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
        pageready = True
        RemoveHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
    End If
End Sub

    #End Region

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    If CheckBox1.Checked = True Then
        login_thesite()
        WaitForPageLoad()
    End If

End Sub
Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged

    TextBox1.Text = ""
    TextBox2.Text = ""
    Label3.Visible = True
    WebBrowser1.Navigate("https://thesite.com/#/login")
    WaitForPageLoad()
End Sub
End Class     
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • Are you awaiting hoping that the document is completed loading? Use the DocumentCompleted event. – LarsTech Jan 03 '19 at 19:54
  • Then why do you have all that async / await stuff happening? – LarsTech Jan 03 '19 at 20:15
  • @LarsTech Yes, the document is fully loading. – user9289698 Jan 03 '19 at 20:17
  • @larsTech I am new to vb and was troubleshooting. – user9289698 Jan 03 '19 at 20:17
  • Well, get rid of it. And use the DocumentCompleted event instead of the ProgressChanged event. – LarsTech Jan 03 '19 at 20:18
  • @larsTech I updated the code as you suggested, I think I did it correctly, but I'm still getting the same thing. – user9289698 Jan 03 '19 at 20:55
  • 1
    Get rid of that DoEvents and the while loop. Get rid of the AddHandler and just have the method `Handles DocumentCompleted` at the end of it. The event will fire *after* the document is loaded. Loginthesite isn't a Task function anymore, looks like it should just be a Sub. Your button click is inspecting the document, *then* waiting to load the page. That doesn't sound right. You need to fix all of that before we even get to the debugging part. – LarsTech Jan 03 '19 at 21:01
  • Please do not use that code. Using `DoEvents()` is _**very bad practice**_ and should _**NEVER**_ be used to keep the UI responsive as it is always used incorrectly! Whoever initially created that `WaitForPageLoad()` method (which is now very widespread) did not know what he/she was doing. As Lars said you should rely only on the `DocumentCompleted` event. Apart from `Async/Await`, code that waits on a certain line in a UI thread is unnatural. For a short summary of why `DoEvents()` is bad, please see the first section of [this answer of mine](https://stackoverflow.com/a/45571728/3740093). – Visual Vincent Jan 03 '19 at 21:39

1 Answers1

2

You don't need any async procedure here. The WebBrowser.DocumentCompleted event is already invoked asynchronously. DoEvents() is equally useless if not disruptive.

You just need to subscribe to the DocumentCompleted event and call the Navigate method to let the WebBrowser load the remote Html resource.

When the HtmlDocument is finally loaded, the WebBrowser will signal its completion setting its state to WebBrowserReadyState.Complete.

About the Html Input Element and Forms:
here, the code is supposing that there's only one Form in that HtmlDocument.
It may be so, but it may be not. A Html Document can have more than one Form and each Frame can have its own Document. IFrames will have one each for sure.

Read the notes in this answer (C# code, but you just need the notes) for more information on how to handle multiple Frames/IFrames


Button1 will wire up the DocumentCompleted event and call Navigate().
When the Document is completed, the code in the event handler will run and perform the LogIn procedure.
The event handler is then removed, since it has complelted its task and you still need to use the WebBrowser for other purposes.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Button1.Enabled = False
    WebSiteLogIn()
End Sub

Private Sub WebSiteLogIn()
    AddHandler WebBrowser1.DocumentCompleted, AddressOf PageWaiter
    WebBrowser1.Navigate("https://thesite.com/#/login")    
End Sub

Private Sub PageWaiter(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
    If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
        WebBrowser1.Document.GetElementById("username").SetAttribute("value", "Username")
        WebBrowser1.Document.GetElementById("Password").SetAttribute("value", "Password")

        Dim allInputElements = WebBrowser1.Document.Body.All.
            Cast(Of HtmlElement).Where(Function(h) h.TagName.Equals("INPUT")).ToList()

        For Each element As HtmlElement In allInputElements
            If element.GetAttribute("type").ToUpper().Equals("SUBMIT") Then
                element.InvokeMember("click")
            End If
        Next

        RemoveHandler WebBrowser1.DocumentCompleted, AddressOf PageWaiter
        Button1.Enabled = True
    End If
End Sub
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • Your code can add multiple AddHandlers, but not always the corresponding RemoveHandler. Not sure why you would even want to juggle the AddHandler and RemoveHandler stuff. Just add the event once through the designer and be done. – LarsTech Jan 03 '19 at 22:09
  • @LarsTech From what the OP is saying, the WebBroser is also used after the login is completed; the DocumentCompleted event will be raised for all the Web pages visited after the login. So, adding the handler and removing it after the login is completed is not wrong. What could be done here, is to disable the Button that triggers the LogIn proc and enable it again when the procedure is completed. – Jimi Jan 03 '19 at 22:19
  • Thanks for your suggestions and input, here. I appreciate it! I'll be sure to research what you've suggested and give it a try. – user9289698 Jan 04 '19 at 13:40
  • Sure. If you have questions (directly related to this code), post a comment here. – Jimi Jan 04 '19 at 13:43