Handling an exception thrown from inside a dll - vb.net

I'm working on a project for school in which a dll is loaded.
The dll that is loaded is a bridge between my program and the Twincat System Manager which forms the bridge between the computer and a PLC via the local network.
I need to read Variables trough this whole chain from the plc to my program.
This is the way I do this:
Public Function adsReadReal(ByVal Variabelenaam As String) As Single
Dim ds = New TwinCAT.Ads.AdsStream(4 * 8) ' new data stream
Dim br = New System.IO.BinaryReader(ds) 'new binary
Dim hVar = New Integer
Try
ConState(1)
tcclient = New TcAdsClient
ConState(2)
tcclient.Connect(Form1.amsAdress, 801) 'connects the tcclient to the PLC
hVar = tcclient.CreateVariableHandle(Variabelenaam) 'creats a handle for the variable
tcclient.Read(hVar, ds) 'read it
ConState(5)
Return br.ReadSingle() 'convert it from binary to readable for vb
Catch ex As Exception
ConState(0)
PrintEx(ex) 'print out the exception
finally
tcclient.Dispose() 'make the object stop being used to prevent a lingering connection
End Try
Return False
End Function
Now the program loads a dll called TwinCAT.ADS.dll at the start of the connection module. If the Twincat system manager is running the program ends normally, but when it is not it crashes and gives me this error:
System.DllNotFoundException was unhandled
Message="Kan DLL tcadsdll.dll niet laden: Kan opgegeven module niet vinden. (Uitzondering van HRESULT: 0x8007007E)"
Source="TwinCAT.Ads"
TypeName=""
StackTrace:
bij TwinCAT.Ads.Internal.TcAdsDllWrapper.TcAdsDll.AdsAmsUnRegisterRouterNotification()
bij TwinCAT.Ads.Internal.TcAdsDllWrapper.AmsUnRegisterRouterNotification(Boolean
throwAdsException)
bij TwinCAT.Ads.Internal.TcLocalSystem.Dispose(Boolean disposing)
bij TwinCAT.Ads.Internal.TcLocalSystem.Finalize()
which is roughly translated to:
Cannot load DLL tcadsdll.dll: Cannot find given module. (Exception at
HRESULT: 0x8007007E)
This is not a dll that I have imported, so it must be from the TwinCAT.ADS.dll
How can I prevent the program from throwing this error at me and instead close the program peacefully? I have tried to catch all the exceptions of every dll related operation possible.
Also the source is on Bitbucket. I will make it public on request.
Some links on the official but quite unhandy Beckhoff site:
http://infosys.beckhoff.com/espanol.php?content=../content/1034/tcquickstart/html/tcquickstart_samplevisualbasicnet.htm&id=10449
Edit:
Apparently using tcclient.dispose() causes the error since the finnaly statement was use instead of just after the try block
Edit: This currently catches the exception but it does not handle it.
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
AddHandler currentDomain.UnhandledException, AddressOf MyHandler
Dim tick As Byte = 0
Sub MyHandler(sender As Object, args As UnhandledExceptionEventArgs)
Dim ex As Exception = DirectCast(args.ExceptionObject, Exception)
MsgBox("exception tick" & Str(tick))
tick = tick + 1
PrintEx(ex)
End Sub
Edit:
The exception isn't caught properly because in vs2008 a couple of errors occurs but the tick appears after I press F5 (continue)
When the program is run directly, I only see 1 Tick. Then windows gives an error.

Did you try an unhandled exception handler?
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
AddHandler currentDomain.UnhandledException, AddressOf MyHandler
Sub MyHandler(sender As Object, args As UnhandledExceptionEventArgs)
Dim e As Exception = DirectCast(args.ExceptionObject, Exception)
Console.WriteLine("MyHandler caught : " + e.Message)
End Sub

Related

Exception handling with Async Task functions

