Textbox not updating after thread abort - vb.net

I have a thread which is executing a sub and within a sub I am updating the textbox during a "Do while" loop is in progress. I then created a cancel button to abort the thread.
When the button is clicked then the thread is aborted but the textbox (Status_Txtbox.Text) doesn't get updated with the message "Parsing is terminated". I tried to do the debug and I see the code is executed perfectly and if condition for thread.isalive satisfies but not sure why the textbox doesn't get updated with the message.
Any idea how to update the textbox once thread is aborted ?
Dim thrd1 As Thread
Private Sub Parse_Btn_2G_Click(sender As Object, e As RoutedEventArgs) Handles Parse_Btn_2G.Click
Parse_Btn_2G.IsEnabled = False
Scan_Btn_2G.IsEnabled = False
cancel_Btn_2G.IsEnabled = True
Dim start As DateTime = DateTime.Now
Dim elapsedtime As Double
Dim action As Action
thrd1 = New Thread(Sub()
ButtonClickWork.DoWork()
action = Sub()
Status_Txtbox.Text = "Parsing Data, Please wait..."
End Sub
Me.Dispatcher.Invoke(action)
End Sub)
thrd1.Start()
elapsedtime = (DateTime.Now.Subtract(start).TotalSeconds) / 60
elapsedtime = Math.Round(elapsedtime, 2)
Status_Txtbox.Text = " Managed Objects in XML, total time elapsed is" & elapsedtime
End Sub
Private Sub Cancel_Btn_2G_Click(sender As Object, e As RoutedEventArgs) Handles cancel_Btn_2G.Click
Try
If cancel_Btn_2G.IsEnabled = True Then
If MsgBox("Do you really want to exit Parsing?", vbYesNo) = MsgBoxResult.Yes Then
Parse_Btn_2G.IsEnabled = True
Scan_Btn_2G.IsEnabled = True
cancel_Btn_2G.IsEnabled = False
thrd1.Abort()
thrd1.Join()
If thrd1.IsAlive = False Then
Status_Txtbox.Text = "Parsing is terminated"
End If
End If
End If
Catch ex As ThreadAbortException
Status_Txtbox.Text = "Parsing is terminated"
End Try
End Sub

Related

WebBrowser won't fire DocumentCompeted event or print

