Fixing the System ExecutionEngineException in 2024
Inhaltsverzeichnis
- 1 The System ExecutionEngineException – A mean Exception Type…
- 2 What is an ExecutionEngineException?
- 3 Investigating the ExecutionEngineException
- 3.1 Tip 1 – Narrowing down possible error sources
- 3.2 Tip 2 – The little things do matter
- 3.3 Tip 3 – Focus on the interesting stuff
- 3.4 Tip 4 – Review recent code changes
- 3.5 Tip 5 – Third-Party Libraries
- 3.6 Tip 6 – Platform specific problems
- 3.7 Tip 7 – Look at the Event Viewer
- 3.8 Tip 8 – Check your hardware
- 4 Fixing my personal problem case
- 5 Conclusion
- 6 Related posts
The System ExecutionEngineException – A mean Exception Type…
Did you encounter the System.ExecutionEngineException as well? Then I got some nice news for you, I will try my best to help you discover and fix the root source for that horrible exception type. Let me warn you beforehand though – there can be many different sources for the System.ExecutionEngineException!
I would love to give you the „This is the problem – this is the solution“ type of answer, but considering the ExecutionEngineException, you simply just can’t. Please read the descriptions carefully & use the table of contents to jump to specific sections – fixing one of the meanest exceptions in .NET. Feel free, to jump to my personal fix, regarding my own problem case.
It can be thrown at the start of your application, or even when you were in the middle of your work – not expecting it at all. I’m talking about for example opening a new Window / Form and then it gets thrown after 4 seconds, without doing much. On the next test run, you might get it after like 7 seconds with clicking a few buttons. Wait, what?
But let me catch you right here! At least for our main example in today’s post, this is a typical behaviour for this type of „problem source“. We will get to that in a few seconds, but let’s first talk about the System.ExecutionEngineException in general.
What is an ExecutionEngineException?
In general – as already mentioned at the start – the System.ExecutionEngineException is a pretty mean and „horrible things failed“ type of Exception. But why is that? Well, there can be many different sources throwing that exception. The mean thing is, that it’s very hard to debug, at least most of the time. This is, because you will most likely have a bad luck finding the actual source of this Exception.
Lemme show you the „Why is it hard to determine“ real quick! For this, please take a look at the next image(s) showing a typical occurence of the ExecutionEngineException. When looking at the marked sections, you will see some bad stuff – at least for us .NET developers – going on. „Why“, do you ask?
But why is it so hard to solve?
Well, because in my and many other cases:
- There is no StackTrace to build up upon
- No InnerException is available
- The origin is rather „unspecific„
- It occured unregularly
Mostly no StackTrace
Even though I can’t guarantee it like a 100%, I personally never had a StackTrace attached, when stumbling over this Exception type. Feel free to tell me about a situation, where you actually had a StackTrace available – which obviously would make it so much easier to fight this Exception.
When thinking about for example a „FileNotFoundException“ you already know like „Hey, I was very likely trying to open a file, which obviously didn’t exist“. Or when thinking about an „ArgumentException“, you would be like: „Hey, I called a method, which expected an argument in a specific form, which I didn’t correctly provide“.
No InnerException available
InnerExceptions usually define some sort of root cause when being a (child-) part of another Exception. This can help you identify the problem or the „actual“ cause of an occuring error. Sadly, I never had an ExecutionEngineException containing an InnerException, making it almost impossible finding the root cause behind it. Again: Feel free to post information on your personal experiences with this Exception, if you have some!
Unspecific origin
When talking about an unspecific origin, I’m thinking about the actual „debugger stop point“, like you can see in the image above. Even though the debugger directed me there, I experienced at least 3 other stop points, making it pretty unreliable / hard to find out, what the actual problem was. Thank god, I could actually find my personal source of failure considering my ExecutionEngineException occurence, when looking further into that „User32.PeekMessageW“ stuff.
Unregular occurence
As listed in the introduction of this section, I already mentioned, that the occurences can sometimes be some sort of „unregular“, if not even kinda „random“. I mean, at least it feels like, right? In my personal example, the Exception was thrown like after a few seconds, without doing anything. But in my next test run, it was totally different – this doesn’t really help finding the problem.
Take a look at this image here, which was risen from the exact same root cause, even though being displayed differently:
Investigating the ExecutionEngineException
The aspects mentioned above, are the exact reasons, why I thought, I would like my fellow .NET Developers know, how I personally fixed my individual problem case. Feel free to add more information in the comments, so we can aggregate different solutions, helping other .NET Developers.
Maybe one of the following steps can help you solve your special case of the ExecutionEngineException.
Tip 1 – Narrowing down possible error sources
Even if it sounds pretty obvious, you should first try to narrow down the possible error sources. You could therefore try to exctract the specific parts out of your application, that could possibly be running the problematic code.
For sure, this can be a pretty big and time consuming adventure, but it’s probably the „easiest“ way, in it’s core. If you’re developing your applications with a clean architecture, this should be easier than having a rather chaotic, non unit-testable application structure. This – of course –, is, why I always tell my students and customers, that a clean software architecture is important.
Tip 2 – The little things do matter
Even if you don’t get pretty much information, I mean, just like demonstrated above. Taking a look at the images, again, there wasn’t much information at first sight. I mean, no StackTrace, no InnerException, what else we do actually have?
In one screenshot, there was pretty much nothing, just a generic delegate-ish call to another like sub-method. Nothing really important, right?
But taking a more detailed look at the other image, there was at least some portion of information. Even though it isn’t some sort of „This here, is the root problem / cause“, we at least get anything! Looking at this image, we can see, that there was some stuff:
This will lead us to my personal fix, in the soon to follow section, in this post.
Tip 3 – Focus on the interesting stuff
When encountering such extraordinary Exceptions, you should – you guessed right – actually look for the interesting / extraordinary codes / sections of your application. I mean, most likely, a typical „SomeControl.Text = xy“ won’t pretty much raise these hardcore exceptions. Contrary to that, some native like „C code calls“ will, at least probably!
So try to focus on those things and extract them like mentioned in step 1. Often times, native method calls or such alike, are a common root cause – and it actually was in my personal case, as well!
Tip 4 – Review recent code changes
Even though this being a typical problem solving aspect in general, don’t forget about it here, as well. If you never had such an exception before, check, if someone actually changed the (possibly) related code. This can help with digging deeper and finding out the real trouble.
At best, you should like be using some sort of version control system / software like git – which’s usage is integrated in Visual Studio. This makes it easier to go back in time and taking a look at specific code regions without breaking anything in an instant.
Tip 5 – Third-Party Libraries
The funny thing considering the „third party libraries“ aspect is, that I encountered this problem while working on my freely available „rskibbe.I18n“ NuGet packages. So if I were to push / upload this problematic code (which I obviously wouldn’t, at least, intentionally), you could possibly encounter this error based on my third party code. So keep in mind, that you actually check third party dependencies and exclude them like for testing purposes.
Tip 6 – Platform specific problems
Sometimes these kind of deeper errors occur, when there is some sort of platform specific / implementation error. If you’re for example developing a cross platform app, which is running on different platforms, like: Windows, Linux, MacOS – don’t forget about considering these things during your research.
Tip 7 – Look at the Event Viewer
The Event Viewer provided by your Windows based OS could help as well. Run (for example) the „eventvwr“ command or just type like „event viewer“ inside the Windows search bar, which should pop it up as well. There you will have different possibilities of checking error protocols giving hints about things, that possibly went wrong.
Tip 8 – Check your hardware
If nothing of the previous stuff could help, you should potentially check your hardware for problems. Especially, if the problems are occuring more or less randomly and can’t be limited to some sort of special „place“. Use common tools like the CLI (Command Line) tool called „mdsched“ for your RAM or like „chkdsk“ for your file system / harddrive.
Fixing my personal problem case
After I tried – and hopefully somehow succeeded – explaining the evilness of the ExecutionEngineException to you, we will now come to the fix of my personal case. If you (sadly) had no success with my solving tips I’ve listed in the section above, you can potentially get some insights from my successful approach, here.
Following the important tips
For my situation (which is shown in the images in the „What is..“ section), I mainly followed tips number 2 and 3, being:
- The little things do matter
- Focus on the interesting stuff
The little things that did matter were, that I saw (and obviously somehow integrated myself) some sort of native functionality erroring at „User32.PeekMessageW“. This led to some thinking process like: „Hmm, where did I implement / do something considering a message loop“ and „This has to do with native calles regarding message loops, which are usually used in like Windows, WndProc stuff, etc.“.
Determining the problematic root
After that – as mentioned by the third tip – I focused on the interesting stuff, of course, in combination with my last thinking process. So I got to a spot, where I was declaring the usage of native „user32“ code (with some extra time and thinking circles, of course…):
public delegate IntPtr CBTProc(int code, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern IntPtr SetWindowsHookEx(WindowsHooks hookType, CBTProc hookProc, IntPtr instancePtr, uint threadID);
Don’t get me wrong, the code itself is pretty much okay / nothing inherently wrong with it, at least with the definition. But I later found out, that the problem lied within the actual call of that native code!
The root cause, disclosed
I won’t go too deep on each point here, but I’ll try to describe the important stuff around it. At the end of my problem solving approach, I ended up here, where I was calling the („imported“) native function „SetWindowsHookEx“. Take a close look on the „Callback“ function, which I’ve passed to the „SetWindowsHookEx“ call!
// called somewhere.. <someVariableStoredOutside> = NativeMethods.SetWindowsHookEx(WindowsHooks.WH_CALLWNDPROC, Callback, IntPtr.Zero, NativeMethods.GetCurrentThreadId()); // defined method somewhere near static IntPtr Callback(int code, IntPtr wParam, IntPtr lParam) { // some implementation // some other irrelevant stuff here return NativeMethods.CallNextHookEx(IntPtr.Zero, code, wParam, lParam); }
This little guy, the „Callback“ passed to the „SetWindowsHookEx“ function, was the root cause of my problem. Not, that it was inherently wrong, no! But the problem here was / is, that the GarbageCollector „cleaned it up“, before it’s job was actually done! This caused the ExecutionEngineException!
The final fix, thank god!
So, to achieve the final fix of this problem, I just had to keep an instance-reference to this „Callback“ delegate, alive. This would keep it alive and the GC (Garbage Collector) didn’t toucht it anymore. Look at this fixed / working code.
Keep in mind though, that I didn’t want to make it more complex, so I excluded some parts. Your focus should just lie on the „Keep an instance of the delegate alive – in a correct scope“. This was done by the variable „myHookProc“ being then passed to the „SetWindowsHookEx“ function as second parameter.
// defining a delegate type - like before public delegate IntPtr CBTProc(int code, IntPtr wParam, IntPtr lParam); // import the native method - like before [DllImport("user32.dll")] public static extern IntPtr SetWindowsHookEx(WindowsHooks hookType, CBTProc hookProc, IntPtr instancePtr, uint threadID); // defining a callback - like before static IntPtr Callback(int code, IntPtr wParam, IntPtr lParam) { // some implementation // some other irrelevant stuff here return NativeMethods.CallNextHookEx(IntPtr.Zero, code, wParam, lParam); } // here's the change!! // create an instance staying alive in the correct scope // in my case, static was fitting... // this is the key!! static CBTProc myHookProc = new CBTProc(Callback); // NOW, when calling this, pass the delegate instance, which now stays alive // and doesn't get GC collected resulting in an error SetWindowsHookEx(WindowsHooks.WH_CALLWNDPROC, myHookProc, IntPtr.Zero, GetCurrentThreadId());
Conclusion
In the end, an ExecutionEngineException is a serious and pretty much uncommon type of exception. It therefore only occures in more critical situations, where a problem with the execution engine itself has happened. This is often related to low-level issues in the Common Language Runtime (CLR) or the underlying hardware itself.
In my case, it was related to some native code call, which is pretty common, to sometimes be more complicated. I found the root of the problem by following tips 2 & 3 of my tips list and could finally solve it.