I am beginning to learn about Async / Task functions for running cancellable SQL queries in VB.NET.
I have the following code in a class library which runs two tasks and does not have any exception handling, as I would like to handle this in the application that calls the class library. I have the following code in the class library.
Public Async Function DirectoryList(ct As CancellationToken) As Task(Of List(Of Directory))
ct.ThrowIfCancellationRequested()
Dim ds As DataSet
Dim dirs As List(Of Directory)
Dim ctUnregister As CancellationTokenRegistration
ds = Await Task.Run(Function()
Using newConnection = New SqlConnection(Me.InitializationData.ConnectionString)
Using sqlAdapter = New SqlDataAdapter("DirectoryList", newConnection)
ctUnregister = ct.Register(Sub() sqlAdapter.SelectCommand.Cancel())
With sqlAdapter
.SelectCommand.CommandType = CommandType.StoredProcedure
.SelectCommand.CommandTimeout = Me.InitializationData.CommandTimeout
End With
Dim newDataSet As DataSet = New DataSet()
sqlAdapter.Fill(newDataSet)
Return newDataSet
End Using
End Using
' Remove the registration we set earlier so the cancellation token can be used normally.
ctUnregister.Dispose()
End Function, ct)
dirs = Await Task.Run(Function()
Dim dirsResult As New List(Of Directory)
Dim tbl As DataTable = ds.Tables(0)
For Each row As DataRow In tbl.Select()
' process the data
ct.ThrowIfCancellationRequested()
Next
Return dirsResult
End Function, ct)
Return dirs
End Function
I then call it as follows:
Try
dirs = Await databaseLibrary.DirectoryList(cts.Token)
MsgBox("Query complete!")
Catch ex As System.Data.SqlClient.SqlException
MsgBox("Cancelled (SQL)")
Catch ex2 As OperationCanceledException
MsgBox("Cancelled")
Catch ex3 As Exception
MsgBox("Cancelled")
End Try
Functionally it seems to work as expected - I can cancel the requests and exceptions are thrown as expected.
However I would like to handle the exceptions so I can gracefully cancel the tasks, but even when running in Debug mode within the IDE, the app still breaks and the exception (e.g. SqlException) is shown within the IDE. If I step on, eventually the Catch logic runs.
When the application is run outside of the IDE, the exception handling works as expected.
This seems different to the normal behaviour when running in the debugger- normally the exception handling runs but only breaks if the exception is unhandled.
Why is this behaviour different, presumably because of the Async functions?
when running in Debug mode within the IDE, the app still breaks and the exception (e.g. SqlException) is shown within the IDE.
Why is this behaviour different, presumably because of the Async functions?
Exceptions thrown by asynchronous functions are not caught directly by the catch. What actually happens is that the exception is caught by a compiler-generated state machine, and that exception is placed on the returned Task. Later, when that task is awaited, the exception is re-raised, and then can be caught by the catch.
However, this "indirection" in exception handling means that the IDE does kind of freak out when the exception is initially thrown. As far as it can tell, there is no catch in your code that is going to catch it, so that's why it breaks and displays the exception. It doesn't know that the compiler-generated code will catch it. You can tell the IDE not to worry about uncaught exceptions.

TPL Exception - How to attach to the main thread

I am planning to implement TPL in my existing asp.net application to improve the performance.
I am implementing them in the selective places (like calls to write data to one table and some kind of irrelevant and non sync operations ), and we are processing all the unhandled exception in the application_error event in the global.asax file.
But when I get an error the TPL its not throwing that error to the application_error event.
When I tried with Task.WaitAll(t1) then its throwing to app_error event, but its taking a while to process the written code.
But when I try with the following method (to take some time only when fault is occurred) then also its not throwing the exception to the main thread.
Task.Factory.StartNew(Sub(mainContext)
HttpContext.Current = mainContext
LookUpRepository.AddItems(CurrentContext.LoggedInUser.UserID, ClientID)
End Sub, HttpContext.Current).ContinueWith(Sub(tplException)
For Each ex In tplException.Exception.InnerExceptions
Throw ex
Next
End Sub, TaskContinuationOptions.OnlyOnFaulted Or TaskContinuationOptions.ExecuteSynchronously)
I tried with ConcurrentQueue to catch the exceotion and sometimes it halts the applcation and the control is not proceeding further line of code.
So how can I attach the the TPL exceotion to the main thread which is hosting the asp.net application.
There is an observable collection ObservableCollection that is available in .Net. If there is an error in the thread that we opened and if we add them in this ObservableCollection , then it will raise an event, From that event we can handle this error
Task.Factory.StartNew(Sub(mainContext)
'Using sw As StreamWriter = File.AppendText("c:/logs/t7.txt")
' sw.WriteLine("thread=" + Thread.CurrentThread.ManagedThreadId.ToString())
'End Using
HttpContext.Current = mainContext
'Do some operation
End Sub, HttpContext.Current).ContinueWith(Sub(tplException, subContext)
HttpContext.Current = subContext
If IsNothing(tplException.Exception) Then Exit Sub
For Each ex In tplException.Exception.InnerExceptions
MyAppState.Instance.ObservableException.Add(ex)
Next
End Sub, HttpContext.Current)
Collection needs to declared
Public WithEvents ObservableException As ObservableCollection(Of Exception)
Event that will get raised by .Net framework to handle the exception
Private Sub CollectionChangedHandler(sender As Object, e As NotifyCollectionChangedEventArgs) Handles ObservableException.CollectionChanged
Dim ex = DirectCast(e.NewItems(0), Exception)
ExceptionLog.SaveFile(ExceptionLog.ReadExceptionInformationFromException(ex))
End Sub

