Friday, May 26, 2006 5:01 PM
codingsanity
SafeHandle (Reliability Part 2)
Right, now that we've got the basics of reliability covered, we can move on to SafeHandle. If you've ever done any interop you should know all about handles. They're effectively pointers to internal OS resources. Pretty much everything you do in Windows has a handle involved somewhere. If you open a file, the OS gives you a file handle that you pass into functions that read/write etc from that file. If you have a window it's got a handle, and it's device context has a handle too. Happily enough, .NET hides all this from you.
Sometimes, however, you need some functionality that the .NET classes just don't offer. When this happens you inevitably need to use and abuse these Windows handles. The problem is that many people don't close the handles properly, and even those that do often don't handle error conditions gracefully, ensuring that the handle is closed. The problem is that losing handles is effectively a memory leak. The reason for this is that many handles actually represent data stored in the kernel or UI subsystem about the thing you're messing with. If you don't close that handle, that memory may not be released. In addition it may not be easy to notice since it's kernel memory not app memory.
Luckily the OS is pretty good at shutting down handles used by processes once they finish. All well and good unless you're running a high-availability service. Worse if you're using these handles inside something like SQL Server. So, what's the solution here? Well, ideally you'd go off and write a disposable class using critical finalizers to control access to your handle. Now, even in the dire circumstances of a massive failure in your code or environment, your handle will likely be released. Bit of a pain for a little extra reliability though.
Enter SafeHandle
So, MS have provided us with a little class that does all that for you. It's called SafeHandle, and can be found in the System.Runtime.InteropServices namespace. It inherits from CriticalFinalizerObject, and places reliability guarantees on most of it's methods. The idea is that you wrap your handle inside a SafeHandle subclass, and it will ensure that it gets cleaned up OK. There are two subclasses of SafeHandle (which are also abstract) called SafeHandleMinusOneIsInvalid, and SafeHandleZeroOrMinusOneIsInvalid. They can be found in the Microsoft.Win32.SafeHandles namespace.
The reason for these subclasses is the wacky world of Windows handles. When a handle is returned from a function, sometimes 0 is returned to indicate failure, and sometimes -1 is given for a failure. Depending on the type of handle their failure indicators could be different. Basically these subclasses of SafeHandle just override the IsInvalid property of SafeHandle with the relevant check.
So, what's required when implementing a SafeHandle or one of it's derived subclasses? Basically all you have to do is implement the ReleaseHandle method. Since SafeHandle doesn't know what kind of handle it's dealing with, it doesn't know how to close it, so you have to provide it that logic. ReleaseHandle implements a ReliabilityContract, so you have to stick to the limitations I discussed in my earlier post.
Type Safety & Interop
Another thing about handles is that since they're generally (up to now) passed around as IntPtrs, there's no type safety. Nothing stops you from passing a window handle to a function expecting a file handle. Oh, your code will fail, but it'll be a runtime failure, not compile time. Interop has a great new feature where you can declare an interop method as taking a SafeHandle. Interop will know to actually cast it to the relevant underlying HANDLE type. This allows you to declare your interop methods in a type safe manner.
As an example, I wanted to be notified when a certain registry key changed. Windows has a function called RegNotifyChangeKeyValue where it can block until a change occurs, or signal an event. Unfortunately the RegistryKey class doesn't offer this functionality. So I created a SafeRegistryHandle class that inherited from SafeHandleZeroOrMinusOneIsInvalid, and put a call to RegCloseKey in the ReleaseHandle override. Then I created an interop declaration on my class that looked like this:
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegNotifyChangeKeyValue(SafeRegistryHandle hKey, bool watchSubtree, int dwNotifyFilter, SafeWaitHandle hEvent, bool fAsynchronous);
So now I have a type-safe declaration for RegNotifyChangeKey, and I'm assured that the handle I've opened will be closed (given the caveats for Constrained Execution Regions).
If you go and look in Reflector you'll see that the RegistryKey class uses a SafeRegistryHandle class itself. Unfortunately it's an internal class so I had to reinvent the wheel a touch, but the point is that this technique of using SafeHandles should be your method of choice when dealing with OS handles.
Filed under: Interop, Code