Terminating process on unhandled exception

Not sure how many people aware, but .NET runtime will terminate process when background thread raises exception that is not handled. Reason behind that if exception is not handled, then this situation was not tested and can lead to program corrupting its state and it is much better to just terminate it. This reason definitely has some sense and normally I would agree with it. Application will crash, mini dump will be created and sent to Microsoft. Then you can have access to these crash reports and analyze them. But this approach has some issues.

First issue. In reality, corruption is extremely rare. For example, in our application I never heard about corrupted files. Still possible, just very rate. But on other side in many cases, if we allow user to work after exception, user can have chance to save work and make own decision about files he saved. I know that if files are corrupted in not obvious way, user will still blame us, but in many cases, user will be grateful that it is possible to save work because with 99.99% probability, data will be fine. And our application does backups automatically but still some data will be lost anyway. Is it worth to make user life much worse just to save data in 0.01% of cases? User already will be frustrated by some error, is it worth make its live even worse, by make user lost all changes for like last 5-10 minutes? And some users switch auto save, as result there is chance to lose much more work.

Second issue. In .NET it is pretty much impossible to have corrupted memory. It is in 99.99% cases, #1 source of all problems with data. And most exceptions with have with .NET is null reference exception, some errors from storage or network. These kinds of errors are really hard to test and as result they will be unhandled. Another solution is global try catch and that’s also not nice.

Third issue. Before we add .NET blocks to our application, it already has global unhandled exception handler. It collects custom information about environment including license type used, video adapters configuration, commands user executed and many other data. Also, we do present this information to user, so user able to see what will be send to us. It is possible to add it to Microsoft crash report, but it is requiring quite a bit of non-trivial work. Instead we spent little bit of time and added .NET bits to existing crash report and used all our existing infrastructure for this. And in our infrastructure, we are able to get access to crash report pretty much instant after it was sent. With Microsoft infrastructure it will take days to see report.

Forth issue. Our application used a lot of non-Microsoft compilers (Delphi is one example) and until some time ago, crash dumps were absolutely useless to analyze Delphi callstack. And even we did invest time to use Microsoft crash dump infrastructure it will not help us. Now we are able to build .pdb files for all Delphi binaries and it helps a lot, but everything already working fine and there is not much sense to do anything.

Fifth issue. In many places we have fire and forget style of calls to async void functions. We don’t really care what happens with them. One example is sending statistics. If it failed, it will definitely will not corrupt anything. All these places require try catch, then redirect exception to our exception handler or else .NET runtime will terminate process. It is error prone and adding try catch sometimes changes flow of execution.

Sixth issue. When .NET runtime decided to terminate application, it just simply disappears. Firstly, you think perhaps you closed it accidentally, but then you realized, that it just silently gone. Again, it is possible to do something about it and show something to user with explanation of what happens, but it is another problem to solve and support.

Solution. At the end after long discussion, we decided to prevent .NET runtime terminating process when exception is unhandled and redirect all exception to global exception handler. As result we put following line:

<legacyUnhandledExceptionPolicy enabled="true" />

In <configuration> <runtime> section of configuration file of our application and handle all exceptions in AppDomain.CurrentDomain.UnhandledException handler. Then we collect all information about exception and environment and present it to user with recommendation to restart application without saving data. As result, no more silent crashes, we have more exception reports about problems that previously leads to silent crashes and no reports. Everybody is happy.