Open raster file error: A Debugger is attached to w3wp exe but not configured

I have written a vb.net function to open a raster file. The function is as follows:
Public Function OpenRaster(ByVal directoryName As String, ByVal fileName As String) As IRaster
Dim workspaceFactory As IWorkspaceFactory
Dim rasterWorkspace As IRasterWorkspace
Dim rasterDataset As IRasterDataset = Nothing
Try
workspaceFactory = New RasterWorkspaceFactory
ESRI.ArcGIS.DataSourcesGDB.AccessWorkspaceFactoryClass()
rasterWorkspace = TryCast(workspaceFactory.OpenFromFile(directoryName, 0), IRasterWorkspace)
rasterDataset = rasterWorkspace.OpenRasterDataset(fileName)
Catch e As Exception
End Try
Dim pRasterLayer As IRasterLayer = New RasterLayer()
pRasterLayer.CreateFromDataset(rasterDataset)
Dim pRaster As IRaster = Nothing
pRaster = pRasterLayer.Raster
Return pRaster
End Function
The above function is throwing below error in TryCast statement :
A Debugger is attached to w3wp exe but not configured to debug this unhandled exception. To debug this expression detach the current debugger.
I searched on google also, some suggestions were to enable 32-bit in IIS application pool, but enabling this also is not making the code work

Building A True Error Handler

