How to check for BackgroundWorker cancellation between each of multiple function calls - vb.net

I have a BackgrounWorker that calls vairous methods from a custom class called ExcelOutput. The below function works, but it does not allow me to check for cancellation.
Private Sub bw_DoWork(ByVal sender As Object,
ByVal e As DoWorkEventArgs)
Dim UserDump As New CurrentUserDumpInfo(Me.MainForm.ConfigForm) ' Create a new instance of the CurrentUserDumpInfo
Dim Excel As New ExcelOutput(Me) ' Create a new instance of the ExcelOutput
Dim FirstRow As Integer = 4 ' The row on which the output should start
Dim CurrentRow As Integer ' The currnet row on which the output shoud continue
'** Prepare the CurrentUserDumpInfo and the ExcelOutput *'
Call UserDump.prepare()
Call Excel.Prepare()
CurrentRow = Excel.OutputGroup("General", UserDump.lstGeneralHeaders, UserDump.lstGeneralData, FirstRow)
CurrentRow = Excel.OutputGroup("Address", UserDump.lstAddressHeaders, UserDump.lstAddressData, CurrentRow + 2)
Call Excel.AutofitContents(4, CurrentRow)
Call Excel.AlignCells(4, CurrentRow)
Call Excel.StyleWorksheet()
Call Excel.MakeSafeCopy()
Call Excel.LockWorksheet()
Call Excel.Finish(UserDump.CurrentUser.FullName)
End Sub
To do this, I've set each methods listed above to check for errors (using Try/Catch), and if there is an error, I set bw.WorkerSupportsCancellation = True call the bw.CancelAsync() method (you'll notice I pass Me when initiating the ExcelOutput instance, making this possible).
This method works, but to implement it fully I have to wrap every call in an If block like so, making the code very long and difficult to read (each call goes from 1 line to 6 lines) -
If bw.CancellationPending = True Then
e.Cancel = True
Exit Sub
Else
CurrentRow = Excel.OutputGroup("General", UserDump.lstGeneralHeaders, UserDump.lstGeneralData, 4)
End If
Is there a better way to do this, that will both keep the code short and sweat but offer the functionality of the cancellation check? Thanks.
Update
Thanks to the answer below, here is the exact bw_DoWork() method that I am now using, which achieves exactly what I was looking for -
Private Sub bw_DoWork(ByVal sender As Object,
ByVal e As DoWorkEventArgs)
Dim UserDump As New CurrentUserDumpInfo(Me.MainForm.ConfigForm) ' Create a new instance of the CurrentUserDumpInfo
Dim Excel As New ExcelOutput(Me) ' Create a new instance of the ExcelOutput
Dim FirstRow As Integer = 4 ' The row on which the output should start
Dim CurrentRow As Integer ' The currnet row on which the output shoud continue
Dim Counter As Integer = 0
While Not bw.CancellationPending = True
Select Case Counter
Case 0 : Call UserDump.prepare() : Exit Select ' Prepare the CurrentUserDumpInfo object ready for use
Case 1 : Call Excel.Prepare() : Exit Select ' Prepare the ExcelOutput object ready for use
Case 2 : CurrentRow = Excel.OutputGroup("General", UserDump.lstGeneralHeaders, UserDump.lstGeneralData, FirstRow) : Exit Select
Case 3 : CurrentRow = Excel.OutputGroup("Address", UserDump.lstAddressHeaders, UserDump.lstAddressData, CurrentRow + 2) : Exit Select
Case 4 : CurrentRow = Excel.OutputGroup("Account", UserDump.lstAccountHeaders, UserDump.lstAccountData, CurrentRow + 2) : Exit Select
Case 5 : CurrentRow = Excel.OutputGroup("Profile", UserDump.lstProfileHeaders, UserDump.lstProfileData, CurrentRow + 2) : Exit Select
Case 6 : Call Excel.AutofitContents(4, CurrentRow) : Exit Select
Case 7 : Call Excel.AlignCells(4, CurrentRow) : Exit Select
Case 8 : Call Excel.StyleWorksheet() : Exit Select
Case 9 : Call Excel.MakeSafeCopy() : Exit Select
Case 10 : Call Excel.LockWorksheet() : Exit Select
Case 11 : Call Excel.Finish(UserDump.CurrentUser.FullName) : Exit Select
Case Else : Exit While
End Select
Counter += 1
End While
'** Check to see if the BackgroundWorker should be cancelled *'
If bw.CancellationPending = True Then e.Cancel = True
End Sub