So I've been looking around for a solution to this for a little over a week to no avail. I have a program that needs to be able to print htm(l) files and I'm having a terrible time getting it to comply.
This is the code I'm using at the moment:
Private Sub HtmlPrinterLaunch(i As Integer)
'Dim htmlWBPrinter As New WebBrowser()
'AddHandler htmlWBPrinter.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf HtmlPrinter)
'htmlWBPrinter.Visible = True
'htmlWBPrinter.ScriptErrorsSuppressed = False
'htmlWBPrinter.Show()
''frmHTMLPrint.wbPrintHtml.AllowNavigation = True
''AddHandler frmHTMLPrint.wbPrintHtml.DocumentCompleted, AddressOf HtmlPrinter
''frmHTMLPrint.wbPrintHtml.Visible = False
''frmHTMLPrint.wbPrintHtml.Navigate("file:///" & IO.Path.GetFullPath(_prints(i).SourcePathFileName))
''Application.Run(frmHTMLPrint)
''Dim appPath As String = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase)
''Dim reportPath As String = Path.Combine(appPath, Path.GetFileName(_prints(i).SourcePathFileName))
''htmlWBPrinter.Url = New Uri(reportPath) 'New Uri(Path.Combine("file:///", reportPath)) 'New Uri("file://" & IO.Path.GetFullPath(_prints(i).SourcePathFileName))
'htmlWBPrinter.Url = (New Uri(Path.Combine("file:///" & IO.Path.GetFullPath(_prints(i).SourcePathFileName))))
'While ((htmlWBPrinter.DocumentText = ""))
' Thread.Sleep(10000)
'End While
'htmlWBPrinter.ShowPrintDialog()
'' htmlWBPrinter.Dispose()
Dim wb As New WebBrowser
AddHandler wb.DocumentCompleted, Sub() If wb.ReadyState = WebBrowserReadyState.Complete Then wb.Print()
wb.ScriptErrorsSuppressed = True
Dim url As New Uri(Path.Combine("file:///" & IO.Path.GetFullPath(_prints(i).SourcePathFileName)))
wb.Navigate(url)
End Sub
Private Sub HtmlPrinter(sender As Object, e As WebBrowserDocumentCompletedEventArgs)
state = 4
Dim wbPrinter As WebBrowser = CType(sender, WebBrowser)
wbPrinter.Print()
wbPrinter.Dispose()
'frmHTMLPrint.BeginInvoke(New Action(Sub() frmHTMLPrint.Close()))
End Sub
As you can see I have a couple attempts in there (kept some older code that sort of worked but I'd rather not us it as I kept getting pop ups)
Some likely related issues:
-the WebBrowser state stays in loading (1)
-the Url never updates with the file:///path even if I pass it directly
So to put it in brief, my in code WebBrowser control won't hit the DocumentCompleted event, nor will it print out files. I need this code to print documents with no input from the user. What am I missing here?
Edit:
So I've messed around with this some more. I have the webbrowser control on its own form and I can get it to load/print when called from the main thread, but I'm having no luck invoking it. My current code for invocation:
If wbPrintHtml.InvokeRequired Then
If url.SourcePathFileName = "about:blank" Then
wbPrintHtml.Invoke(CType(Sub()
wbPrintHtml.Navigate(url.SourcePathFileName)
End Sub, MethodInvoker))
Else
wbPrintHtml.Invoke(CType(Sub()
wbPrintHtml.Navigate("file:///" & url.SourcePathFileName)
End Sub, MethodInvoker))
End If
Else
If url.SourcePathFileName = "about:blank" Then
wbPrintHtml.Navigate(url.SourcePathFileName)
Else
wbPrintHtml.Navigate("file:///" & url.SourcePathFileName)
End If
End If
I finally got this to work as intended. It's not the prettiest thing in the world, but it functions, and I'm ok with that.
Public Class frmHTMLPrint
Public Shared formHandle As frmHTMLPrint
Private Sub wbPrintHtml_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles wbPrintHtml.DocumentCompleted
Dim wbPrinter As WebBrowser = CType(sender, WebBrowser)
If wbPrinter.ReadyState = WebBrowserReadyState.Complete AndAlso Not wbPrinter.Url.ToString() = "about:blank" Then
wbPrinter.Print()
End If
End Sub
Shared Function setURL(url As Reporter.ReportServer.PrintMessageType) As Boolean
If formHandle.wbPrintHtml.InvokeRequired Then
If url.SourcePathFileName = "about:blank" Then
formHandle.wbPrintHtml.Invoke(CType(Sub()
formHandle.wbPrintHtml.Navigate(url.SourcePathFileName)
End Sub, MethodInvoker))
Else
formHandle.wbPrintHtml.Invoke(CType(Sub()
formHandle.wbPrintHtml.Navigate("file:///" & url.SourcePathFileName)
End Sub, MethodInvoker))
End If
Dim wbReady As WebBrowserReadyState
formHandle.wbPrintHtml.Invoke(CType(Sub()
wbReady = formHandle.wbPrintHtml.ReadyState
End Sub, MethodInvoker))
While ((Not wbReady = WebBrowserReadyState.Complete))
Application.DoEvents()
formHandle.wbPrintHtml.Invoke(CType(Sub()
wbReady = formHandle.wbPrintHtml.ReadyState
End Sub, MethodInvoker))
End While
Return wbReady = 4
Else
If url.SourcePathFileName = "about:blank" Then
formHandle.wbPrintHtml.Navigate(url.SourcePathFileName)
Else
formHandle.wbPrintHtml.Navigate("file:///" & url.SourcePathFileName)
End If
While ((Not formHandle.wbPrintHtml.ReadyState = WebBrowserReadyState.Complete))
Application.DoEvents()
End While
Return formHandle.wbPrintHtml.ReadyState = 4
End If
End Function
Private Sub frmHTMLPrint_Load() Handles Me.Load
InitializeComponent()
Dim wbInitializer As New Reporter.ReportServer.PrintMessageType
formHandle = Me
wbInitializer.SourcePathFileName = "about:blank"
setURL(wbInitializer)
End Sub
End Class

How do I call a function on the main thread from another thread in vb

I have a timer function which is called on my windows form which works perfectly when a button on the UI is clicked. However, in my application Im connecting to a server using a TCP socket, when the server disconnects Im catching the exception thrown and Id like at this point for the timer to start (ticker) then run the reconnection.
When i reference the timer from the try catch it wont run, so I imagine its because its on the main thread?
Here is my code for the timer:
Public Delegate Sub DroppedConnectionDelegate(ByVal ReConn As Integer)
Public Sub DroppedConnection(ByVal ReConn As Integer)
Console.Write("Dropped Connection()")
Thread.Sleep(1000)
If ReConn = 1 Then
MakeConnection(My.Settings.savedIP, False)
Else
isRunning = False
Second = 0
'client.Close()
'client.Dispose()
Timer1.Interval = 1000
Timer1.Enabled = True
Timer1.Start() 'Timer starts functioning
End If
' End If
End Sub
Public Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Second = Second + 1
Console.WriteLine(Second)
If Second >= 10 Then
Timer1.Stop() 'Timer stops functioning
Timer1.Enabled = False
MakeConnection(ServerAddressString, False)
End If
End Sub
Here is the sub from where Id like to call the timer on catch exception:
Public Shared Sub ReceiveCallback(ByVal ar As IAsyncResult)
Dim state As StateObject = CType(ar.AsyncState, StateObject)
Dim client As Socket = state.workSocket
Dim strReceiveBuffer As String = String.Empty
' Read data from the remote device.
Dim bytesRead As Integer
Console.WriteLine("ar to string = " & ar.ToString)
'While client.Connected
If (AsynchronousClient.connectDone.WaitOne(5000, True)) Then
Try
If client.Connected = True Then
bytesRead = client.EndReceive(ar)
Console.WriteLine("Socket Connected")
If bytesRead > 0 Then
state.sb.Clear()
' There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead))
strReceiveBuffer = state.sb.ToString()
MainForm.main_Response = state.sb.ToString()
If strReceiveBuffer.IndexOf("doses-remaining") > 0 Then
response = state.sb.ToString()
MainForm.GetLabelTextToString(response)
'receiveDone.Set()
'isReceived = True
'Return
End If
' Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)
Else
' All the data has arrived; put it in response.
If state.sb.Length > 1 Then
response = state.sb.ToString()
End If
' Signal that all bytes have been received.
receiveDone.Set()
isReceived = True
End If
Else
MainForm.WriteLog("RecieveCallback() Error outside catch :" & Date.Today.ToString & " " & TimeOfDay)
MainForm.UpdateList("RecievecallBack error, attempting reconnect..." & client.RemoteEndPoint.ToString())
MainForm.isRunning = False
MainForm.DroppedConnection(0)
End If
Catch ex As Exception
'MessageBox.Show("ReceiveCallback Error, Check Log.")
MainForm.WriteLog("RecieveCallback() Error inside catch :" & Date.Today.ToString & " " & TimeOfDay & ex.Message)
MainForm.UpdateList("RecievecallBack error, attempting reconnect..." & client.RemoteEndPoint.ToString())
client.Shutdown(SocketShutdown.Both)
client.Close()
client.Dispose()
MainForm.isRunning = False
Dim d As DroppedConnectionDelegate = AddressOf MainForm.DroppedConnection
'MainForm.DroppedConnection(0)
d.BeginInvoke(0, Nothing, Nothing)
Exit Try
End Try
End If
' End While
End Sub 'ReceiveCallback
If I run the application and the server disconnects unexpectedly, currently it wont reconnect automatically. But it will run the timer if I click the connect button (which calls the timer anyway)
Can anyone help please?
Thanks in advance
I'm not 100% sure as I've never tried but I think that if you use a Timers.Timer instead of a Windows.Forms.Timer then what you're doing now will work. By default, a Timers.Timer will raise its Elapsed event on a secondary thread but you can assign a form or other control to its SynchronizingObject property to make it raise events on the UI thread. Calling Start from a secondary thread should be OK in that case.
If that doesn't work then you can do what you want by first marshalling a method call to the UI thread before starting the Timer. That might look like this:
Private Sub StartTimer()
If Me.InvokeRequired Then
Me.Invoke(New Action(AddressOf StartTimer))
Else
Me.Timer1.Start()
End If
End Sub
You can call that method on any thread and it will just work.
Note that setting the Enabled property and calling Start or Stop is redundant. Start already sets Enabled to True and Stop sets it to False. Do one or the other, not both. It is most appropriate to call a method when you know for a fact that you want to either start or stop the Timer. If you have a Boolean value that might be either True or False then you should use the property. Setting the property using a literal value is not wrong per se but it is not what was intended. An example of where it's appropriate to use the property is when you use the same method to both start and stop the Timer from a secondary thread:
Private Sub EnableTimer(enable As Boolean)
If Me.InvokeRequired Then
Me.Invoke(New Action(Of Boolean)(AddressOf EnableTimer), enable)
Else
Me.Timer1.Enabled = enable
End If
End Sub
Again, calling that method on any thread will just work.

How to properly exit from canceled BackgroundWorker async?

I'm currently using this code, (which is working) but I'm not satisfied with how it looks... Is there a more professional way to do it ?
Here's the code I use now :
Private Sub BackgroundWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker.DoWork
Try
If BackgroundWorker.CancellationPending Then
e.Cancel = True
Exit Sub
End If
LatestVersion = Web.DownloadString(UpdateLink)
If BackgroundWorker.CancellationPending Then
e.Cancel = True
Exit Sub
End If
If LatestVersion.Contains(InstalledVersion) Then
e.Result = UP_TO_DATE
Else
e.Result = OUTDATED
End If
Web.Dispose()
Catch ex As Exception
e.Result = ex.Message
End Try
End Sub
As you can see, I'm repeating two times the same condition. But imagine if there was more code, I should have repeat it more times...
My problem is that I would like to exit the Sub at anytime, as long as the BackgroundWorker.CancellationPending property is set to True.
I'm using the same condition two times because I wanna check if the operation has been canceled before, and after downloading of my string (I don't wanna wait for the string to be downloaded whereas I've already canceled the operation... it's a waste of time).
Should I use a While statement ? If yes, how ?
Don't use BackgroundWorker and this problem goes away. The code probably should be (mixture of VB and C#):
//Instance field:
CancellationTokenSource cts = ...;
//Update method
var downloadTask = HttpClient().DownloadString(UpdateLink, cts.Token);
await Task.WhenAll(downloadTask, cts.Token); //Wait with timeout
LatestVersion = await downloadTask;
If LatestVersion.Contains(InstalledVersion) Then
ShowResult(UP_TO_DATE);
Else
ShowResult(OUTDATED);
End If
And if you want to cancel, signal cts.
Also, error handling is missing. This is easy to test for and add.
I've found another solution. By the way, I've renamed Web by WebClient...
Here's my code :
Private Sub Form1_Load() Handles Form1.Load
AddHandler WebClient.DownloadStringCompleted, AddressOf WebClient_DownloadStringCompleted
End Sub
Private Sub BackgroundWorker_DoWork() Handles BackgroundWorker.DoWork
WebClient.DownloadStringAsync(New Uri(UpdateLink))
End Sub
Private Sub WebClient_DownloadStringCompleted(sender As Object, e As System.Net.DownloadStringCompletedEventArgs)
Dim Output As String = Nothing
If Not e.Cancelled Then
LatestVersion = e.Result
If LatestVersion.Contains(InstalledVersion) Then
Output = UP_TO_DATE
Else
Output = OUTDATED
End If
End If
End Sub
And, to cancel the operation, I run WebClient.CancelAsync().

