Working with unmanaged memory is not my forte. Thus I am very appreciative of when someone not only reads my blog, but then takes the time to do their own research and leave comments and suggest updates. TLDR: Thank you, Steven Pereyda!
Last year I blogged about how to kill a child process when the parent process is killed. The solution involves using the operating system by invoking Kenral32. However, as mentioned above, there was a small memory leak and a few other optimizations that we should have made; so let's take a look at how to fix those!
Fixes and Optimizations in v2
1. There was a small memory leak.
In the job constructor we used Marshal.AllocHGlobal to create an unmanaged copy of the JobObjectExtendedLimitInformation object, but we never freed that memory. The new constructor how has a finally block that ensures we invoke Marshal.FreeHGlobal.
2. We should use SafeHandles.
.NET has a SafeHandle class that can be used as a wrapper around unmanaged handles, which can help you prevent memory leaks; and I learned how to use this by reading a code project article by the always awesome Stephen Cleary. Please note that in v2 there is a now a JobObjectHandle class that extends SafeHandle, and we use this instead of storing the IntPtr ourselves.
3. We moved the GC.SuppressFinalize into the public dispose method.
Yes, this is the right way to do it. So why didn't I do it that way the first time? Because ReSharper was warning me that I wasn't using the isDisposing param...and hate warnings!
4. Follow the best practices of hosting all external calls in a single Native Methods class.
Microsoft recommends that you keep all of your external calls in one NativeMethods class, and then decorate that class with attributes to avoid security warnings. Sounds like a good idea to me.