There's a "common" rule not to use try/catch in a DoWork event. The exception is if you want to dispose or clean up objects. If an error occurs it will be available in the RunWorkerCompleted event (e.Error).
Private Sub bw_DoWork(sender As Object, e As DoWorkEventArgs) Handles bw.DoWork
Dim obj As IDisposable = Nothing
Dim [error] As Exception = Nothing
Try
obj = New Control()
Throw New Exception("Simulated exception")
Catch ex As Exception
[error] = ex
Finally
If (Not obj Is Nothing) Then
obj.Dispose()
obj = Nothing
End If
End Try
If (Not [error] Is Nothing) Then
Throw [error]
End If
End Sub
With that being said, you can try to do the work in a While Loop and check for cancellation after each cycle.
Private Sub bw_DoWork(sender As Object, e As DoWorkEventArgs) Handles bw.DoWork
Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
Dim num As Integer = Nothing
Dim obj As IDisposable = Nothing
Dim [error] As Exception = Nothing
Try
'TDOD: obj = New IDisposable ()
num = 0
While (Not e.Cancel)
Select Case num
Case 0
'Do somthing
Exit Select
Case 1
'Do somthing
Exit Select
Case 2
'Do somthing
Exit Select
Case Else
Exit While
End Select
num += 1
e.Cancel = worker.CancellationPending
End While
Catch ex As Exception
[error] = ex
Finally
If (Not obj Is Nothing) Then
obj.Dispose()
obj = Nothing
End If
End Try
If (Not [error] Is Nothing) Then
Throw [error]
ElseIf (Not e.Cancel) Then
'TODO: e.Result = Nothing
End If
End Sub

Related

System.Diagnostics.Process not properly closing after the application is closed

I have been trying to find a solution to why the ExitTool I am using is not properly closing out after I close the main application. What is happening is that when I close my application, the ExifTool stays running in the background and I have to manually kill it.
Here is the code snippet of the process startup.
Public Shared Sub ExecuteExifTool()
If ExifToolStarted Then Exit Sub
ExifToolStarted = True
Dim NowString As String = Date.Now.ToString("yyyyMMddHHmmss")
If Not IO.Directory.Exists(".\Runtime") Then IO.Directory.CreateDirectory(".\Runtime")
Dim GetDirectory As New IO.DirectoryInfo(".\Runtime")
If GetDirectory.GetFiles.Count > 0 Then
For Each i As IO.FileInfo In GetDirectory.GetFiles
If i.FullName.Contains("exif.Yv") AndAlso i.FullName.Contains("-cL") AndAlso i.FullName.Contains(".exe") Then : Try : i.Delete() : Catch : End Try : End If
Next
End If
HostName = ".\Runtime\exif.Yv" & NowString.Substring(0, 8) & "-cL" & NowString.Substring(8, 6) & ".exe"
IO.File.Copy(".\exiftool.exe", HostName)
Using ExifToolProcess As New Process
With ExifToolProcess
.StartInfo.RedirectStandardInput = True
.StartInfo.FileName = HostName
.StartInfo.UseShellExecute = False
.StartInfo.Arguments = "-stay_open" & " True -# " & "-"
.StartInfo.RedirectStandardOutput = True
.StartInfo.RedirectStandardError = True
.StartInfo.CreateNoWindow = True
.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
.Start()
.BeginOutputReadLine()
.BeginErrorReadLine()
End With
End Using
End Sub
The latest attempt to solve this issue was to try and launch another executable application; which essentially, is another Windows.Forms.Form that waits for the main application to close and then it will attempt to kill the process immediately afterwards, then dispose of itself. Here is the snippet.
Public Class KillProcess
Private _ProcessName As String
Public Property ProcessName As String
Get
Return _ProcessName
End Get
Set(value As String)
_ProcessName = value
End Set
End Property
Private _MainApp As Form
Public Property MainApplication As Form
Get
Return _MainApp
End Get
Set(value As Form)
_MainApp = value
End Set
End Property
Private Sub KillProcess_Load(sender As Object, e As EventArgs) Handles MyBase.Load
CleanupProcess()
End Sub
Public Sub CleanupProcess()
While Not MainApplication.IsDisposed
Dim FilesToDelete As New List(Of String)
Dim ProcessesToKill As New List(Of Process)
For Each p As Process In Process.GetProcesses
If p.ProcessName = ProcessName Then
FilesToDelete.Add(p.MainModule.FileName)
ProcessesToKill.Add(p)
End If
Next
For Each p As Process In ProcessesToKill
Try
p.Kill()
p.WaitForExit(10000)
p.Close()
Catch winException As System.ComponentModel.Win32Exception
Catch invalidException As InvalidOperationException
End Try
Next
End While
Me.Dispose()
End Sub
End Class
And here is the code snippet of the startup.
Public Sub CleanupTask()
Dim Handler As New Custodian.KillProcess With {.ProcessName = ExifToolHooker.HostName, .MainApplication = Me}
Windows.Forms.Application.Run(Handler)
End Sub
Private Sub CloseApplication(sender As Object, e As FormClosingEventArgs) Handles Me.Closing
Dim TaskHandler As Thread = New Thread(AddressOf CleanupTask)
TaskHandler.SetApartmentState(ApartmentState.STA)
TaskHandler.Start()
...
End Sub

