Vb.net: How To Trap Exceptions In Invoked Methods?

free web hosting
Free Web Hosting > Computers & Tech > Programming > Programming General > BASIC / Visual Basic (.NET)

Vb.net: How To Trap Exceptions In Invoked Methods?

turbopowerdmaxsteel
I am having some trouble in trapping the exceptions raised by Invoked methods. By Invoked, I mean the methods that have been Invoked by the Control.Invoke method. Given below is a simple example of this problem.

I have a Form named Form1 which contains an object Tim of the Timer class. Unlike the System.Windows.Forms.Timer object, the Timer class (FullName: System.Timers.Timer) raises the Elapsed event on a seperate thread. I think the Timer object does the same thing but synchronizes the event by raising it on the same thread as that of the Form it is in.

When I try to execute the following code, an exception: Cross threaded operation not valid is thrown.

CODE
Public Class Form1
    Dim Tim As New System.Timers.Timer

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Tim.Interval = 1000
        AddHandler Tim.Elapsed, AddressOf Tim_Elapsed
        Tim.Enabled = True
    End Sub

    Sub Tim_Elapsed(ByVal Sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
        Me.Text = "The Clock Just Ticked"   ' This Line Causes the Exception
    End Sub
End Class


This is because the Tim_Elapsed method is being invoked on a seperate thread, the one belonging to the object Tim. So, I used the mechanism stated in MSDN and came up with the following code:-

CODE
Public Class Form1
    Dim Tim As New System.Timers.Timer

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Tim.Interval = 1000
        AddHandler Tim.Elapsed, AddressOf Tim_Elapsed
        Tim.Enabled = True
    End Sub

    Sub Tim_Elapsed(ByVal Sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
        Dim D As New MyDelegate(AddressOf MyMethod)
        Me.Invoke(D)
    End Sub

    Delegate Sub MyDelegate()
    Sub MyMethod()
        Me.Text = "The Clock Just Ticked"
    End Sub
End Class


All things seem to be fine, but when the delegate method raises an exception, say an overflow exception, the exception is traced on the line which invokes this method rather than the line which raises the exception - I = I / 0.

CODE
Sub MyMethod()
        Dim I As Integer = 5
        I = I / 0  ' The culprit Line
        Me.Text = "The Clock Just Ticked"
End Sub




This problem raises havoc when trying to debug larger implementation of this concept. Any ideas on how to solve this problem?

 

 

 


Reply

tansqrx
You have come across one of my biggest headaches in .NET, cross threading exceptions. I usually use a little more sophisticated logic to determine if an invoke is required or not. The following code either enables or disables a control.

CODE
#Region "Threaded Callbacks"
    Delegate Sub txtEnableDelegate(ByVal bEnabled As Boolean, ByVal ctl As Control)
    Dim dtxtEnable As New txtEnableDelegate(AddressOf txtEnable)

    Private Sub txtEnable(ByVal bEnabled As Boolean, ByVal ctl As Control)
        If ctl.InvokeRequired Then
            Dim d As New txtEnableDelegate(AddressOf txtEnable)
            Me.Invoke(d, New Object() {bEnabled, ctl})
        Else
            ctl.Enabled = bEnabled
        End If
    End Sub
#End Region


and it is called by

CODE
dtxtEnable(True, udCaptureRate)


Another trick that I use is threading.timer. I find that it is more flexible when you are starting a new thread and want a timer built-in. If this does not help then you may want to consider making your own exception class specific that your thread. You can throw a general or your own exception when it is needed and to my understanding exceptions will automatically be invoked.

A second approach would to make your object to be threaded a control itself. That way you can call the control.invoke(delegate) from the thread and not have all the logic needed to invoke the individual controls. The trick is to pass the delegate as a property to the thread when it is created. There is a good example of this in YCC Bot Maker http://ycoderscookbook.com/YCC_Bot_Maker.htm

I hop this help and let me know if it still doesn’t do the trick. I am interested in your results myself.

 

 

 


Reply

tansqrx
This problem got me to thinking more. I wrote a test program that checked to see if exceptions are automatically invoked onto the correct thread and they are. As long as the code that may cause the exception is in a try catch statement you should not have any problems. The code below demonstrates this. The main form only has a textbox named txtOutput which is multiline and has scrollbars turned on. You will notice that the textbox does not have any invoke statements yet still handles the text coming out of the exception even though it is raised on another thread. This is because an exception will automatically jump to the calling thread until it reaches the lowest level thread. In this case it is the main GUI thread.

CODE
Option Strict On
Option Explicit On

Public Class frmMain

    Private Sub frmMain_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim Tim As New System.Windows.Forms.Timer
        Tim.Interval = 1000
        AddHandler Tim.Tick, AddressOf Tim_Elapsed
        Tim.Enabled = True
    End Sub

    Sub Tim_Elapsed(ByVal Sender As Object, ByVal e As System.EventArgs)
        Try
            Dim I As Integer = 5
            I = CInt(I / 0)  ' The culprit Line
            Me.txtOutput.Text += Date.Now.ToString + "The Clock Just Ticked" + vbCrLf
        Catch ex As Exception
            Me.txtOutput.Text += ex.ToString + vbCrLf
        End Try

    End Sub
End Class

Reply

turbopowerdmaxsteel
I used to utilize the Invoke Required flow block as well. But it is kind of useless, because in my case, everytime invoke is required. You have utilized the System.Windows.Forms.Timer component in your code. The elapsed event is automatically raised on the same thread as that of the form it is in. So, the exceptions in it are properly traced. If you remove the line I = Cint(I / 0) from the block, Me.txtOutput.text + = .... will not throw an exception. While, if the System.Timers.Timer class was used, it would throw the Cross threaded operation not valid exception.

My problem is not specific to the Timer control itself. I used the Timer class for simplicity. Actually, I am using asynchronous operations on a Socket class and wrapping it up on a custom Socket component which behaves similar to the old VB6 Socket.

Reply

tansqrx
Sorry about that, I was being a bonehead and rushed out the answer right before I left work. I of all people should have caught that one.

My traditional approach is to just put invokes on every single control that interacts with a threaded return. It can become time consuming and nasty but I just put them in a separate region and forget about them. In my experience there are actually only a few controls that will need invokes. The current program that I am working on has the same problem but the only controls affected are buttons that need to change their enabled status and a list view. I wrote a single delegate to take care of the controls (from the previous example) and two for the list view as there were only two operations involved.

I understand that there may be several threads flying around in your main class but it really doesn’t matter. The only time that you need to worry is with windows controls.

Below is the second alternative example that I mentioned before. You can create a separate class and have it inherit control. Since you are making your own socket class I assume this would be the best anyway as you can just drop it on your form. As you can see I have created two delegates on frmMain which are passed into the control as a property. Once you want output from the control, the invoke method is called with the proper delegate. The delegates are put back on the main thread for you.

You can also grab the full example project from http://www.ycoderscookbook.com/Files/ThreadException.rar. Once again let me know if this helps.

The main form

CODE
Option Strict On
Option Explicit On

Public Class frmMain
    Private _work As New WorkComponent

#Region "Threaded Callbacks"
    Delegate Sub WorkComponent_ReturnDelegate(ByVal strMessage As String)
    Delegate Sub WorkComponent_ExceptionDelegate(ByVal e As Exception)
    Dim WorkComponent_ReturnDeleg As New WorkComponent_ReturnDelegate(AddressOf WorkComponent_Return)
    Dim WorkComponent_ExceptionDeleg As New WorkComponent_ExceptionDelegate(AddressOf WorkComponent_Exception)
#End Region

    Private Sub frmMain_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        'setup work
        'You have to create the handle first
        Dim iCMDHandle As IntPtr = _work.Handle
        _work.dReturn = WorkComponent_ReturnDeleg
        _work.dException = WorkComponent_ExceptionDeleg

        Dim Tim As New System.Timers.Timer
        Tim.Interval = 1000
        AddHandler Tim.Elapsed, AddressOf Tim_Elapsed
        Tim.Enabled = True
    End Sub

    Sub Tim_Elapsed(ByVal Sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
        _work.WorkToBeDone()
    End Sub

    Private Sub WorkComponent_Return(ByVal strMessage As String)
        txtOutput.Text += strMessage + vbCrLf
    End Sub

    Private Sub WorkComponent_Exception(ByVal e As Exception)
        txtOutput.Text += e.ToString + vbCrLf
    End Sub
End Class


The control class

CODE
Option Strict On
Option Explicit On

Public Class WorkComponent
    Inherits Control

    Private _dReturn As frmMain.WorkComponent_ReturnDelegate
    Private _dException As frmMain.WorkComponent_ExceptionDelegate

    Private _iCount As Integer

#Region "Properties"
    Public Property dReturn() As frmMain.WorkComponent_ReturnDelegate
        Get
            Return _dReturn
        End Get
        Set(ByVal Value As frmMain.WorkComponent_ReturnDelegate)
            _dReturn = Value
        End Set
    End Property

    Public Property dException() As frmMain.WorkComponent_ExceptionDelegate
        Get
            Return _dException
        End Get
        Set(ByVal Value As frmMain.WorkComponent_ExceptionDelegate)
            _dException = Value
        End Set
    End Property
#End Region

    Public Sub WorkToBeDone()
        _iCount += 1
        Try
            'exception thrown every other time
            If _iCount Mod 2 = 0 Then
                Dim I As Integer = 5
                I = CInt(I / 0)  ' The culprit Line
            End If
            BeginInvoke(_dReturn, New Object() {"Work Done"})
        Catch ex As Exception
            BeginInvoke(_dException, New Object() {ex})
        End Try
    End Sub
End Class


P.S. To bad code doesn’t count for post credits, I am quite proud of this one, lol.

P.P.S Try not to poke a hole in this idea wink.gif

Reply

turbopowerdmaxsteel
The WorkComponent_Return method is where our custom code would go, right? In that case, the Arithmetic Exception should be raised there. Again, in this scenario, the error line cannot be traced.

The following is a simple example of what happens in my Winsock component.

CODE
Imports System.Net.Sockets

Public Class Winsock
    Dim Sck As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

    Sub Connect(ByVal Host As String, ByVal Port As Integer)
        Dim D As New System.AsyncCallback(AddressOf ConnectCallback)
        Sck.BeginConnect(Host, Port, D, Sck)
    End Sub

    Public Event SocketConnected()
    Sub ConnectCallback(ByVal ar As IAsyncResult)
        Sck.EndConnect(ar)

        _SyncObject.Invoke(New ConnectedEventRaiserDelegate(AddressOf ConnectedEventRaiser))
    End Sub

    Delegate Sub ConnectedEventRaiserDelegate()
    Sub ConnectedEventRaiser()
        RaiseEvent SocketConnected()
    End Sub

    Public _SyncObject As Control
    Public Property SyncObject() As Control
        Get
            Return _SyncObject
        End Get
        Set(ByVal value As Control)
            _SyncObject = value
        End Set
    End Property
End Class


The Winsock control contains a System.Net.Sockets.Socket object for the asynchronous network activities such as connecting to host, sending/reciving data, etc. It generates events such as SocketConnected, DataArrival, etc. These are analogous to their VB6 counterparts. Now, the trouble is, that the event, say SocketConnected is raised from the Delegate which ends the asynchronous operation, in this case, ConnectCallback. The main form which handles this event cannot make any cross threaded operations inside this event handler unless the event is raised on the same thread. So, I am using the invoke method to invoke a method on the main form's thread. This method in turn raises the SocketConnected event. Now the event handler in the main form can do jobs such as changing the text of the Form to something like - "Socket Connected". This confirms that the event is being raised on the same thread as that of the main form. However, any exception in this event handler is traced to the line which invoked the method (_SyncObject.Invoke(New ConnectedEventRaiserDelegate(AddressOf ConnectedEventRaiser))) and not to the culprit line in the event handler.

(To generalize this effect, I am using the Sync object property which specifies the form on whose thread the event needs to be raised.)

Here's the code for the main Form:-

CODE
Public Class frmMain
    Dim WithEvents Sck As New Winsock()
    Private Sub frmMain_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Sck.SyncObject = Me ' This allows the Invoke method to be called on this Form
        Sck.Connect("www.microsoft.com", 80)

    End Sub

    Private Sub Sck_SocketConnected() Handles Sck.SocketConnected
        Me.Text = "Socket Connected"    ' To Make Sure Event is Raised on the same thread
        Dim I As Integer = 5
        I = I / 0   ' Culprit Line
    End Sub
End Class


Socket Designer code. This is the default code generated when you add a component:-

CODE
Partial Class Winsock
    Inherits System.ComponentModel.Component

    <System.Diagnostics.DebuggerNonUserCode()> _
    Public Sub New(ByVal container As System.ComponentModel.IContainer)
        MyClass.New()

        'Required for Windows.Forms Class Composition Designer support
        If (container IsNot Nothing) Then
            container.Add(Me)
        End If

    End Sub

    <System.Diagnostics.DebuggerNonUserCode()> _
    Public Sub New()
        MyBase.New()

        'This call is required by the Component Designer.
        InitializeComponent()

    End Sub

    'Component overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'Required by the Component Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Component Designer
    'It can be modified using the Component Designer.
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
    End Sub

End Class

Reply


Got an Opinion! Express your Views! (no registration):-
Add your Reply/ Opinion/ Views/ Comments/ Suggestion/ Questions/ Queries etc.
Posts with decent grammar & English will be accepted and please refrain from profanities.
For asking a Question, We recommend you to sign-up (for free) so that you can track the topic easily.

Nature of your Post*: Opinion/ Reply/ Comments
Question/Query
Feedback to us.
       
Name   Email
Title/Question*

Recent Queries:-
  1. mydelegate vb listview - 0.37 hr back. (2)
  2. exceptions and threads vb.net - 16.09 hr back. (1)
  3. working with exceptions vb.net threads - 39.49 hr back. (1)
  4. vb.net thread exceptions - 64.53 hr back. (1)
  5. socket timer vb.net - 0.92 hr back. (2)
  6. vb.net cannot throw exception from timer tick - 71.46 hr back. (1)
  7. throw exception timer tick event vb.net - 75.22 hr back. (2)
  8. vb.net catch exceptions thrown from timer tick - 76.02 hr back. (1)
  9. vb.net throw exception from timer tick - 77.35 hr back. (1)
  10. php trap exception - 78.87 hr back. (1)
  11. .net vb thread exception - 103.21 hr back. (1)
  12. href "enabled = " .vb.net - 125.72 hr back. (1)
  13. vb.net throw event - 128.51 hr back. (1)
Similar Topics

Keywords : vb net trap exceptions invoked methods


    Looking for vb, net, trap, exceptions, invoked, methods,






*SIMILAR VIDEOS*
Searching Video's for vb, net, trap, exceptions, invoked, methods,
advertisement




Vb.net: How To Trap Exceptions In Invoked Methods?