I am trying to build an error handler for my desktop application. The code Is in the class ZipCM.ErrorManager listed below.
What I am finding is that the outputted file is not giving me the correct info for the StackTrace.
Here is how I am trying to use it:
Try
'... Some stuff here!
Catch ex As Exception
Dim objErr As New ZipCM.ErrorManager
objErr.Except = ex
objErr.Stack = New System.Diagnostics.StackTrace(True)
objErr.Location = "Form: SelectSite (btn_SelectSite_Click)"
objErr.ParseError()
objErr = Nothing
End Try
Here is the class:
Imports System.IO
Namespace ZipCM
Public Class ErrorManager
Public Except As Exception
Public Location As String
Public Stack As System.Diagnostics.StackTrace
Public Sub ParseError()
Dim objFile As New StreamWriter(Common.BasePath & "error_" & FormatDateTime(DateTime.Today, DateFormat.ShortDate).ToString().Replace("\", "").Replace("/", "") & ".log", True)
With objFile
.WriteLine("-------------------------------------------------")
.WriteLine("-------------------------------------------------")
.WriteLine("An Error Occured At: " & DateTime.Now)
.WriteLine("-------------------------------------------------")
.WriteLine("LOCATION:")
.WriteLine(Location)
.WriteLine("-------------------------------------------------")
.WriteLine("FILENAME:")
.WriteLine(Stack.GetFrame(0).GetFileName())
.WriteLine("-------------------------------------------------")
.WriteLine("LINE NUMBER:")
.WriteLine(Stack.GetFrame(0).GetFileLineNumber())
.WriteLine("-------------------------------------------------")
.WriteLine("SOURCE:")
.WriteLine(Except.Source)
.WriteLine("-------------------------------------------------")
.WriteLine("MESSAGE:")
.WriteLine(Except.Message)
.WriteLine("-------------------------------------------------")
.WriteLine("DATA:")
.WriteLine(Except.Data.ToString())
End With
objFile.Close()
objFile = Nothing
End Sub
End Class
End Namespace
What is happenning is the .GetFileLineNumber() is getting the line number from objErr.Stack = New System.Diagnostics.StackTrace(True) inside my Try..Catch block. In fact, it's the exact line number that is on.
Any thoughts of what is going on here, and how I can catch the real line number the error is occuring on?
Edit: Changed the code to account for the Exception.StackTrace being a string rather than a real StackTrace
You're creating a new StackTrace, so then it will be for the line you're declaring it on, if you want the line number of the original exception, use the stack trace in Exception.StackTrace.
I think you're being a little confused, I can't see why you create the new StackTrace at all?
Edit: Added more bits to the answer here since easier to see the syntax than in a comment
Currently you have the line
objErr.Stack = New System.Diagnostics.StackTrace(True)
Which means that you're creating a whole new stacktrace, starting when you're creating it.
Instead change that line to:
objErr.Stack = New System.Diagnostics.StackTrace(ex, True)
Which will have the stacktrace from when the error actually happened.
Edit: Added complete sample:
Private Sub a1()
Try
a2()
Catch ex As Exception
Dim st As New StackTrace(True)
Debug.WriteLine(String.Format("ST after exception, will give line number for where st was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
st = New StackTrace(ex, True)
Debug.WriteLine(String.Format("ST after exception using exception info, will give line number for where exception was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
End Try
End Sub
Private Sub a2()
Dim st As New StackTrace(True)
Debug.WriteLine(String.Format("ST before exception, will give line number for where st was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
Dim b As Integer = 0
Dim a As Integer = 1 / b
End Sub
SOLVED: You should change the wrong instruction, of the original code:
.WriteLine(Stack.GetFrame(0).GetFileLineNumber())
with this new one:
.WriteLine(Stack.GetFrame(Stack.FrameCount - 1).GetFileLineNumber)
and you will see, it will return the exact Line_Number of code
where the run time error occurs!!
You do not need to include a Stack property for your ErrorManager, because you have access to the stack trace through the exception.
From experience, I would create a Shared Sub Write(ex As Exception, location As String) method on the ErrorManager, and call in your Catch statement as:
ZipCM.ErrorManager.Write(ex, "Form: SelectSite (btn_SelectSite_Click)")
This change results in cleaner code, reduces the need to write lots of code in each Catch statement, and allows you to change the implementation of Write without having to revisit/rework/refactor each Catch statement.
For instance, you can change the Write method to also call Debug.WriteLine(ex) so you can see which exceptions you are handling during debugging without having to open the file. Moreover, you may want to include WriteNotify method that displays a message box of the exception then calls the Write method to log the exception.

Problems with Exception Handling in Winforms application using a global error handler

I have a Windows Form application that has a global error handler to display unexpected errors.
Namespace My
Class MyApplication
Delegate Sub ProcessParametersDelegate(ByVal sender As Object, ByVal args() As String)
Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException
Dim ErrorText As String = e.Exception.Message & ", " & e.Exception.StackTrace
Try
MessageBox.Show("Global Error Handler: An unhandled error occurred. It will be copied to your clipboard upon OKing this message: " & ErrorText)
Clipboard.SetText(ErrorText)
Catch ex As Exception
End Try
End Sub
End Class
End Namespace
In VS2008, I have my Debug - > Exceptions options set to the defaults, which are to raise an error on all unhandled exceptions.
I have a few issues:
1) for the same deployed code, I SOMETIMES get line number in the displayed StackTrace error message and sometimes do not, even when the error message includes source code that I have written rather than a referenced binary. The project is compiled with a DEBUG configuration.
2) The application strangely minimizes to the tray when the error occurs (I thin it is unlikely that anyone can diagnose this issue w/o my more code posted, but I'll mention it anyways)
3) When I try to intentionally raise an error by, for example, dividing by zero hoping to test by global error handler, I get a dialog error message from the interactive debugger rather than jumping into my global error handler (which i want to debug because there is more to it than I posted.) Do you have any idea how to triggerand force teh execution of the global event handler?
1)
If you are missing part of your stack trace, it is likely because of how you are ignoring inner exceptions.
Instead of:
Dim ErrorText As String = e.Exception.Message & ", " & e.Exception.StackTrace
Use:
Dim ErrorText As String = e.Exception.ToString()
Also, make sure your .pdb file gets deployed to the same location as your .exe file.
2) Not sure.
3)
To step through MyApplication_UnhandledException in the debugger:
Debug -> Start Without Debugging
Debug -> Attach to Process
Find your app name in the "Attach to Process" window and Attach to it. Your breakpoint will now be hit.
I like to use this to force an exception, but there are many other options:
MessageBox.Show(Nothing.ToString())
Regarding pt 3:
If your only relevant handler for an exception is the global exception handler, then the debugger treats the exception as "unhandled." Essentially, you cannot step through your global event handler as you are trying to do.