UIAutomation getting progress value every second instead of just once

Hey all I am am currently getting the value for the progress bar that a application is using when I use the following code:
Dim progressBarThread = New Thread(New ThreadStart(AddressOf Me.ThreadProcSafe))
Me.Invoke(New MethodInvoker(AddressOf progressBarThread.Start))
Do
status = range.Current.Value.ToString
numwaits += 1
'Me.Invoke(New MethodInvoker(AddressOf progressBarThread.Start))
Wait(5)
Loop While status <> 100 AndAlso numwaits < 50
LogMessage("100%" + Environment.NewLine)
Delegate Sub SetTextCallback([text] As String)
Private Sub ThreadProcSafe()
Me.SetText(status)
End Sub
Private Sub SetText(ByVal [text] As String)
If Me.TextBox1.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.ProgressBar1.Value = [text]
End If
End Sub
Problem being is that it only gets the value once and doesn't constantly update the value as I am wanting. As you see in the code above, I tried to use Me.Invoke(New MethodInvoker(AddressOf progressBarThread.Start)) within the loop in order to see if it would update the value that way but it does not work and causes an error.
How can I accomplish this?

Async and Await, why two return values

I'm trying to get my head around Async and Await. It's going well but one thing I would like clarification on is why there are two return statements in my method. I'm really looking for an explanation of what is actually happening behind the scenes.
I'll post the full code below as it only amounts to around 80 lines. I'm talking about the central method AllSubfolderFiles, which has both Return counter and Return dirsFraction. What's actually happening with these?
Basically, it is a WinForm application that iterates all the files of subfolders, updating a ProgressBar for each iterated subfolder.
Imports System.IO
Public Class frmAsyncProgress
Private Sub frmAsyncProgress_Load(sender As Object, e As EventArgs) Handles MyBase.Load
barFileProgress.Minimum = 0
barFileProgress.Maximum = 100
btnCancel.Enabled = False
End Sub
Private Async Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
If String.IsNullOrWhiteSpace(txtPath.Text) Then
MessageBox.Show("Provide a location first.", "Location")
Exit Sub
End If
Dim sLocation As String = txtPath.Text.Trim()
If Not Directory.Exists(sLocation) Then
MessageBox.Show("Directory doesn't exist.", "Location")
Exit Sub
End If
Dim progressIndicator = New Progress(Of Integer)(AddressOf UpdateProgress)
btnStart.Enabled = False
btnCancel.Enabled = True
lblPercent.Text = "0%"
Dim allFiles As Integer = Await AllSubfolderFiles(sLocation, progressIndicator)
Debug.WriteLine(allFiles.ToString()) 'the number of subfolders iterated
btnStart.Enabled = True
btnCancel.Enabled = False
End Sub
Private Async Function AllSubfolderFiles(location As String, progress As IProgress(Of Integer)) As Task(Of Integer)
Dim dirsTotal As Integer = Directory.GetDirectories(location).Length
Dim dirsFraction As Integer = Await Task(Of Integer).Run(Function()
Dim counter As Integer = 0
For Each subDir As String In Directory.GetDirectories(location)
SubfolderFiles(subDir)
counter += 1
If progress IsNot Nothing Then
progress.Report(counter * 100 / dirsTotal)
End If
Next
Return counter
End Function)
Return dirsFraction
End Function
Private Sub UpdateProgress(value As Integer)
barFileProgress.Value = value
lblPercent.Text = (value / 100).ToString("#0.##%")
End Sub
Private Sub SubfolderFiles(location As String)
'source: http://stackoverflow.com/questions/16237291/visual-basic-2010-continue-on-error-unauthorizedaccessexception#answer-16237749
Dim paths = New Queue(Of String)()
Dim fileNames = New List(Of String)()
paths.Enqueue(location)
While paths.Count > 0
Dim sDir = paths.Dequeue()
Try
Dim files = Directory.GetFiles(sDir)
For Each file As String In Directory.GetFiles(sDir)
fileNames.Add(file)
Next
For Each subDir As String In Directory.GetDirectories(sDir)
paths.Enqueue(subDir)
Next
Catch ex As UnauthorizedAccessException
' log the exception or ignore it
Debug.WriteLine("Directory {0} could not be accessed!", sDir)
Catch ex As Exception
' log the exception or ...
Throw
End Try
End While
'could return fileNames collection
End Sub
End Class
My assessment is that counter is returned and then marshalled back onto the UI thread as dirsFraction, but I'm not convinced by my attempted explanation.
Inside your AllSubfolderFiles function you call Task.Run and pass in an anonymous function that returns with Return counter. AllSubfolderFiles awaits the result of that call and then returns with Return dirsFraction.
So, you have 2 returns in the same function because you have an anonymous function inside your original function. You can move that function out to its own named function which will make it clearer that there are 2 different functions here.

