VB on unhandled exception, Click button and resume loop? - vb.net

when ever my program is popping 'UnHandled Exception', can I call for a button click in my program? I have a button in my form which can actually 'fix' or 'go around' the exception. like, something 'On Error Button2.PerformClick() 3 times' ( just an example of my thinking )

Enable the application event and handle it from there, You can use the unhandled exception function to show your form, Take a look at the below code:
Imports System.Text
Imports System.IO
Namespace My
' The following events are available for MyApplication:
'
' Startup: Raised when the application starts, before the startup form is created.
' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
' UnhandledException: Raised if the application encounters an unhandled exception.
' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
Partial Friend Class MyApplication
'One of the global exceptions we are catching is not thread safe,
'so we need to make it thread safe first.
Private Delegate Sub SafeApplicationThreadException(ByVal sender As Object, ByVal e As Threading.ThreadExceptionEventArgs)
Private Sub MyApplication_Startup(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) Handles Me.Startup
'There are three places to catch all global unhandled exceptions:
'AppDomain.CurrentDomain.UnhandledException event.
'System.Windows.Forms.Application.ThreadException event.
'MyApplication.UnhandledException event.
AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf AppDomain_UnhandledException
AddHandler System.Windows.Forms.Application.ThreadException, AddressOf app_ThreadException
' AddHandler AccessViolationException, AddressOf app_AccessViolationException
End Sub
'Private Sub app_AccessViolationException(ByVal sender As Object, ByVal ex As System.AccessViolationException)
'End Sub
Private Sub app_ThreadException(ByVal sender As Object, ByVal e As Threading.ThreadExceptionEventArgs)
'This is not thread safe, so make it thread safe.
If MainForm.InvokeRequired Then
' Invoke back to the main thread
MainForm.Invoke(New SafeApplicationThreadException(AddressOf app_ThreadException), _
New Object() {sender, e})
Else
frmDebug.Show()
End If
End Sub
Private Sub AppDomain_UnhandledException(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
frmDebug.Show()
End Sub
Private Sub MyApplication_UnhandledException(sender As Object, e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException
frmDebug.Show()
End Sub
End Class
End Namespace

Related

Wait for Single Instance occurrences to complete before proceeding to next line VB.NET

Currently, I'm using the 'make app single instance'(MyApplication_StartupNextInstance event) in VB (.net framework win forms) to pass command line arguments from multiple instances to the main form. I'm adding this to a list of string and then passing this list to the next function/ sub. The list captures all the arguments if I add a message box just before calling the next function but then when there's no msgbox, not all the arguments are captured.
I've tired using timers/delays which is a hit and a miss. Tried using timed msgbox that disapper after couple secs, which is the same.
How can I make it wait till all the instances have run and then proceed to the next line of code?
'ApplicationEvents.vb
Private Sub MyApplication_StartupNextInstance(sender As Object, e As ApplicationServices.StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
Dim f = Application.MainForm
If f.GetType Is GetType(my_app_name) Then
CType(f, my_app_name).NewArgumentsReceived(e.CommandLine(0))
End If
End Sub
'my app has the below codes
Public Sub NewArgumentsReceived(args As String)
mylist.Add(args)
End Sub
Private Sub SomeForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
mylist.Add(arg) 'arg is for main form 'args' is for instances
'this is where I want to wait until all the other instances have completed
Anotherfunction(mylist)
End Sub
As I mentioned in my comments, the StartupNextInstance event can be raised any time so you should design your app to react to it at any time. The initial instance has no idea how many subsequent instances there will be or when they will start so it should simply react to them one at a time, whenever they occur. Here's an example of a single instance application where the main form is an MDI parent and the commandline arguments are text file paths that each get opened in a child window.
Child form:
Imports System.IO
Public Class ChildForm
Public Property FilePath As String
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1.Text = File.ReadAllText(FilePath)
End Sub
End Class
Parent form:
Public Class ParentForm
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim args = Environment.GetCommandLineArgs()
If args.Length > 1 Then
LoadChildForm(args(1))
End If
End Sub
Public Sub LoadChildForm(filePath As String)
Dim child As New ChildForm With {.MdiParent = Me,
.FilePath = filePath}
child.Show()
End Sub
End Class
Application events:
Imports Microsoft.VisualBasic.ApplicationServices
Namespace My
' The following events are available for MyApplication:
' Startup: Raised when the application starts, before the startup form is created.
' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
' UnhandledException: Raised if the application encounters an unhandled exception.
' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
Partial Friend Class MyApplication
Private Sub MyApplication_StartupNextInstance(sender As Object, e As StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
Dim args = e.CommandLine
If args.Count > 0 Then
DirectCast(MainForm, ParentForm).LoadChildForm(args(0))
End If
End Sub
End Class
End Namespace
In this case, the initial instance doesn't wait for anything. It just goes ahead and does what it does with its own commandline argument. It has no idea if there will be any subsequent instances or, if there are, how many there will be and when they will start, so it would have no idea what it was waiting for. Any time another instance is started, the initial instance reacts then, using the commandline argument provided.

"COM object that has been separated from its underlying RCW cannot be used" error related to vb.net form event

I'm hooking an arcobjects map event to a vb.net form to listen for map selection changes. This all works fine but users are reporting this error occassionally when opening the form. I can't see any pattern to reproduce the error and it seems to be random.
"COM object that has been separated from its underlying RCW cannot be used"
It originates from the form Load() method where I am hooking the event.
Can anyone help me understand what I've done wrong? I'm unhooking the map selection event in the FormClosing() event which I think is the correct approach.
Public Class MyForm
Private _activeViewEvents As IActiveViewEvents_Event
Private Sub FormLoad(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
_activeViewEvents = TryCast(pMxDoc.ActiveView.FocusMap, IActiveViewEvents_Event)
AddHandler _activeViewEvents.SelectionChanged, AddressOf SelectionChanged
End Sub
Private Sub SelectionChanged
'do something when selection is changed
End Sub
Private Sub FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
RemoveHandler _activeViewEvents.SelectionChanged, AddressOf SelectionChanged
End Sub
End Class
The approach you are taking to creating and destroying your handlers are valid. You can receive a RCW COM Exception when the map document is changed while your form is open. Since you are using the FocusMap to create the handles, when the document is changed, so is the FocusMap, which means you need to re-create your handlers for the new map document.
Ok so I think i've resolved this via use of the ActiveViewChanged event. Instead of rehooking the event on each form load or new document event, I tried listening for when the ActiveViewChanged event was fired and rehooking the SelectionChanged event each time. Turns out this is fired more than once each time a new document is opened (not sure why). Anyway, problem seems to have gone. Here's some example code:
Public Class MyForm
Private _activeViewEvents As IActiveViewEvents_Event
Private _docEvents As IDocumentEvents_Event
Private Sub FormLoad(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddHandler _docEvents.ActiveViewChanged, AddressOf ActiveViewChanged
End Sub
Private Sub ActiveViewChanged()
Dim maps = pMxDoc.Maps
For i = 0 to maps.Count - 1 'remove handlers from all maps
RemoveActiveViewEvents(maps.Item(i))
Next
SetupActiveViewEvent(pMxDoc.ActiveView.FocusMap) 'only add handler to active map
End Sub
Private Sub RemoveActiveViewEvents(map As IMap)
_activeViewEvents = CType(map, IActiveViewEvents_Event)
RemoveHandler _activeViewEvents.SelectionChanged, AddressOf SelectionChanged
End Sub
Private Sub SetupActiveViewEvents(map As IMap)
_activeViewEvents = CType(map, IActiveViewEvents_Event)
AddHandler _activeViewEvents.SelectionChanged, AddressOf SelectionChanged
End Sub
Private Sub SelectionChanged
'do something when selection is changed
End Sub
End Class

axacropdflib - set source from worker thread

I have a vb.net project which has a treeview of pdf's on the left and the acrobat AxAcroPDF viewer control on the right. Click a item in the treeview, I get the fileinfo.fullname value and pass that over to the AxAcroPDF src property.
While testing I noticed that pdf's were slow to load and would block my ui thread so I decided that a workerthread would be a good helper to lazy load these pdf's in the background.
When I run my code with the worker thread's DoWork method and it tries to update my pdfviewer object I get an invalid cast exception.
System.InvalidCastException was caught HResult=-2147467262
Message=Unable to cast COM object of type 'System.__ComObject' to
interface type 'AcroPDFLib.IAcroAXDocShim'. This operation failed
because the QueryInterface call on the COM component for the interface
with IID '{3B813CE7-7C10-4F84-AD06-9DF76D97A9AA}' failed due to the
following error: No such interface supported (Exception from HRESULT:
0x80004002 (E_NOINTERFACE)). Source=mscorlib StackTrace:
at System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget, Boolean& pfNeedsRelease)
at AcroPDFLib.IAcroAXDocShim.set_src(String pVal)
at AxAcroPDFLib.AxAcroPDF.set_src(String value)
at myapp.fill_treeview_with_filesfolders_docked_andthreads.LoadPDFInBackground(String
selectedfile) in
C:\Users\me\Desktop.....\fill_treeview_with_filesfolders_docked_andthreads.vb:line
84 InnerException:
I can't find any other threads online with this exception detail so I am not sure what the issue is here. I thought my problem had to do with a cross thread access violation but even if I set Control.Checkforillegalcrossthreadcalls to false I get the same exception. It didn't make sense to me that I would check for invokerequired from the DoWork routine anyways because the point of my worker thread is to handle the load for me, not shove it back into the UI thread.
Can anyone recommend a workaround that I can try to achieve what I am after here?
my Code:
The treeview afterselect is wired to displayfile
AddHandler TreeView.AfterSelect, AddressOf displayfile
Private Sub displayfile(sender As Object, e As TreeViewEventArgs)
Try
Dim selectedfile As FileInfo = New FileInfo(e.Node.Tag) 'tag has our full path embedded.
'todo: Future - consider type of the file and load a pre-made panel with appropriate host object
If selectedfile.Extension.ToLower.Equals(".pdf") Then
'show "loading...."
LoadingPanel.BringToFront()
backgroundworker.RunWorkerAsync(selectedfile.FullName)
End If
Catch ex As Exception
End Try
End Sub
Background Worker Stuff:
#Region "Background Worker Events"
' This event handler is where the time-consuming work is done.
Private Sub backgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As DoWorkEventArgs) Handles backgroundworker.DoWork
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
e.Result = LoadPDFInBackground(e.Argument)
End Sub
' This event handler updates the progress.
Private Sub backgroundWorker_ProgressChanged(ByVal sender As System.Object, ByVal e As ProgressChangedEventArgs) Handles backgroundworker.ProgressChanged
ProgressBar.Value = e.ProgressPercentage
End Sub
' This event handler deals with the results of the background operation.
Private Sub backgroundWorker_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As RunWorkerCompletedEventArgs) Handles backgroundworker.RunWorkerCompleted
If e.Result Then
'hide loading panel and show pdf panel
pdfviewer.BringToFront()
Else
'what to do if failed to load???
End If
End Sub
#End Region
Private Function LoadPDFInBackground(ByVal selectedfile As String) As Boolean
Try
pdfviewer.src = selectedfile
Return True
Catch ex As Exception
Return False
End Try
End Function
Just a thought, but try changing this line:
pdfviewer.src = selectedfile
to the following:
If pdfviewer.InvokeRequired Then
pdfviewer.Invoke(Sub() pdfviewer.src = selectedfile)
It might work around the error. Interesting to see if it does.

Make error window

I want to create my edition error report form instead default error window.
How I can create my edition form error report?
For example:
So you want to call a custom handler when exception happens? No problem, just define these 3 magic lines in the beginning of your program (as first lines of Sub Main):
AddHandler Application.ThreadException, AddressOf GenericHandler
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException)
AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf UnhandledHandler
Then define GenericHandler and UnhandledHandler, which would call your custom form.
Here is a sample implementation of both handlers:
Public Shared Sub GenericHandler(ByVal sender As Object, ByVal args As Threading.ThreadExceptionEventArgs)
ReportException(args.Exception)
End Sub
Public Shared Sub UnhandledHandler(ByVal sender As Object, ByVal args As UnhandledExceptionEventArgs)
If Not Debugger.IsAttached Then
ReportException(args.ExceptionObject)
End
End If
Public Shared Sub ReportException(ByVal ex As System.Exception)
MsgBox(ex.ToString, MsgBoxStyle.OkOnly Or MsgBoxStyle.Exclamation, "Unhandled exception - Please contact support")
'you can further improve this to add custom logging etc.
End Sub

System.Threading.Timer not firing, global.aspx

I am a newbie, but I am unable to get this code working. FileSweeper is supposed to start a timmer that triggers fileCopy on a web server. fileSweeoer is triggered by global.asax. FileCopy then will copy the file. However the FC.copy never fires. Any help/explanation would be helpful!
Would it make any difference if this was a class running on a web server?
My code is from http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx.
Imports Microsoft.VisualBasic
Imports System
Imports System.Threading
Public Class fileSweeper
Dim stateTimer As Timer
<MTAThread()> _
Sub Main()
Dim FC As New fileCopy
Dim tcb As TimerCallback = AddressOf FC.Copy
stateTimer = New Timer(tcb, "", 20000, 200000)
GC.KeepAlive(stateTimer)
End Sub
End Class
Global.asax:
<%# Application Language="VB" %>
<script runat="server">
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
End Sub
Sub Application_End(ByVal sender As Object, ByVal e As EventArgs)
' Code that runs on application shutdown
End Sub
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
' Code that runs when an unhandled error occurs
End Sub
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' Code that runs when a new session is started
Dim FS As New fileSweeper
FS.Main()
End Sub
Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)
' Code that runs when a session ends.
' Note: The Session_End event is raised only when the sessionstate mode
' is set to InProc in the Web.config file. If session mode is set to StateServer
' or SQLServer, the event is not raised.
End Sub
</script>
You are probably going to get unexpected results here because you are likely going to get multiple sessions on a web server. You might want to consider creating a console app and using windows task scheduler.