VB.NET 1.1 Safely abort thread - vb.net

I'm trying to safely kill a thread. In my button click I have:
try
dim bar as new foo()
dim mythread as New System.Threading.Thread(AddressOf bar.Start)
mythread.Start()
sleep(1000)
mythread.abort()
catch ex as Exception
msgbox ex.Message
end try
In my Class I have:
class foo
public function Start()
Try
do some stuff...
Catch tae As ThreadAbortException
Thread.ResetAbort()
Catch ex As Exception
LogData("[ ERROR ] ", "[ Start ]" & ex.Message & " line: " & Erl())
End Try
end sub
end class
When it goes to abort the thread I still get a thread abort error. What am I doing wrong?

It's really impossible to "safely" abort a thread. You should, instead, focus on having a mechanism where you can notify the thread that it should exit, and allow it to cooperatively exit (via returning from its main entry point method).

Related

Display progress bar based on download called by another class

I have a windows form which has a download button. The download button creates another class' object and this object triggers the download. I have created a progress bar in the main windows form and want this progress bar to update as the file gets downloaded.
The main windows form calls the object as below :
Dim dwObj= Downloader()
dwObj.DownloadFileFromLocation(fileName, fileType)
The DownloadFileFromLocation method is defined as below in the Downloader class :
Public Function DownloadFileFromLocation(FileName As String, FileType As String))
Dim Address As String = "MyLINK"
If (Not System.IO.Directory.Exists(MyLocalPath)) Then
System.IO.Directory.CreateDirectory(MyLocalPath)
End If
Try
Dim fileReader As New WebClient()
If (FileType.Equals(MyFileType)) Then
If Not (System.IO.File.Exists(MyLocalPath + FileName)) Then
AddHandler fileReader.DownloadProgressChanged, AddressOf Download_ProgressChanged
fileReader.DownloadFile(Address, MyLocalPath + FileName)
End If
End If
If (FileType.Equals(AWSGlobals.DWC)) Then
If Not (System.IO.File.Exists(AWSGlobals.DWPath + FileName)) Then
fileReader.DownloadFile(Address, AWSGlobals.DWPath + FileName)
End If
End If
Catch ex As HttpListenerException
Console.WriteLine("Error accessing " + Address + " - " + ex.Message)
Catch ex As Exception
Console.WriteLine("Error accessing " + Address + " - " + ex.Message)
End Try
End Function
Now, I have defined a function to update the progress bar, but since its in another file, I get an error :
Private Sub Download_ProgressChanged(sender As Object, e As DownloadProgressChangedEventArgs)
Try
ProgressBar1.Value = CInt(Math.Round((e.BytesReceived / e.TotalBytesToReceive) * 100, 0, MidpointRounding.AwayFromZero))
Catch ex As Exception
Debug.Print(ex.ToString)
End Try
End Sub
Now I get the error, which I understand because Progress bar is declared in the main windows form
ProgressBar1 is not declared. It may be inaccessible due to its protection level
The question is how can I handle this now. Can someone please help me out here?

How to Determine Exception Subtype

I'm wondering if there's a standard way to determine the sub-type of an exception. For example, for the File.Copy() method, IOException indicates that the destination file exists OR a general I/O error occurred. There are other such cases. In my exception handler, how can I determine which it is? I'm checking the end of ex.Message for the string already exists., which works, but seems awfully kludgy and unreliable.
While it is possible to check File.Exists() on the destination file, confirm overwrite with the user if it exists and then perform File.Copy() this is not atomic, which is to say, between checking and copying, it is possible for the conditions to change, for example if some other process created or copied a file into the destination location.
EDIT:
I had already changed the code based on comments here, but I just rolled it back and will post it here, just to show what I was doing:
Try
File.Copy(SrcFile, DstFile, OverWrite)
Catch ex As DirectoryNotFoundException
MsgBox(ex.Message)
Catch ex As FileNotFoundException
MsgBox("File not found: " & ex.FileName)
Catch ex As UnauthorizedAccessException
MsgBox("You do not have write access to the destination.")
Catch ex As IOException
' IOException represents an existing destination file OR a general IO error.
If SubStr(ex.Message, -15) = "already exists." Then
OverwriteCheck = MsgBox(
"Overwrite " & IO.Path.GetFileName(SrcFile) & " in destination directory?",
MsgBoxStyle.YesNo
)
If OverwriteCheck = DialogResult.Yes Then
Try
File.Copy(SrcFile, DstFile, OverWrite)
Catch iex As Exception
MsgBox("Unable to copy " & SrcFile & ":" & vbNewLine & iex.Message)
End Try
End If
Else
Throw ex
End If
Catch ex As ArgumentException
' The user left a blank line in the text box. Just skip it.
End Try
Here is an option using FileStreams to get more granular information about your exception
Sub Main()
Try
copyTo("C:\t\output3.txt", "C:\t\output1.txt", True)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Sub
Private Sub copyTo(source As String, destination As String, Optional overwrite As Boolean = False)
' raises FileNotFoundException if source doesn't exist
Using fsSource As New FileStream(source, FileMode.Open, FileAccess.Read, FileShare.None)
If Not overwrite AndAlso File.Exists(destination) Then
' Raises exception when destination file exists and not overwrite
Throw New Exception(
String.Format("Destination file '{0}' exists and overwrite is false.", destination))
Else
Using fsDestination As New FileStream(destination, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)
fsSource.CopyTo(fsDestination)
End Using
End If
End Using
End Sub
This is a rudimentary example, but you can see how you can differentiate different exception cases, while having atomicity between checking file existence and copying.
I believe you looking for this pattern:
Try
IO.File.Copy("source", "Dest", True)
Catch exUnAuth As System.UnauthorizedAccessException
Catch exArg As System.ArgumentException
Catch exNotFound As IO.FileNotFoundException
Catch exGeneral As System.Exception
End Try
Place the list of specific exceptions first in the sequence. The last exception tested for should be the least derived.
You should read through the documentation: How to use structured exception handling in Visual Basic .NET or in Visual Basic 2005. Yes this is an old reference, but that is an indication how long this has been part of the language.