vb.net system.threading.thread ask the thread to cleanup and stop

I am new to vb.net multithreading, and I am facing a issue.
For threading I am using system.threading.thread function.
I know I can stop a thread using thread.abort call. But that's not a good choice (I have searched a bit and came to know that). This also will not work because my running threads needs some cleanup before cancellation.
So, I'm trying to do some kind of action that set a variable to UI thread like
dim bolStop as Boolean
So, if my UI thread set that Boolean value as true then all running thread can access/notice that change and can start cleanup process before stop/cancel the thread itself.
hope that makes sense...
best regards
Edit:1 (Source Is Pasted Here as Suggested By #DrunkenCodeMonkey
This is the code for Main Form (Form1.vb) (I think i need something here and also on the thread module (class) too) so, below i will paste module code too
Public Class Form1
Private objThreadList As ArrayList
Dim bolStop As Boolean
Private Sub cmd1_Click(sender As System.Object, e As System.EventArgs) Handles cmd1.Click
Dim intTLoop As Integer
sPrepareThreading()
For intTLoop = 1 To 5
sSendThread("", "Zakir", "Biplob")
Next
'sFinishThreading()
MessageBox.Show("Done!")
End Sub
Sub sPrepareThreading()
Dim intTLoop As Integer
objThreadList = New ArrayList
For intTLoop = 1 To 3
objThreadList.Add(Nothing)
Next intTLoop
End Sub
Sub sSendThread(strDraft As String, ByVal strTitle As String, ByVal strSubHeader As String)
Dim bolFalied As Boolean
Dim intTLoop As Integer
Dim objItem As System.Windows.Forms.ListViewItem
Dim objTData As objThreadCrossData
Dim objThreadClass As clsThread
Dim objNewThread As System.Threading.Thread
Zakir_RecheckThread:
My.Application.DoEvents()
If bolStop = True Then
Exit Sub
End If
bolFalied = True
For intTLoop = 1 To 3
bolFalied = True
If objThreadList(intTLoop - 1) Is Nothing Then
bolFalied = False
Else
If CType(objThreadList(intTLoop - 1), System.Threading.Thread).IsAlive = False Then
bolFalied = False
Else
bolFalied = True
End If
End If
If bolFalied = False Then
objTData = New objThreadCrossData
objTData.ID = ListView1.Items.Count
objTData.bolStop = False
objTData.strTime = DateTime.Now.ToString
objItem = ListView1.Items.Add(strTitle)
objItem.SubItems.Add(strSubHeader)
objItem = Nothing
objThreadClass = New clsThread(objTData, "", Me)
objNewThread = New System.Threading.Thread(AddressOf objThreadClass.StartThread)
objNewThread.IsBackground = True
objNewThread.Start()
objThreadList(intTLoop - 1) = objNewThread
Exit For
End If
Next
If bolFalied = True Then
GoTo Zakir_RecheckThread
Exit Sub
End If
End Sub
Public Sub ReceiveThreadMessage(ByVal objCrosData As Object)
Dim objTDataV1 As objThreadCrossData
objTDataV1 = CType(objCrosData, objThreadCrossData)
ListView1.Items(objTDataV1.ID).SubItems(1).Text = objTDataV1.strProgress
End Sub
Private Sub cmd2_Click(sender As System.Object, e As System.EventArgs) Handles cmd2.Click
REM This is where i like to stop the running thred either by setting bolStop = True and all running thead check that variable.
REM or by chate the thread (Thread Data Object Property naemd "bolStop" value"
REM please help me out...
End Sub
End Class
Code For Thread Module (clsThread.vb):
Public Class clsThread
Private m_Counter As Integer = 0
Private m_MainWindow As Form
Dim m_ThreadCrorssData As objThreadCrossData
Private Delegate Sub NotifyMainWindow(ByVal objCrosData As objThreadCrossData)
'We need an object of this deletegate
Private m_NotifyMainWindow As NotifyMainWindow
Public Sub New(ByVal objCrossData As Object, ByVal strXML As String, ByRef MainWindow As Form1)
m_MainWindow = MainWindow
m_ThreadCrorssData = CType(objCrossData, objThreadCrossData)
'We need to point our delegate to the Method, which we want to call from this thread
m_NotifyMainWindow = AddressOf MainWindow.ReceiveThreadMessage
End Sub
Public Sub StartThread()
While m_Counter < 100
m_Counter = m_Counter + 1
rem for example here i like to check for the value that whether user asked to stop
m_ThreadCrorssData.strProgress = CStr(m_Counter)
m_MainWindow.Invoke(m_NotifyMainWindow, m_ThreadCrorssData)
rem for example also here i like to check for the value that whether user asked to stop
'wait for some time before continuing loop
System.Threading.Thread.Sleep(1000)
End While
End Sub
End Class
Here is the Code For Module (Thread Object Data)
Module mdlCMM
Public Class objThreadCrossData
Property strProgress As String
Property bolStop As Boolean = False
Property strTime As String
Property ID As Integer
Property intStatusNum As Integer
Property strStatusValue As String
End Class
End Module

Multithreading A Function in VB.Net

I am trying to multi thread my application so as it is visible while it is executing the process, this is what I have so far:
Private Sub SendPOST(ByVal URL As String)
Try
Dim DataBytes As Byte() = Encoding.ASCII.GetBytes("")
Dim Request As HttpWebRequest = TryCast(WebRequest.Create(URL.Trim & "/webdav/"), HttpWebRequest)
Request.Method = "POST"
Request.ContentType = "application/x-www-form-urlencoded"
Request.ContentLength = DataBytes.Length
Request.Timeout = 1000
Request.ReadWriteTimeout = 1000
Dim PostData As Stream = Request.GetRequestStream()
PostData.Write(DataBytes, 0, DataBytes.Length)
Dim Response As WebResponse = Request.GetResponse()
Dim ResponseStream As Stream = Response.GetResponseStream()
Dim StreamReader As New IO.StreamReader(ResponseStream)
Dim Text As String = StreamReader.ReadToEnd()
PostData.Close()
Catch ex As Exception
If ex.ToString.Contains("401") Then
TextBox2.Text = TextBox2.Text & URL & "/webdav/" & vbNewLine
End If
End Try
End Sub
Public Sub G0()
Dim siteSplit() As String = TextBox1.Text.Split(vbNewLine)
For i = 0 To siteSplit.Count - 1
Try
If siteSplit(i).Contains("http://") Then
SendPOST(siteSplit(i).Trim)
Else
SendPOST("http://" & siteSplit(i).Trim)
End If
Catch ex As Exception
End Try
Next
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim t As Thread
t = New Thread(AddressOf Me.G0)
t.Start()
End Sub
However, the 'G0' sub code is not being executed at all, and I need to multi thread the 'SendPOST' as that is what slows the application.
Catch ex As Exception
End Try
A very effective way to stop .NET from telling you what you did wrong. Not knowing why it doesn't work is however the inevitable outcome.
Delete that.
Public Class Form1
'This just shows some concepts of threading.
'it isn't intended to do anything
'requires a Button, and two Labels
'
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles Button1.Click
'starts / stops a test thread
'isRun = 0 no thread running, start one
'isRun = 1 thread running, stop it
If Threading.Interlocked.Read(isRun) = 0L Then
'start thread
Threading.Interlocked.Increment(isRun)
t = New Threading.Thread(AddressOf showTime)
'simple threading app - display time about twice per second
t.IsBackground = True 'from a background thread
t.Start()
Else
'stop thread
Threading.Interlocked.Exchange(isRun, 0L)
t.Join() 'wait for thread to end
Threading.Monitor.Enter(listLock)
intervalList.Clear() 'clear the list
Threading.Monitor.Exit(listLock)
Label1.Text = "Stop"
Label2.Text = ""
End If
End Sub
Dim t As Threading.Thread
Dim intervalList As New List(Of Double)
Dim listLock As New Object
Dim isRun As Long = 0L
Private Sub showTime()
Dim dlgt As New UpdLblDel(AddressOf UpdateLabel) 'delegate for UI access
Dim lastDateTime As DateTime = Nothing
Do
Dim d As DateTime = DateTime.Now
If lastDateTime <> Nothing Then
'record difference of times - check sleep interval
Threading.Monitor.Enter(listLock)
intervalList.Add((d - lastDateTime).TotalMilliseconds)
Threading.Monitor.Exit(listLock)
End If
lastDateTime = DateTime.Now
dlgt.BeginInvoke(d, Nothing, Nothing) 'update the UI - note immediate return
Threading.Thread.Sleep(500) 'sleep for approx. 500 ms.
Loop While Threading.Interlocked.Read(isRun) = 1L
End Sub
Delegate Sub UpdLblDel(ByVal theTime As Object)
Private Sub UpdateLabel(ByVal theTime As Object)
If Threading.Interlocked.Read(isRun) = 1L Then
If Label1.InvokeRequired Then 'prevent cross-thread errors
Label1.BeginInvoke(New UpdLblDel(AddressOf UpdateLabel), theTime)
Exit Sub
Else
Label1.Text = CType(theTime, DateTime).ToString("HH:mm:ss.f") 'show the time from the background thread
End If
If Threading.Interlocked.Read(intervalList.Count) >= 10L Then
'take average
Threading.Monitor.Enter(listLock)
Dim avg As Double = intervalList.Sum / intervalList.Count 'sum all of the intervals / count
intervalList.Clear() 'clear the list
intervalList.Add(avg) 'forward the average
Label2.Text = avg.ToString("n2") 'show average
Threading.Monitor.Exit(listLock)
End If
End If
End Sub
End Class
You have to wrap the method that accesses the UI component in a delegate (it doesn't have to be a named delegate; it can be anonymous or an Action or Func), and then pass that to Me.Invoke, as others have alluded to.
In this example, I'm wrapping the split functionality in a lambda, and assigning that lambda to a variable of type Func(Of String()). I then pass that variable to Me.Invoke.
Public Sub G0()
Dim siteSplitFunc As Func(Of String()) = Function() _
TextBox1.Text.Split(vbNewLine.ToCharArray())
Dim siteSplit As String() = CType(Me.Invoke(siteSplitFunc), String())
For i = 0 To siteSplit.Count - 1
Try
If siteSplit(i).Contains("http://") Then
SendPOST(siteSplit(i).Trim)
Else
SendPOST("http://" & siteSplit(i).Trim)
End If
Catch ex As Exception
'Do something useful
End Try
Next
End Sub
You cannot access UI object directly from a thread.
When you want to read/write a textbox, you have to do it in the UI thread. This can be done by using Invoke. Or better yet, send/receive the information with parameters.
Here's a Delegate and a matching method. You call the method to update the textbox and it figures out if it should proxy the method for you by basically asking form if its on the same thread:
Private Delegate Sub UpdateTextBoxDelegate(ByVal text As String)
Private Sub UpdateTextBox(ByVal text As String)
If Me.InvokeRequired Then
Me.Invoke(New UpdateTextBoxDelegate(AddressOf UpdateTextBox), text)
Else
TextBox2.Text &= text
End If
End Sub
To use it, just change your catch statement to:
If ex.ToString.Contains("401") Then
UpdateTextBox(URL & "/webdav/" & vbNewLine)
End If