How to make each form it's own thread and able to update?

I am have three(4 counting the main one) threads in my application(chat app) and I want to put each form into it's own thread(one thread does not need a form so it is irrelevant). The 2 forms I am working with are clocks with a local digital font included. The problem is that threadb will start and run, but threadc will remain as the text LABEL1 and not change at all to: MM/dd/yyyy hh:mm:ss tt. I am wondering how to get these threads to work together.
Here is the thread code(creating at beginning of porgram, this is from sub official_start):
threadb = New Threading.Thread(AddressOf form3threadsub)
threadc = New Threading.Thread(AddressOf form4threadsub)
threadb.IsBackground = False
threadc.IsBackground = False
threadb.Start()
threadc.Start()
Here is the code for the thread subs:
Private Sub form3threadsub()
Dim first As Boolean = False
Do
System.Threading.Thread.Sleep(100)
If first = False Then
form3.ShowDialog()
first = True
End If
form3.Label1.Text = DateTime.UtcNow.ToString("MM/dd/yyyy hh:mm:ss tt")
form3.Update()
Loop
End Sub
Private Sub form4threadsub()
Dim first As Boolean = False
Do
System.Threading.Thread.Sleep(100)
If first = False Then
Form4.ShowDialog()
first = True
End If
Form4.Label1.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss tt")
Form4.Update()
Loop
End Sub
Here is the button2 click:
Dim clicked As Boolean = True
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If clicked Then
threadb.Suspend()'obsolete
threadc.Suspend()'obsolete
Button2.Text = "Resume Clocks"
clicked = False
Else
threadb.Resume()'obsolete
threadc.Resume()'obsolete
Button2.Text = "Pause Clocks"
clicked = True
End If
End Sub
I think I found my working solution. I took the extra forms out and replaced them with labels on the main form. Each label is it's own thread and updates accordingly.

