0

I'm working on a WPF C# application that needs to run some cleanup code on every exit, no matter what. Dotnet provides some fatal crash handlers, but they don't get called if I kill the app through debugger or task manager.

I've tried looking for some sort of best practice for this and was unable to come across anything. The best I could come up with is trying to create a separate process to watch for the main process to crash. I don't think this solution will work if I want to package everything up as a single portable .exe and run it. From what I understand, any thread spawned from the main thread will be destroyed when the main thread crashes. And instead of starting a separate thread, if I wanted to spawn a new process, I don't think I can package that in my .exe.

So I'm really not sure I have an approach here. Can someone point me in the right direction?

1
  • 1
    'I'm working on a WPF C# application that needs to run some cleanup code on every exit, no matter what'...including power failure? Your requirement is unrealistic if not impossible. Commented Mar 3, 2023 at 22:31

2 Answers 2

5

The short answer is there's absolutely nothing you can do in 100% of cases about a forcible process termination at the time of termination. Think about it. This is not unique to desktop apps. Browser apps have this problem. Server apps have it. The power could go out. The room could explode. There's literally no way to guarantee something happens before the app goes caput.

But even if you can't guarantee your cleanup executes before exit, the situation isn't hopeless. Essentially, you need to keep your application's state saved somewhere persistent - this can be local or online depending on your application. Every time you start something critical, you log that it's starting. When it finishes successfully, you log that it succeeded (or you delete the record of there being an open task).

Now let's say there's a crash. Two possibilities -

(1) if you're an online application, and were saving this state online, then your server could be pinging your app periodically until the task is done. If it doesn't get a successful ping back from your app, it can assume that the app died, and cleanup whatever's necessary.

(2) If you're totally offline, then you likely can only do the clean up when your application restarts. At that point you check the record of the last state, see if it was open/incomplete, and if so do your cleanup.


Edit in response to comment -

Based on what you're doing as far as changing the system with Windows APIs and wanting to ensure it's restored properly, it sounds like an online solution won't work.

I think your idea to launch a separate process to be your monitor might be your best bet. You need it to be a single portable EXE but still should be able to run the same process via Process.Start, but put it in a monitor "mode" via command line arguments. You can get the process path through Assembly.GetExecutingAssembly and then launch a second instance to put it in that mode.

That still won't help you from a system crash though. If you want the highest level of robustness you may want to install your app in the Startup folder so that it can check for and handle this failure mode on restart. In that case you'll need to be constantly logging progress to disk somewhere so that your application knows how to properly roll back on startup if things failed.

2
  • So I should have been a little more clear. I don't need to guarantee it runs before the app crashes or exactly when it crashes, I just need it to run at some point after a crash. My program has connection to the internet, it's modifying the desktop environment through WindowsAPI and I need to make sure that I can clean up all my changes at some point during or after an exit. I would prefer if this can be done without relying on a restart of the program.
    – blam
    Commented Mar 2, 2023 at 0:47
  • Ah ok. Added some additional thoughts above that might help. Commented Mar 2, 2023 at 2:28
2

Make a single executable that can be executed in two different ways. The default "way" would take no arguments (or only what is necessary) and when run, would run as the monitor.

The monitor would then run the same executable ("itself") using Process.Start but with different arguments so it behaves as your WPF UI.

In this sense the monitor is the parent process and a "shim" for the real process. As such the monitor can detect when the child exits and do whatever cleanup is necessary.

This allows you to also package a single executable.

Incidentally, the monitor needs to, itself, exit after detecting and cleaning up the executing child so you don't have a zombie process lurking around too long.

1
  • It appears great minds think alike. :) Commented Mar 2, 2023 at 2:57

Not the answer you're looking for? Browse other questions tagged or ask your own question.