Throwing exception from sub to async call - vb.net

This is a windows forms application in which I have a particular form. On this form I display the progress of some processing that is supposed to happen in the background asynchronously. All of it works great, except for when I try to handle exceptions that are caught within the background processing....
This is the sub in my form's code that calls the Async function, which is in a module containing all the background processing code:
Public Async Sub BasicProcessing()
Try
Dim processTarget As Action(Of Integer)
processTarget = AddressOf UpdatePulseProcessing
myProgress = New Progress(Of Integer)(processTarget)
myCount.Vehicles = Await ProcessmyCountFile(myCount, myProgress)
If OperationCanceledByUser = True Then
Exit Sub
End If
Catch ex As Exception
MessageBox.Show(Me, "Unable to update count." _
& Environment.NewLine & ex.Message, _
"Error updating count", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End Try
End Sub
This is the async function that it calls, which is in a separate module:
Public Function ProcessmyCountFile(CountToProcess As Count, ByVal ProgressObject As IProgress(Of Integer)) As Task(Of List(Of Vehicle))
myProgressObject = ProgressObject
basicToken = New CancellationTokenSource
Try
Return CType(Task(Of List(Of Vehicle)).Run(Function()
If basicToken.IsCancellationRequested Then
Return Nothing
Exit Function
End If
myCountFile = CountToProcess
MyVehicles = New List(Of Vehicle)
'All that is important in here to note is a call to a regular sub within this module
CreateVehicles()
Return MyVehicles
End Function, basicToken.Token), Global.System.Threading.Tasks.Task(Of List(Of Global.STARneXt.Vehicle)))
Catch ex As Exception
Throw New Exception(ex.Message)
Return Nothing
End Try
End Function
Public Sub StopProcess()
If Not basicToken Is Nothing Then
basicToken.Cancel() ' We tell our token to cancel
End If
End Sub
This is the regular sub called by the Async function:
Private Sub CreateVehicles()
Try
'In here are calls to other regular subs within the same module, let's just call them A and B
Catch ex As Exception
StopProcess()
Throw New Exception("Error creating vehicles at pulse " & pulsePointer & ". " & ex.Message)
End Try
End Sub
When I run this code with data that I know ends up generating an error in sub B, the error does propagate up, as far as up to the method that is directly called by the async function....
So when running in VS, it will stop at "Throw New Exception("Error creating vehicles at pulse " & pulsePointer & ". " & ex.Message)", with the Message containing the message thrown by sub B.
This is what the debugger says on that line:
An exception of type 'System.Exception' occurred in MyProject.exe but
was not handled in user code. Additional information: Error creating
vehicles at pulse....[error message propagated up as thrown by called
subs]. Arithmetic operation resulted in an overflow.
Then strangely enough, within the debugger if I hit "Step Into", it does then return to the sub in my form that called the Async function, which shows the message box on the GUI.
So how do I get this to automatically return back up to the original form code to show the message box? Why does it stop where it stops without continuing to propagate?
FYI, I did find this question, but it ultimately did not help.

#StephenCleary was right - I created a new install for my project as it is, and in the installed version I do get the message box with the expected error message.
Strange behavior on the part of the debugger, and a bit discouraging, but none-the-less I am glad that my code as laid out in my question does actually work.

Related

vb.net - error handling on main method or every method

I have a program that is interacting with emails. I am upgrading it from vb6 to vb.net. There was extensive error handling in the program before ,using On Error commands, to ensure that it never broke, just ignored and logged the errors. In most functions there is this On Error code, where it handles an error in the function by returning a default value and exiting the function. For Example:
Public Function Init() As Boolean
On Error GoTo Err_Init
Init = True
Exit_Init:
Exit Function
Err_Init:
Init = False
Resume Exit_Init
End Function
I want to change all error handling to Try - Catch blocks. My initial thought when I was upgrading the vb6 code was to replace all error handling with a simple Try - Catch around the Sub Main entry point, as below:
Public Sub Main()
Try
Init()
catch
'do stuff
end try
End Sub
public function Init() As boolean
init = true
end function
as any errors in the program would be caught in this way and I could handle them all in one Try - Catch.
However I then realised that, that would not make the function above return a value of False when an error occurred. If I still want this functionality do I have to wrap everything in Try blocks?
Since your concern is that you do not want the application to crash or break, Yes you can use Try cache block for that concern.
However, If you are talking about error handling in the whole application, then I would suggest that you think of a way to refactor the whole code.
Remove all the "On Error" statements and apply some new logic to handle the errors.
That logic could vary (Only logging, Retrying strategy Design pattern, etc) Think of all the expected errors and handle them accordingly.
If you need to do something special for errors in a particular function, then the function should have its own Try/Catch block to implement that behavior. For example:
Public Function Init() As Boolean
Try
'Presumably some other code goes here
Return True
Catch ex As Exception
Return False
End Try
End Function
Get rid of all the on Error steps and do the error handling globally
In the main_load put the three block below
' Get your application's application domain for error handling.
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
' Define a handler for unhandled exceptions.
AddHandler currentDomain.UnhandledException, AddressOf MYExnHandler
' Define a handler for unhandled exceptions for threads behind forms.
AddHandler Application.ThreadException, AddressOf MYThreadHandler
and then add the two sub routines shown below
Private Sub MYExnHandler(ByVal sender As Object,
ByVal e As UnhandledExceptionEventArgs)
Dim EX As Exception
EX = e.ExceptionObject
If EX.StackTrace.Contains("SOMETHING") Then
My.Computer.FileSystem.WriteAllText(LogToWrite, "Error SOMETHING caught:" & EX.StackTrace & vbCrLf, True, System.Text.Encoding.Default)
MsgBox("error caught. Contact **XXX** for assistance!" & vbCrLf & vbCrLf & "Error: " & vbCrLf & EX.StackTrace,, "Fatal Error")
End If
End Sub
Private Sub MYThreadHandler(ByVal sender As Object,
ByVal e As Threading.ThreadExceptionEventArgs)
If e.Exception.StackTrace.Contains("SOMETHING") Then
My.Computer.FileSystem.WriteAllText(LogToWrite, "Error SOETHING caught:" & e.Exception.StackTrace & vbCrLf, True, System.Text.Encoding.Default)
MsgBox("Error caught. Contact **xxx** for assistance!" & vbCrLf & vbCrLf & "Error: " & vbCrLf & e.Exception.StackTrace,, "Fatal Error")
End If
End Sub
You can just check the content of the errors and respond accordingly to every single error with a Case Array..

server busy error message vb.net

I get this error. I can not catch it where it comes from. All methods made with try catch but still I get this error from time to time. I used code:
Private Sub add2ComboBox_called(ByVal text As String)
Try
If Me.InvokeRequired Then
Dim args() As String = {text}
Me.Invoke(New Action(Of String)(AddressOf add2ComboBox_called), args)
Return
End If
ComboBox_called.Items.Add(text)
Catch ex As Exception
log_it(ex.StackTrace)
End Try
End Sub
and also this one:
Public Sub clear_do_not_open()
Dim FILENAME As String = path & "\DO_NOT.OPEN"
Dim index As Integer
Try
Using fs As New IO.FileStream(FILENAME, IO.FileMode.Truncate, IO.FileAccess.Write, IO.FileShare.ReadWrite), _
tl As New TextWriterTraceListener(fs)
index = Trace.Listeners.Add(tl)
Trace.Write("")
Trace.Listeners(index).Flush()
Trace.Flush()
End Using
Trace.Listeners.RemoveAt(index)
Catch ex As Exception
log_it(ex.StackTrace)
End Try
End Sub
How do I make this to press Retry by default if it appears? Wait for a second and press Retry again? If not like this then how do I know that this message pops up? If catch the moment it shows up I could then just restart the program or send an email to myselt so I could come to PC where program runs and I could handle it. Now message pops up and program stops while its on.

Unhandled Exception error line and source function

I am using VS2012 VB.net.
Can I please have some help in creating some code to calculate the error line of an exception and also the function that the exception occurred in.
Here is my current code:
Partial Friend Class MyApplication
Public exceptionListOfExceptionsToNotPauseOn As New List(Of ApplicationServices.UnhandledExceptionEventArgs)
Private Sub MyApplication_UnhandledException(sender As Object, e As ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException
Dim msgboxResult As MsgBoxResult
Dim booleanExceptionFoundInList As Boolean = False
'Dim trace As System.Diagnostics.StackTrace = New System.Diagnostics.StackTrace(ex, True)
'Dim exceptionLineNumber = trace.GetFrame(0).GetFileLineNumber()
For x = 0 To exceptionListOfExceptionsToNotPauseOn.Count - 1
If exceptionListOfExceptionsToNotPauseOn(x).Exception.Message = e.Exception.Message Then
booleanExceptionFoundInList = True
End If
Next
If Not booleanExceptionFoundInList Then
msgboxResult = MessageBox.Show("An exception error has occured." & vbCrLf & "Error message: " & e.Exception.Message & vbCrLf & "Do you wish to pause on this exception again?", "Exception", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question)
If msgboxResult = Microsoft.VisualBasic.MsgBoxResult.No Then
exceptionListOfExceptionsToNotPauseOn.Add(e)
End If
End If
e.ExitApplication = False
End Sub
End Class
UPDATE
The code for the trace code uses an Exception data type where as the code for handling unHandled exceptions above has a parameter of "e As ApplicationServices.UnhandledExceptionEventArgs". Can I use the trace code with this data type? Do I need to cast it to an exception type? Or is it not possible?
I am not mastering in Vb.Net but previously i used below code, may be it can help you
[ex.StackTrace()]
Try
'Your Code goes here
Catch ex As Exception
MsgBox(ex.StackTrace())
End Try
Here are a couple of tips. First is to use PostSharp its a AOP kit that will let you trace all methods entry and exit using Attributes. This would direct you to the function straight away.
Another trick. Subscribing to the ThreadExceptionEventHandler actually does cause the debugger to break on unhandled exceptions! Hence temporarily comment out your MyApplication_UnhandledException and add a ThreadExceptionEventHandler
<STAThread> _
Public Shared Sub Main(args As String())
Try
'your program entry point
Application.ThreadException += New ThreadExceptionEventHandler(Application_ThreadException)
'manage also these exceptions
Catch ex As Exception
End Try
End Sub
Private Sub Application_ThreadException(sender As Object, e As ThreadExceptionEventArgs)
ProcessException(e.Exception)
End Sub
Another trick is is not to run under the debugger. The debugger is masking the exception for some reason. If you run your app normally (Ctrl+F5), you'll get the usual Unhandled exception has occurred in your application... Continue/Quit? dialog.
The code for handling unHandled exceptions above has a parameter of "e
As ApplicationServices.UnhandledExceptionEventArgs". Can I use the
trace code with this data type?
No.
You cannot easily use your trace code datatype with the UnhandledExceptionEventArgs. One idea might be to make a class that inherits from UnhandledExceptionEventArgs but I don't know how you'd call the MyApplication_UnhandledException function with the special type, because that function is invoked when an Exception is Unhandled.

Is it correct to raise custom events from Timer/ BackgroundWorker Completed that go on and update UI

My winforms application needs to interact with hardware devices.
The display is kind of workflow... after step 1 .. completed.. go to step 2 .. etc.
The display is dynamic with controls being made visible at run time and activity being initiated (each of these User controls use timer/ BGWorker within).
I m Raising custom events from timer/ BGWorker_Completed. this will help proceed to the next step and update UI. Am I doing the right thing?
Im a newbie in winforms and Im just not able to figure out why the display is failing.
Im not catching any exception... but after a particular step I do not see the controls !!! I do not know where and how to debug this scenario. If I execute the main form as a stand alone.. I can see the display as well.
however if I navigate from the login page/ change the tabs within the main form and return back.. I do not see the display.
I tried putting in checks before calling the update on UI and in the same order as below.
Thread.Current.IsBackground returns false, control.IsHandleCreated returns true or else Im creating it with dim x=Control.handle()) Me.InvokeRequired/ Control.invokeRequired returns false (the way I wanted). However I do not see userControl being displayed... visibility/color/ everything is being set (in the program).. and Im able to see the hardware interaction!!!.. But no display :-( (step 4 and later)
Im not doing anything fancy in the login page...or in tabChanged events.
(On tab changed event... Im only cleaning up open connections/ closing bg workers if any.. which would be connected back whenever required)
Please let me know if I need to do anything...and how to solve this problem.
I m also calling EnsureHandleIsCreated(control) subroutine soon after initialize component of every user control/ main form.
'Code in Login Form
Dim myForm as new MainForm()
myForm.ShowDialog(Me) ' here i also tried with show/showDialog.. with/without ownerForm
Me.Hide() ' Hide login page
'Code for checking if handle is created or not
Public Sub CheckForInvalidThread()
frmMain.CheckForIllegalCrossThreadCalls() = True
If Thread.CurrentThread.IsBackground Or Not Thread.CurrentThread.Name Is THREAD_MAIN_NAME Then
Throw New InvalidOperationException(THREAD_IS_INVALID)
End If
If Not Me.IsHandleCreated Then
Dim x = Me.Handle()
Thread.Sleep(20)
End If
End Sub
Public Sub EnsureHandleIsCreated(ByRef c As Control)
Try
If Not c.IsHandleCreated Then
Dim h As System.IntPtr = c.Handle()
End If
If c.HasChildren Then
For Each child As Control In c.Controls
Try
EnsureHandleIsCreated(child)
Catch ex As Exception
DAL.LogException(ex.Message, ex.StackTrace, "EnsureHandleIsCreated: " & c.Name, 0)
End Try
Next
End If
Catch ex As Exception
DAL.LogException(ex.Message, ex.StackTrace, "EnsureHandleIsCreated: " & c.Name, 0)
End Try
End Sub
Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
lbRole.Text = RoleName
lbName.Text = UserName
Try
Me.Activate()
If Thread.CurrentThread.IsBackground Then
Throw New ApplicationException(THREAD_IS_INVALID)
End If
Thread.CurrentThread.Name = THREAD_MAIN_NAME
CheckForInvalidThread()
clGlobals.frmMain = Me
If RoleName Is Nothing Or String.IsNullOrEmpty(RoleName) Or RoleName.Equals("OPERATOR", StringComparison.InvariantCultureIgnoreCase) Then
tcEtch.TabPages("tbMaintenance").Hide()
tcEtch.TabPages("tbAdmin").Hide()
End If
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
Finally
End Try
Initiate()
End Sub
Public Sub GoToNextStep() Handles Me.GoToNextStepEvent
Try
CheckForInvalidThread()
CurrentStep = CurrentStep + 1
Select Case CurrentStep
Case 0 To 2
If Me.InvokeRequired Then
_delegateDisplayInitiate = AddressOf DisplayStep2
Me.Invoke(_delegateDisplayInitiate)
Else
DisplayStep2()
End If
Case 3
If ucCycleStart.InvokeRequired Then
_delegateDisplayInitiate = AddressOf DisplayStep3
ucCycleStart.Invoke(_delegateDisplayInitiate)
Else
DisplayStep3()
End If
Case 4
If Me.InvokeRequired Or ucPartCountVerification.InvokeRequired Or Thread.CurrentThread.IsBackground Then
Throw New Exception("Check out")
End If
EnsureHandleIsCreated(ucPartCountVerification)
If ucPartCountVerification.InvokeRequired Then
_delegateDisplayInitiate = AddressOf DisplayStep4
ucPartCountVerification.Invoke(_delegateDisplayInitiate)
Else
DisplayStep4()
End If
End Select
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Private Sub DisplayStep4() Handles Me.DisplayStep4Event
ucPartCountVerification.Visible = True
ucPartCountVerification.Show()
ucPartCountVerification.Initiate()
End Sub
Public Sub Initiate()
frmMain.CheckForInvalidThread()
'Just to verify if things are fine.. i put in this check below
If Me.InvokeRequired Or pnStep4.InvokeRequired Or Not (Me.IsHandleCreated And pnStep4.IsHandleCreated ) Then
MessageBox.Show("cHECK OUT")
Else
Me.Visible = True
pnStep4.Visible = True
Me.BackColor = Color.Red
pnStep4.BackColor = Color.Gray
Dim height = Me.Size.Height
Dim width = Me.Size.Width
MessageBox.Show(height.ToString() + Me.InvokeRequired.ToString())
End If
end Sub
You certainly can raise events from a BackgroundWorker or Timer. You're already checking InvokeRequired, which is the correct thing to do. You need to then call BeginInvoke (or Invoke, if it needs to be synchronous) to update your UI.
You are on the right track. When dealing with the events that come from some libraries, you cannot be certain on which thread they are delivered. That is disappointing because Windows Forms has the restriction that call calls be made from the GUI thread. This MSDN article explains the problem.
You are on the right track with InvokeRequired. It lets you see whether you are on the right thread, but you need a way to handle this case and re-invkoe the event on the proper thread.
Here is how I do this in C#...
public delegate void uiEventHandler();
void uiEvent(object sender, EventArgs e)
{
if (InvokeRequired)
{
// We are not on the correct thread. We'd better get there.
var eh = new uiEventHandler(uiEvent);
Invoke(eh, new object[] { sender, e });
return; //This thread has no more work to do.
}
// Do your work here that requires being performed on the GUI thread.
}

vb.net timer and exception (try... catch) and memory leak

Scenario:
I've hundreds of applications instances running on client machines doing a certain job. Something like a cloud app.
Objective: When an error occur in any of them i want to report the error to an error-log database an quit silently the app to avoid user annoyance.
The problem:
I've migrate to VB.NET recently and don't know yet wich is the best aproach to error handle the code. Those instances are runing a routine under a timer.
Private Sub timerLifeSignal_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timerLifeSignal.Tick
MAINSUB()
End Sub
Friend Sub MAINSUB()
frmSAN.timerLifeSignal.Enabled = False
...
'do the job
...
frmSAN.timerLifeSignal.Enabled = True
end sub
At first glance i've put try/catch into every single function but it leads to a memory leak since, AFIK, the exception object created was not disposed correctly.
So is there a way to make try/catch do not memory leak under these circumstances?
Thx,
UPDATE:
Basically what i was doing is something like:
Private Sub timerLifeSignal_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timerLifeSignal.Tick
MAINSUB()
End Sub
Friend Sub MAINSUB()
Try
frmSAN.timerLifeSignal.Enabled = False
...
'do the job
...
frmSAN.timerLifeSignal.Enabled = True
Catch ex as Exception : gERRFUNC(" | MAIN | " & ex.Message) : End Try
end sub
friend sub dothejob
try
...
' really do the job
...
Catch ex as Exception : gERRFUNC(" | MAIN | " & ex.Message) : End Try
end sub
and so on... and finally (may here it's my mistake) another try/catch nested into here:
Public Sub gERRFUNC(Optional ByVal errorMSG As String = "")
Try
' log message on database
SQL = "INSERT INTO sanerrorlog VALUES (NULL, '" & currentMySQLTime() & "', '" & errorMSG & "');"
' function that open conn and execute the sql... working fine
' NOTE THAT INSIDE THE DORS FUNCTION THERE'S ALSO A TRY/CATCH USING THE SAME EX OBJECT.
DORS(SQL)
' clean up things
SQL = "DELETE FROM sannie WHERE sid=" & gMyID
DORS(SQL)
For i = 0 To UBound(gCONN)
If gCONN(i).State = ConnectionState.Open Then gCONN(i).Close()
Next
frmSAN.nfi.Visible = False
gWWB = Nothing
End
Catch E As Exception: End: End Try
End Sub
So... if i do this:
Private Sub timerLifeSignal_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timerLifeSignal.Tick
Try
MAINSUB()
Catch ex as Exception : gERRFUNC(" | MAIN | " & ex.Message) : End Try
End Sub
Means all exceptions inside mainsub should be catched?
You have another way..
You can attach events to the app domain and main thread, in case it fails, to catch the errors.
Something like:
AppDomain.CurrentDomain.UnhandledException +=
CurrentDomain_UnhandledException;
Application.ThreadException +=
Application_ThreadException;
Application.ApplicationExit += Application_ApplicationExit;
with this in mind, every time an exception occurs, anywhere that it hasn't a catch by itself, will fall trough any of this ones...
Try/Catches by themselves don't cause memory leaks. Not finalizing something after a failure, which triggers a catch can however. By removing your try/catches, you've apparently exposed something else that does finalize, even though informally, the object which was causing a memory leak.
Ask yourself this, how could a directive in your code cause a memory leak? Try, nor Catch are references to an object, and thus could not cause memory consumption issue by themselves -- only by the logical path of the code they control.
Just for reference the memory leak problem occurs because of nested try/catch loops wich (by my mistake) uses the same variable as exception object.
To be more clear take the following example:
Public Sub gERRFUNC(Optional ByVal errorMSG As String = "")
Try
// do something wrong here
Catch E As Exception: gERRFUNC(ex.Message): End Try
End Sub
friend Sub gERRFUNC(msg as string)
Try
// call another external sub (wich eventually may also throw an execption)
// take note that the var 'E' is also referenced here and everywhere
// so as 'E' is set it enters on a Exception loop causing the 'memory leak' problem.
Catch E as Exception: End: End Try
End Sub
By removing the nested try/catch or by using a well structured error 'flow' may avoid these type of problem.
Best Regards,
Paulo Bueno.