VB.net ListView Adding Items Multiple Times

I am having an issue where I get multiple entries in my ListView for the same item if I run my action more than once.
I am creating a simple network scanner/hostname grabber that will add the items to the listview as they come back alive to my ping test.
When I run it the first time it runs fine and creates one entry as it should.
When I run it subsequent times it creates the item as many times as I have ran the code ex. 3rd time hitting start it creates each entry 3 times when it should just create the entry once.
Here is my go button code:
Private Sub Go_Click(sender As Object, e As EventArgs) Handles Go.Click
Dim verifyIP
ListView1.Items.Clear()
chkDone = 0
verifyIP = ipChk(ipAdd.Text)
If verifyIP = 1 Then
ipAddy = Split(ipAdd.Text, ".")
pingTest1.WorkerReportsProgress = True
pingTest1.WorkerSupportsCancellation = False
AddHandler pingTest1.ProgressChanged, AddressOf pingTest1_ProgressChanged
pingTest1.RunWorkerAsync()
pingTest2.WorkerReportsProgress = True
pingTest2.WorkerSupportsCancellation = False
AddHandler pingTest2.ProgressChanged, AddressOf pingTest2_ProgressChanged
pingTest2.RunWorkerAsync()
pingTest3.WorkerReportsProgress = True
pingTest3.WorkerSupportsCancellation = False
AddHandler pingTest3.ProgressChanged, AddressOf pingTest3_ProgressChanged
pingTest3.RunWorkerAsync()
pingTest4.WorkerReportsProgress = True
pingTest4.WorkerSupportsCancellation = False
AddHandler pingTest4.ProgressChanged, AddressOf pingTest4_ProgressChanged
pingTest4.RunWorkerAsync()
While chkDone < 4
wait(25)
End While
Else
MsgBox("IP Invalid")
End If
MsgBox("Done")
End Sub
Here is the code from one of the background workers I am using:
Private WithEvents pingTest1 As BackgroundWorker = New BackgroundWorker
Private Sub pingTest1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles pingTest1.DoWork
Try
Dim hostCheck
pingResult1 = 0
pingTestDone1 = 0
tryIP1 = ipAddy(0) & "." & ipAddy(1) & "." & ipAddy(2) & ".1"
If My.Computer.Network.Ping(tryIP1) = True Then
'Dim pingsender As New Net.NetworkInformation.Ping
'If pingsender.Send(tryIP).Status = Net.NetworkInformation.IPStatus.Success Then
Try
'Dim host As System.Net.IPHostEntry
hostCheck = ""
'host = System.Net.Dns.GetHostByAddress(tryIP3)
'MsgBox(host.HostName)
'host3 = host.HostName
'hostCheck = System.Net.Dns.GetHostEntry(tryIP3).HostName
hostCheck = System.Net.Dns.GetHostByAddress(tryIP1)
'get the hostname property
hostCheck = hostCheck.HostName
pingTest1.ReportProgress("1", hostCheck)
Catch f As Exception
'MsgBox("Error: " & f.Message)
pingTest1.ReportProgress("1", "No Hostname Found")
End Try
Else
pingResult1 = 2
End If
Catch d As Exception
MsgBox("There was an error trying to ping the IP Address: " & d.Message)
End Try
End Sub
Private Sub pingTest1_ProgressChanged(e.ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
MsgBox("Hey")
Dim str(2) As String
Dim itm As ListViewItem
str(0) = tryIP1 & " Is Alive!!!"
str(1) = e.UserState
itm = New ListViewItem(str)
ListView1.Items.Add(itm)
str(0) = ""
str(1) = ""
itm = Nothing
End Sub
Private Sub pingTest1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles pingTest1.RunWorkerCompleted
chkDone = chkDone + 1
End Sub
I added the Hey box and sure enough the ProgressChanged event gets triggered the amount of times I have hit the Go button. Is it something I have coded incorrectly?
It's most likely because you're adding, but not removing your handlers for the progress changed, so you're handling the event multiple times.
Try adding your Progress Changed Event Handlers when you're instantiating your Background workers, rather than every time you click your button. This way they will only handled once.