Auto Website Login: SetAttribute not working - vb.net

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

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

Related

Running an external app in textbox (how to preserve all functionality?)

I'm trying to run a cmd like application (so no GUI) in my form. In the example down below I called it ExternalApp.exe. The code itself works; I can send commands to it by entering them in TextBox2. The issue is that ExternalApp normally uses a command prompt and supports things like displaying one single screen of output and then wait for an enter before showing the next one. This is no longer working, all output is sent to TextBox1 all at once.
Is there any way to have ExternalApp behave like it normally does? I hope I'm explained myself a bit clear. Thanks for any help in advance!
Kind regards,
Eric
Public Class Form1
Dim WithEvents P As New Process
Dim SW As System.IO.StreamWriter
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
P.EnableRaisingEvents = True
Me.Text = "My title"
AddHandler P.OutputDataReceived, AddressOf DisplayOutput
P.StartInfo.CreateNoWindow() = True
P.StartInfo.UseShellExecute = False
P.StartInfo.RedirectStandardInput = True
P.StartInfo.RedirectStandardOutput = True
P.StartInfo.FileName = "ExternalApp.exe"
P.StartInfo.Arguments = ""
P.Start()
P.SynchronizingObject = Me
P.BeginOutputReadLine()
SW = P.StandardInput
SW.WriteLine()
End Sub
Private Sub DisplayOutput(ByVal sendingProcess As Object, ByVal output As DataReceivedEventArgs)
TextBox1.AppendText(output.Data() & vbCrLf)
End Sub
Private Sub Textbox2_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox2.KeyPress
If e.KeyChar = Chr(Keys.Return) Then
SW.WriteLine(TextBox2.Text)
End If
End Sub
Private Sub myProcess_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles P.Exited
Me.Close()
End Sub End Class

WebBrowser DocumentComplete Not Loaded on First Click VB.Net

I have a problem when click button 1st time, webbrowser.DocumentText always not loaded, but on 2nd or 3rd time clickings the documentText is ALWAYS loaded.
Glad if someone can advice.
my code as below:
Private Property pageready As Boolean = False
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button3.Click
WebBrowser1.Navigate(url)
WaitForPageLoad()
RichTextBox1.Text = WebBrowser1.DocumentText
Msgbox ("Document Loaded")
End Sub
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
Task Delay till the DocumentComplete loaded is the solustion for me.
Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button3.Click
WebBrowser1.Navigate(url)
Await ExampleMethodAsync()
RichTextBox1.Text = WebBrowser1.DocumentText
Msgbox ("Document Loaded")
End Sub
Async Function ExampleMethodAsync() As Task
' The following line simulates a task-returning asynchronous process.
Await Task.Delay(1750)
End Function
Here are two different ways you can dynamically wait for the WebBrowser to load the entire page:
Extending your Async/Await method to actually wait for the page to load you can use a ManualResetEvent. The MRE functions as a door: When you call Set() you open the door, when you call Reset() you close it, and when you call WaitOne(-1) you wait by the door for it to open (if it's closed).
Private DocumentCompletedResetEvent As New ManualResetEvent(False)
Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button3.Click
WebBrowser1.Navigate(url)
Await ExampleMethodAsync()
RichTextBox1.Text = WebBrowser1.DocumentText
Msgbox("Document Loaded")
End Sub
Async Function ExampleMethodAsync() As Task
DocumentCompletedResetEvent.Reset() 'Makes sure that the MRE is closed.
DocumentCompletedResetEvent.WaitOne(-1) 'Waits for it to open.
End Function
Private Sub WebBrowser1_DocumentCompleted(sender As System.Object, e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
DocumentCompletedResetEvent.Set() 'Opens the MRE.
End If
End Sub
The second method adds a temporary event handler to the DocumentCompleted event. Using Lambda expressions you may create an in-line method for the event handler.
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
WebBrowser1.Navigate(TextBox1.Text)
'Creates a temporary event handler.
Dim DocumentCompletedHandler As WebBrowserDocumentCompletedEventHandler = _
Sub()
If WebBrowser1.ReadyState = WebBrowserReadyState.Complete Then
RemoveHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler 'Removes the temporary event handler.
RichTextBox1.Text = WebBrowser1.DocumentText
Msgbox("Document Loaded")
End If
End Sub
AddHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler 'Adds the temporary event handler.
End Sub

Is there a way to run a webbrowser on a form in the background? [duplicate]

I'm running a backgroundworker for my webbrowser on a winform, it works and all, but why does the UI lags or freezes for a moment to wait for the page to load when I want it to load in the background so that the UI is fully functional?
Private Property pageready As Boolean = False
Public Sub WaitForPageLoad()
AddHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf PageWaiter)
While Not pageready
Application.DoEvents()
End While
pageready = False
End Sub
Public 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
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Try
WebBrowser1.Navigate("http://www.mypage.com")
WaitForPageLoad() >>>> Lags and freezes UI for a sec or two to wait for page loading. How can i do this in the background?
Catch ex As Exception
End Try
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'Here i will get my attributes off the webpage ect....
End Sub

"For Each" loop : Application Freeze in Vb.net

I am using the following code to get the size of files inside a directory
and put it in Label1:
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _
"\windows",Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly,_
"*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
Label1.Text = Label1.Text + filesizelabel.Length
Next
The problem is that i have more than 50 for each loops (a system cleaning app).
When I run the loops my app freezes until the loops finish, even if I run one loop.
Is there a solution to make it show the name of the current file? I tried this as well, but it also froze my application:
label2.text = foundfile
The application does not respond to any click, until it finishes the loops. It shows the size in Label1 and the last scanned file in Label2. This also freezes the application:
system.threading.thread.sleep(100)
Is there any alternative to foreach or a solution to fix this issue?
Here's a quick example using Async/Await with a Button Click() Handler:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Await Task.Run(Sub()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
End Sub)
Button1.Enabled = True
End Sub
For VB.Net 2010, try this instead:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Dim T As New System.Threading.Thread(AddressOf Worker)
T.Start()
End Sub
Private Sub Worker()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
Me.Invoke(Sub()
Button1.Enabled = True
End Sub)
End Sub
This is a prime candidate for a background worker.
Have a read about how they work, but at a high level the task is run in another thread with some events that you access in your main UI thread.
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub buttonStart_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub buttonCancel_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If bw.WorkerSupportsCancellation = True Then
' this will allow the user to cancel the work part way through
bw.CancelAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
' your slow code goes here
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub
Derek has put a bad code, it's not working and Idle mind code does not work on .NET 2.0
Dereks approach is working if code is complete, as below:
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub app_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
..
End sub
Private Sub Btn_Click(sender As Object, e As EventArgs) Handles Btn.Click
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
'your work to not freeze form
end sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub

