6

I made a project in VB.NET. If the application I made will produce an unwanted error, it will create a text file containing the error. I was able to do this when running it on Visual Studio but it does not work when running the separate application, the executable file found on bin/Debug.

Here's what I've done:

Sub Main(ByVal ParamArray args() As String)
  Try
System.Windows.Forms.Application.Run(New Form1)
  Catch ex As Exception
WriteErrorLogs(ex)
  End Try
End Sub

Sub WriteErrorLogs(Byval ex As Exception)
' create a textfile the write x.Message, x.Source, x.ToString
  Dim dnow As String = Now.ToString
  Dim filename As String = "Error " & removeInvalidChars(dnow)
  Dim saveto As String = New IO.FileInfo("Errors/" & filename).FullName & ".txt"
  Dim title As String = ex.Message
  Dim stacktrce As String = ex.StackTrace

  If Not IO.Directory.Exists(New IO.DirectoryInfo("Errors").FullName) Then IO.Directory.CreateDirectory("Errors")
  Dim fw As New IO.StreamWriter(saveto, False, System.Text.Encoding.UTF8)
  fw.WriteLine(title)
  fw.WriteLine()
  fw.WriteLine(stacktrce)
  fw.Close()
End Sub

Private Function removeInvalidChars(ByRef s As String)
  Dim invalidChars() As Char = "\/:*?""<>|".ToCharArray
  For Each i As Char In invalidChars
    s = s.Replace(i, ".")
  Next
  Return s
End Function

Is there a better solution for this?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
pvzkch
  • 341
  • 2
  • 4
  • 18
  • Where's the code inside the `WriteErrorLogs` method? – Mike Perrenoud Feb 13 '13 at 16:34
  • It's not clear what you're asking for help with here. Where's the file that you're writing to? Is the problem that you can't write to a text file, or more a query about best practise for error-handling? – thesheps Feb 13 '13 at 16:35
  • Also, there are a lot of ways to handle logging. In [this answer](http://stackoverflow.com/questions/12810475/how-to-dynamically-set-log-file-using-app-config-and-system-diagnostics/12810805#12810805) I detailed `log4net`. – Mike Perrenoud Feb 13 '13 at 16:37
  • You have to be careful that the code inside `WriteErrorLogs` is not also throwing an error. If it is trying to write to the root of the C:\ drive for example this may fail. – Matt Wilko Feb 13 '13 at 16:39
  • (Completed the code) My problem is the Try Catch error trap does not work when not in debug mode. Lets say Form1 has a button that invoked an error. The error will be thrown on the Catch on the Sub Main. Now, I want to know if there is a better solution instead of using Try Catch error trap since it does not work on a deployed application. – pvzkch Feb 13 '13 at 16:48
  • @Michael Perrenoud: Log4Net is complete overkill for writing to a log file. An independent developer wrote something for my company and he used that dll for his logging and it was nothing but a headache from day one. There should be no need for a third party tool when all you need is logging. – Mike_OBrien Feb 13 '13 at 17:07
  • @Ivan: I've had issues like this in the past and most times it is ussually something to do with permissions when trying to create the file or something along those lines. I would put the `WriteErrorLogs` function in its own `try...catch...end try` and have it throw up a message box or something along those lines if there is an error thrown. – Mike_OBrien Feb 13 '13 at 17:09

1 Answers1

10
  Try
      System.Windows.Forms.Application.Run(New Form1)
  Catch ex As Exception
      WriteErrorLogs(ex)
  End Try

Yes, that Catch clause is never going to catch an exception when you run this without a debugger attached. Exceptions that are raised on the UI thread are rerouted and trigger the Application.ThreadException event instead. Which by default displays a dialog, you should have noticed that when you ran it from the bin\Debug directory.

It works differently when you have a debugger attached, that dialog really gets in the way when you need to debug unhandled exceptions. So the ThreadException event is intentionally disabled and the debugger shows you where your code has crashed. Which will not happen with the code you wrote, now that Catch clause does catch the exception.

The Catch clause also will not work when your program crashed due to an unhandled exception that was raised on a worker thread, it can only see exceptions on the UI thread.

You will need a more solid approach, you can get one from the AppDomain.UnhandledException event. Which is raised for any unhandled exception, regardless of what thread it was raised on. Make your code look like this instead:

Module Module1
    Public Sub Main(ByVal args() As String)
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        If Not System.Diagnostics.Debugger.IsAttached Then
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException)
            AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf LogUnhandledExceptions
        End If
        Application.Run(New Form1())
    End Sub

    Private Sub LogUnhandledExceptions(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
        Dim ex = DirectCast(e.ExceptionObject, Exception)
        '' Log or display ex.ToString()
        ''...
        Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(ex))
    End Sub
End Module

Using Debugger.IsAttached ensures that you can diagnose unhandled exceptions with the debugger. Using Application.SetUnhandledExceptionMode ensures that the dialog is never displayed and all exceptions are logged.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • +1 Though this answer is in VB.Net, it has been helpful to me as it is almost identical to the C# implementation I need. – Ian May 25 '16 at 06:05