My goal here is to get stack trace information from a dynamically loaded assembly. If the dynamically loaded assembly just throws the error back to the caller, the stack trace information doesn’t tell you the location in the dynamic assembly where the error occurred. If I throw my own exception, passing in the original exception, it will get added as an inner exception and will have the information need. I’m trying to avoid changing all my dynamic assemblies to throw new exceptions. Does anybody know a better way to get the stack trace information from a dynamically loaded assembly?
Public Class ErrorPlugin
' Example Function from plug in DLL
Public Function SimpleExample() As Double
Dim RentedUnits As Double = 42
Dim NumberUnits As Double = 0
Try
' Just need to generate exception
Return RentedUnits \ NumberUnits
Catch ex As Exception
' Give's me no Stack Trace infomation.
'Throw
' This will give me Stack Trace infomation,
' but requires adjusting all the plugins.
Throw New Exception("Stop dividing by zero.", ex)
End Try
End Function
End Class
Imports System.Reflection
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim rpt As Object
Dim r As Double
Try
rpt = LoadReport("C:\ErrorPlugin\bin\Debug\ErrorPlugin.dll")
r = rpt.SimpleExample()
Catch ex As Exception
MsgBox(ex.Message & vbCrLf & ex.StackTrace)
If ex.InnerException IsNot Nothing Then
MsgBox(ex.InnerException.StackTrace)
End If
End Try
End Sub
Public Function LoadReport(ByVal FileName As String) As Object
Dim m_ReportAssembly As [Assembly]
Dim m_ReportClass As Type
Dim rpt As Object
Try
m_ReportAssembly = Assembly.LoadFrom(FileName)
For Each m_ReportClass In m_ReportAssembly.GetTypes
If m_ReportClass.Name.ToLower = "ErrorPlugin".ToLower Then
rpt = Activator.CreateInstance(m_ReportClass)
Exit For
End If
Next
Return rpt
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Function
End Class
Can you see what happens if you just Throw, not Throw ex? From memory, "throw ex" does a copy while "throw" actually rethrows the caught exception. I guess your "throw ex" is effectively resetting the stack trace to your local code.
MSDN says if you're rethrowing exceptions you should add value to it by wrapping it in a new exception, otherwise you might as well not bother with try/catch in your example.
Do you have the debug symbols for these external DLL's? If you look at the stack trace, and it reads "External Code", that should be expanded into the DLL stack if you have the DLL debug symbols loaded while debugging, since the symbols will provide your debugger with the necessary info to walk the stack.
Either do this by manually loading the symbols, via the Modules Window in the Debug menu, or running against a debug build of the DLL.
Related
I'm trying to connect to my database and I'm getting this error message (ex):
I've another function which opens DB and load some data, it works fine without any error... But when I try to use this, it returns me this error, the line 175 is "cn.Open()".
My connection string and Local_DB is both same as other functions which are working without errors.
Private Sub AtualizaClientes(ID As Integer)
' Local da DataBase
Dim Local_DB As String
Local_DB = "C:\Users\Heitor BASAM\Desktop\Sistema\DataBase_Sistema.accdb"
Try
Dim cn As New OleDb.OleDbConnection
cn.ConnectionString = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={Local_DB}"
cn.Open()
cn.Close()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Glad you fixes your problem! Just a few bits to tidy up the code.
Always use Using...End Using with database objects that expose a Dispose method. They may be using unmanaged objects and their dispose methods must run to release these resources. The End Using takes care of this for you even if there is an error.
You can let the error bubble up to the calling code, user interface code. Wrap the call to AtualizaClientes in a Try...Catch...End Try
Private Sub AtualizaClientes(ID As Integer)
Dim Local_DB = "C:\Users\Heitor BASAM\Desktop\Sistema\DataBase_Sistema.accdb"
Using cn As New OleDb.OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={Local_DB}")
cn.Open()
End Using
End Sub
EDIT
You would add the Try...End Try to the UI code. Example...
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim id As Integer = 7
Try
AtualizaClientes(id)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
I figured out the problem! I've a function when a especify textbox is changed, it calls this "AtualizaClientes" function. The problem is, when you load the form, it runs a "TextChanged" event in every textbox and trys to run the database before loading it. This was causing the error message.
To solve this problem, I made a boolean variable which returns true after loading the database. So, I changed my textchanged event to run only if this variable is true.
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.
I step through this code and find out that the function is not only never called but the rest of the myBase.Load never completes what is going on here.
All outside references are displayed now. Program never hits the lines surrounded in ** and does run frmMain_Load as first item. the stepthrough icon does land ON the line that starts with reader= but never calls runAsIsQuery (breakpoints don't catch and stepthrough just evaporates). then it shows me frmMain without proccessing any other code from frmMain_Load nor from runAsISQuery
Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
sqlstring = "SELECT Nickname FROM tblBikeInfo"
reader = sql.runAsIsQuery(cnn, sqlstring) 'never fires
**If 1 = 1 Then**
'ummmm never comes back here either
End If
Extra details asked for about the other references these are in frmMain as global vars
Dim reader As OleDbDataReader
Dim sql As OLEDB_Handling 'custom class
Public cnn = MotorcyleDB.GetConnection
Function from Custom Class (OLEDB_Handling)
**Public Function runAsIsQuery(connection As OleDbConnection, SQL As String) As OleDbDataReader**
Dim reader As OleDbDataReader
Dim command As New OleDbCommand(SQL)
command.Connection = connection
Try
connection.Open()
reader = command.ExecuteReader()
Return reader
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Function
Connection string class called (MotorcyleDB)
Public Shared Function GetConnection() As OleDbConnection
Return New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\************\Documents\Visual Studio 2010\Projects\MotorcycleMinder\MotorcycleMinder\MotorcycleServiceLog11.accdb")
End Function
I found this:
http://blog.adamjcooper.com/2011/05/why-is-my-exception-being-swallowed-in.html
This is a snippet from the site.
If these conditions are met:
You are running on a 64-bit version of Windows (whether your application is built for 32-bit or 64-bit doesn’t matter; only the bit
depth of the OS)
You are building a WinForms app
You are debugging the application with Visual Studio (using default
options for Exception catching)
Your main form has a Load event handler
During the execution of your Load handler, an exception occurs
Then:
The exception will be silently swallowed by the system and, while your
handler will not continue execution, your application will continue
running.If you wrap your handler code in a try/catch block, you can
still explicitly catch any thrown exceptions. But if you don’t, you’ll
never know anything went wrong.
Note that all of the conditions must be met. If, for instance, you run
the application without debugging, then an unhandled exception still
be correctly thrown.
There is also a workaround on the site. But I would put the code in a try catch block, or put the entire thing in the Initializer/Constructor.
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.
I have a few VB.NET programs to maintain, that have been ported from VB6 and use the old style Unstructured Exception Handling:
On Error GoTo yyy
My question is, can I still get a stack trace while using Unstructured Exception Handling, or do I have to convert them all to Structured Exception Handling (Try/Catch) in order to catch an exception with its full stack trace.
Here's a way to get the stack trace to the line that caused the exception, unlike the other answer which just traces to the routine where your error handler is. The error might have occurred in a different routine.
In the unstructured error handlers, just use the GetException property of the Err object to access the underlying exception - then use the StackTrace property. Like this:
Public Class Form1
Public Sub New()
' This call is required by the Windows Form Designer.'
InitializeComponent()
' Add any initialization after the InitializeComponent() call.'
On Error GoTo ErrHandle
Call test()
Exit Sub
ErrHandle:
MsgBox("Stack trace " & Err.GetException.StackTrace)
Exit Sub
End Sub
Private Sub test()
Call test2()
End Sub
Private Sub test2()
Dim d(2) As Double
MsgBox(d(-1))
End Sub
End Class
As you know yourself, all things being equal, one should always use structured exception handling. However, if you can't, you can get your own stack trace by using the StackTrace class.
NB: Calls to stack trace are expensive, and should only be used in - ahem - 'exceptional' circumstances.
e.g
MethodName = (New StackFrame(0)).GetMethod.Name ' Get the current method
MethodName = (New StackFrame(1)).GetMethod.Name ' Get the Previous method
MethodName = (New StackFrame(2)).GetMethod.Name ' Get the method before that