Quite a few exceptions cannot be caught and that differ from the .NET version to the .NET version. And some exceptions can be caught and logged, but cannot be recovered from (memory exceptions). However, they can be debugged (they are called the first chance exceptions , the first chance is always for the debugger, the second chance for the code, thanks to JeroenH for pointing this out). Here's a post in CodeProject that explains this principle .
What you need to do is select some exceptions for the candidate in Visual Studio that you suspect might occur and attach the debugger to the running instance.
Having unmanaged resources like yours (serial port), you can have unmanaged leaks (not using IDisposable + using ) and unmanaged exceptions. These exceptions can only be caught with an empty catch (i.e., without specifying an even Exception that is not the parent of unmanaged exceptions) in the try / catch block.
PS: undefined behavior can occur when exceptions are thrown in finally blocks or in finalizers / destructors. In addition, not all exceptions propagate along the boundaries of a thread and terminate all threads.
Edit
To make things a little clear, there are a few exceptions that the CLR (and its specification) define as unattractive. In principle, these are all exceptions that cross the boundaries of flows. These asynchronous exceptions that occur during a lock will corrupt the state. The most famous are OutOfMemoryException , ThreadAbortException and StackOverflowException . When an OutOfMemoryException or StackOverflowException occurs in synchronous code, it is unlikely that you can fix the state and the CLR will terminate your application.
In addition, there are ExecutionEngineException and BadImageFormatException , which should not be executed in the code being checked and should not be caught. Exceptions, such as TypeLoadException and MissingMemberException , can sometimes be caught and sometimes not (if there is no related assembly, they will be difficult to catch, but you shouldn't, but if you use reflection, you have to catch these).
In short: exceptions must be caught in the thread in which they occur. You cannot catch exceptions if they occur in another thread because they are not propagated (with the exception of ThreadAbortException ). Your application remains alive after the exception (at least you think), so it’s logical to assume that the exception does not occur in the thread where you are trying to catch it. Using the Debug> Exceptions window, you can select any exception and break the code when they occur.
Exception note
Added note on managed and unmanaged exceptions. You cannot catch an unmanaged exception using catch (Exception e) , because an unmanaged exception is not inherited from Exception . Instead, use an empty catch that will catch any unmanaged exception for you. Wrap this around your applications and stream entry point methods, and you should catch most of the perceptible exceptions.