Unhandled Exception in Background Worker

Hi there I am carrying out some integration testing in an application I am developing. The specific element that is causing an issue is a call to a background worker which interrogates an Oracle database. When an error is encountered in the query I want the exception detail to percolate up the call stack to the application level and at that point provide an appropriate user compatible message. In the example test there is a syntax error in the underlying SQL which results in an OraEx Exception:
Oracle.DataAccess.Client.OracleException ORA-00907: missing right parenthesis
Unfortunately the code generates the following exception:
System.Reflection.TargetInvocationException was unhandled
Message: An unhandled exception of type
'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
Additional information: Exception has been thrown by the target of an
invocation.
in the DoWork sub of the backgroundworker, despite my belief that I am handling the exception correctly. Its pretty obvious that I am missing something fundamental here, can someone suggest a solution please.
Thanks in Advance
Paul J.
Here is the code that makes the call to the background worker:
Private Sub EventSearch(ByVal mySQL As String)
Const procName As String = "EventSearch"
Try
_eventMngr = New ScadaEventManager(_CurrentDB, _userName, _myPwd)
_eventMngr.SQL = mySQL
'Set the flag and stop query tool status accordingly
_Stopped = False
uxStopQueryTool.Enabled = True
'activate the timer object to ensure that the execute query menu
'and tool remain disabled until all background processing is complete
uxBackWorkTimer.Enabled = True
_logger.SendLog(Me.Name & "." & procName & " - Scanning for data.", NLog.LogLevel.Trace)
ReviseStatus(2, "Scanning for data. Please wait...", Color.Black, True, True)
'Force the thread to sleep for half a second so the user can see the scanning state taking place
Threading.Thread.Sleep(500)
'Launch the background worker to retrieve the required data from the database
uxBWScan.RunWorkerAsync(_eventMngr)
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Exclamation, My.Application.Info.ProductName)
_logger.SendLog(ex.Message & ". Thrown in module " & Me.Name.ToString & "." & procName, NLog.LogLevel.Error, ex)
Call ResetStatus()
Finally
End Try
End Sub
And here is the code executed by the background worker:
Private Sub uxBWScan_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles uxBWScan.DoWork
Const procName As String = "uxBWScan_DoWork"
Try
e.Argument.CountRecords(_queryTypeID)
e.Result = e.Argument.RecordCount
Catch NullEx As NullReferenceException
_logger.SendLog(NullEx.Message & ". Thrown in module " & Me.Name.ToString & "." & procName, NLog.LogLevel.Error, NullEx)
Throw
Catch OraEx As OracleException
_logger.SendLog(OraEx.Message & ". Thrown in module " & Me.Name.ToString & "." & procName, NLog.LogLevel.Error, OraEx)
Throw
Finally
End Try
End Sub
And here is the low level code that generates the error:
Public Sub CountRecords(ByVal queryType As Integer)
_myDataset = New DataSet
Try
_myDataset = _myScadaEventDB.CountRecords(_sqlText)
If _myDataset.Tables(0).Rows.Count > 0 Then
If queryType = Enums.QueryType.General Or queryType = Enums.QueryType.KeyPerformanceIndicators Or queryType = Enums.QueryType.TelecontroAnalysis Then
_recordCount = _myDataset.Tables(0).Rows(0).Item("RecordCount")
Else
'The query is grouped therefore count the number of records in the table
_recordCount = _myDataset.Tables(0).Rows.Count
End If
Else
_recordCount = 0
End If
Catch ex As Exception
Throw
Finally
End Try
End Sub
Ok, problem solved. Removed the try catch block from DoWork and moved my exception handling into 'RunWorkerCompleted' using e.error. Some reading of the documentation (RTFM...), highlighted the fact that using Try/Catch in the worker thread interferes with the native functionality of the BackgroundWorker. Thanks again everyone for your input.
Paul,