Getting web page after calling DownloadStringAsync()?

I don't know enough about VB.Net yet to use the richer HttpWebRequest class, so I figured I'd use the simpler WebClient class to download web pages asynchronously (to avoid freezing the UI).
However, how can the asynchronous event handler actually return the web page to the calling routine?
Imports System.Net
Public Class Form1
Private Shared Sub DownloadStringCallback2(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs)
If e.Cancelled = False AndAlso e.Error Is Nothing Then
Dim textString As String = CStr(e.Result)
'HERE : How to return textString to the calling routine?
End If
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim client As WebClient = New WebClient()
AddHandler client.DownloadStringCompleted, AddressOf DownloadStringCallback2
Dim uri As Uri = New Uri("http://www.google.com")
client.DownloadStringAsync(uri)
'HERE : how to get web page back from callback function?
End Sub
End Class
Thank you.
Edit: I added a global, shared variable and a While/DoEvents/EndWhile, but there's got to be a cleaner way to do this :-/
Public Class Form1
Shared page As String
Public Shared Sub AlertStringDownloaded(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs)
' If the string request went as planned and wasn't cancelled:
If e.Cancelled = False AndAlso e.Error Is Nothing Then
page = CStr(e.Result)
End If
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim wc As New WebClient
AddHandler wc.DownloadStringCompleted, AddressOf AlertStringDownloaded
page = Nothing
wc.DownloadStringAsync(New Uri("http://www.google.com"))
'Better way to wait until page has been filled?
While page Is Nothing
Application.DoEvents()
End While
RichTextBox1.Text = page
End Sub
End Class
You can set the RichTextBox1.Text directly in the completed handler if you make the handler function an instance method instead of shared.
Public Class Form1
Private Sub AlertStringDownloaded(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs)
' If the string request went as planned and wasn't cancelled:
If e.Cancelled = False AndAlso e.Error Is Nothing Then
RichTextBox1.Text = CStr(e.Result)
End If
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim wc As New WebClient
AddHandler wc.DownloadStringCompleted, AddressOf AlertStringDownloaded
page = Nothing
wc.DownloadStringAsync(New Uri("http://www.google.com"))
End Sub
End Class
You haven't found a clean way. Close your form while the download is taking place and behold the kaboom you'll get. You'll have to at least set the form's Enabled property to False.
Look at the BackgroundWorker class to do this cleanly.