Process Start and Errors from Slow Applications

I made an application that our company uses to launch databases and updates them on the users machine, when needed.
I am having a slight problem when it comes to launching databases and the database starts up slow. When this occurs my application throws an exception, as I assuming its awaiting some kind of response back.
As of now the error thrown is: The system cannot find the file specified
I am trying to prevent this exception logging for cases like this(Slow Application), but still allow the logging if a real error occurs while opening a database.
Current Code I am using:
Private Sub OpenApplication()
If File.Exists(LocalPathString) Then ' File Found. Open the File.
Try
Dim ps As New Process
ps = Process.Start(LocalPathString)
Catch ex As Exception
ex.Source += " | " & LocalPathString
RaiseEvent ShowError(ex)
Finally
RaiseEvent CancelIt() ' Thread Complete. Close the ActionForm
End Try
Else
If LocalPathString = vbNullString Then
RaiseEvent CancelIt() ' No file exits. Cancel thread.
Else
RaiseEvent ShowError(New Exception("Database Not Located: " & LocalPathString))
End If
End If
End Sub
StackTrace:
System.Diagnostics.Process.StartWithShellExecuteEx(startInfo As ProcessStartInfo)
App.exe: N 00912
System.Diagnostics.Process.Start()
App.exe: N 00136
System.Diagnostics.Process.Start(startInfo As ProcessStartInfo)
App.exe: N 00049
SAMi.ActionClass.OpenApplication()
App.exe: N 00117
Maybe I'm missing something, but why don't you simply omit the logging if you found that specific exception?
Catch ex As Exception
ex.Source += " | " & LocalPathString
if not ex.Message.Contains("The system cannot find the file specified") Then
RaiseEvent ShowError(ex)
end if

Alternative to nested Else If statements with different outcomes?

Is there any better way of handling nested else if statements that have different outcomes?
Here is an example of one of my nested statements to explain:
If My.Computer.Network.Ping(computerName) = True Then
Call InstallVS(computerName)
If My.Computer.Network.Ping(computerName) = True Then
Call PEC(computerName)
If My.Computer.Network.Ping(computerName) = True Then
Call RemoveSoftware(computerName)
Else
Call WriteLog(computerName & " lost connectivity while attemping to remove the temp software")
End If
Else
Call WriteLog(computerName & " lost connectivity while Forcing Communication")
End If
Else
Call WriteLog(computerName & " lost connectivity while attemping to Install")
End If
I have a requirement to lots of these type of statements, some are smaller, some are a lot larger.
You can create a method called PingOrFail, which will test connectivity or throw an exception otherwise, with a given error message. Then your code flow could look something like this:
Try
PingOrFail(computerName, "attempting to install")
Call InstallVS(computerName)
PingOrFail(computerName, "forcing communications")
Call PEC(computerName)
PingOrFail(computerName, "removing temp software")
RemoveSoftware(computerName)
Catch ex As Exception
Call WriteLog (computerName & " lost connectivity while " & ex.Message)
End Try
And this is the PingOrFail method:
Public Sub PingOrFail(computerName as String, message As String)
If My.Computer.Network.Ping(computerName) = False
Throw New Exception (message)
End If
End Sub
These statements don't need to be nested, they could just raise exceptions if they fail.
Private Sub DoStuff(ByVal computerName As String)
Try
If My.Computer.Network.Ping(computerName) Then
InstallVS(computerName)
Else
Throw New Exception(computerName & " lost connectivity while attemping to Install")
End If
If My.Computer.Network.Ping(computerName) Then
PEC(computerName)
Else
Throw New Exception(computerName & " lost connectivity while Forcing Communication")
End If
If My.Computer.Network.Ping(computerName) Then
RemoveSoftware(computerName)
Else
Throw New Exception(computerName & " lost connectivity while attemping to remove the temp software")
End If
Catch ex As Exception
WriteLog(ex.Message)
End Try
End Sub