IDisposable_ What Your Mother Never Told You About Resource Deallocation - CodeProject.pdf

download IDisposable_ What Your Mother Never Told You About Resource Deallocation - CodeProject.pdf

of 33

Transcript of IDisposable_ What Your Mother Never Told You About Resource Deallocation - CodeProject.pdf

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 1/33

    Articles Platforms, Frameworks & Libraries .NET Framework General

    Stephen Cleary, 10 Nov 2014 BSD

    IDisposable: What Your MotherNever Told You About ResourceDeallocation

    One difficulty of the IDisposable interface overcome with theDisposable Design Principle.

    Introduction - RecommendedReadingA lot of the information regarding .NET internals in this article wasgleaned from "CLR via C#", 2nd Ed., by Jeffrey Richter of Wintellect,Microsoft Press. If you don't already own it, buy it. Now. Seriously. It'san essential resource for any C# programmer.

    The Boring Stuff - IDisposableGone AwryThis article is divided into two parts: the first part is an evaluation ofmany of the problems inherent with IDisposable, and the secondpart develops some "best practices" when writing IDisposablecode.

    Deterministic Resource Deallocation Its Necessity

    During more than 20 years of coding, I have occasionally found itnecessary to design my own small programming languages forcertain tasks. These have varied from imperative scripting languages

    4.96 136 votes

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 2/33

    to a specialized regex for trees. There are many choices to make inlanguage design: a few simple rules should never be broken, andthere are many general guidelines. One of these guidelines is:

    Never design a language with exceptions that do notalso have deterministic resource deallocation.

    Guess which guideline the .NET runtime and, by extension, every.NET language does not follow? This article will examine some of theconsequences of this choice, and then propose some best practicesto try to work with this situation.

    The reason for the guideline is that deterministic resourcedeallocation is necessary to produce usable programs withmaintainable code. Deterministic resource deallocation provides acertain point at which a coder may know that the resource has beendeallocated. There are two approaches to writing reliable software:the traditional approach deallocates resources as soon as possible,while a modern approach lets resource deallocation occur at anindeterminate time. The advantage to the modern approach is thatprogrammers do not have to deallocate resources explicitly. Thedisadvantage is that it becomes much more difficult to write reliablesoftware; an entire new set of difficulttotest error conditions isadded to the allocation logic. Unfortunately, the .NET runtime wasdesigned around the modern approach.

    The .NET runtime's support for indeterministic resource deallocationis through the Finalize method, which has special meaning to theruntime. Microsoft has also recognized the need for deterministicresource deallocation, and has added the IDisposable interfaceand other helper classes which we will look at later. However, theruntime views IDisposable as just another interface, without anyspecial meaning; and this reduction in status to secondclass citizencauses some difficulties.

    In C#, a "poor man's deterministic deallocation" may be implementedthrough the use of try and finally, or the notmuchcleanerequivalent of using. There was a lot of debate in Microsoft whetherto have referencecounted references or not, and I personally believethey made the wrong decision. As a result, deterministic resourcedeallocation requires the use of either the awkward finally/using,or the errorprone direct call to IDisposable.Dispose. Neither ofthese are particularly attractive to the programmer of reliablesoftware who is used to, e.g., C++'s shared_ptr.

    The IDisposable Solution

    IDisposable is Microsoft's solution for allowing deterministicresource deallocation. It is intended to be implemented in thefollowing use cases:

    Any type that owns managed IDisposable resources. Note

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 3/33

    that the containing type must own the resources, not just referto them. One difficulty here is that it is not obvious whichclasses implement IDisposable, requiring programmers toconstantly look up each class in the documentation. FxCop is ahelp in this situation, and will flag the programmer's code ifthey forget.Any type that owns unmanaged resources.Any type that owns both managed and unmanaged resources.Any type that derives from an IDisposable class. I don'trecommend deriving from types that own unmanagedresources; it is usually a cleaner objectoriented design to havesuch types as fields rather than base types.

    IDisposable does provide for deterministic deallocation; however,it also comes with its own set of problems.

    IDisposable's Difficulties Usability

    IDisposable objects are cumbersome to use correctly in C#.Anyone who uses an IDisposable object locally must know to wrapit in a using construct. What makes this awkward is that C# does notallow using to wrap an object that isn't IDisposable. So, for everyobject being used in a deterministic program, the coder mustdetermine if using is necessary, either from continualdocumentation lookups, or by wrapping everything in using andremoving the ones causing compilation errors.

    C++ is slightly better in this regard. They support stack semantics forreference types, which has a logic equivalent to inserting a usingonly if necessary. C# would benefit from allowing using to wrapnonIDisposable objects.

    This problem with IDisposable is an enduser problem: it can bemitigated through code analysis tools and coding conventions,although there is no perfect solution. Adding to this problem is thefact that if resources are disposed at an indeterminate time i.e., thecoder forgot using when it was required, then the code may workfine during testing, and inexplicably fail in the field.

    Depending on IDisposable instead of referencecountedreferences also brings up the problem of ownership. When C++'sreferencecounted shared_ptr's last reference goes out ofscope, then its resources are disposed immediately. In contrast,IDisposablebased objects place the burden on the enduser toexplicitly define which code "owns" the object, and is thereforeresponsible for disposing of its resources. Sometimes, ownership isobvious: when an object is owned by another object, the containerobject also implements IDisposable and is, in turn, disposed by itsowner. In another very common case, the lifetime of an object can bedefined by its code scope at some point in the program, and theenduser places a using construct to define the block of code that"owns" that object. However, there are a few other cases where the

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 4/33

    object lifetime may be shared by different code paths, and these aremore difficult for the enduser to code correctly whereas areferencecounted reference would give this problem a simplesolution.

    IDisposable's Difficulties Backwards Compatibility

    Adding IDisposable to or removing IDisposable from aninterface or class is a breaking change. Proper design implies that theclient code will primarily use interfaces, so IDisposable could beadded to an internal class in that situation, bypassing the interface.However, this can still cause a problem with older client code.

    Microsoft ran into this problem themselves. IEnumerator did notderive from IDisposable; however, IEnumerator did. Now,when older client code expecting the IEnumerable collections isgiven IEnumerable collections instead, their enumerators arenot correctly disposed.

    Is this the end of the world? Probably not, but it does open up somequestions about how the secondclass citizen status of IDisposableinfluences design choices.

    IDisposable's Difficulties Design

    The single greatest drawback caused by IDisposable in the area ofcode design is this: every interface designed must predict whether ornot their derived types will need IDisposable. The actual quote is,"Implement the Dispose design pattern on a base type thatcommonly has derived types that hold onto resources, even if thebase type does not" from Implementing Finalize and Dispose toClean Up Unmanaged Resources. From a design perspective,interfaces need to predict whether implementations of that interfacewill need IDisposable.

    If an interface does not inherit from IDisposable, yet one of itsimplementations needs it e.g., an implementation from a thirdpartyvendor, then the enduser code encounters a "slicing" problem. Theenduser code does not realize that the class it is using needs to bedisposed. The enduser code may cope with this problem by testingobjects it is using for the IDisposable interface explicitly; however,this changes the awkward using into a truly horriblelookingfinally construct for each and every abstract local object. Placingthis kind of burden on the end user is, in my opinion, unacceptableat least without language support.

    This "slicing" problem is the generalization of the problem Microsofthad when updating IEnumerator; they decided that enumeratorsoften need to free resources, so they added IDisposable toIEnumerator. However, adding IDisposable to a derived typecan result in slicing when the enduser code uses the old

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 5/33

    IEnumerator interface.

    For some interfaces, it's rather obvious whether their derived typeswill need IDisposable. For other interfaces, however, it's not nearlyas clear, and yet the decision must be made when the interface ispublished. In the general case, this is a real problem.

    In short, IDisposable prevents designing reusable software. Theunderlying problem causing all this difficulty is the violation of one ofthe central principles of Object Oriented Design: keep the interfaceseparate from the implementation. The type of resources allocatedand deallocated internally by an implementation of an interfaceshould be an implementation detail. However, when Microsoft madethe decision to only support IDisposable as a secondclass citizen,they were deciding to view the deallocation of resources as aninterface instead of an implementation detail. They were wrong, andthese difficulties are the result of violating the principle of separation.

    There is one rather unattractive solution: have every interface andevery class descend from IDisposable. Since IDisposable may beimplemented as a noop, it really only means that the derivedinterface or class may have an implementation, derived class, orfuture version that does deallocate resources. I personally have notbeen brave enough to embrace this as a design guideline yet.

    The final difficulty regarding the IDisposable design is how theyinteract with collections. Since IDisposable is an interface, eithercollections must behave differently when they "own" their items, orthe enduser must remember to invoke IDisposable.Disposeexplicitly, when necessary. If this responsibility is placed on thecollection classes, then that would imply a new set of collectionclasses that "own" their items; and duplicating a class hierarchy is ared flag to any designer, indicating that there is something wrong. If.NET supported referencecounted references i.e., IDisposable as afirstclass citizen, then none of this would be a problem.

    IDisposable's Difficulties Additional Error State

    Another difficulty with IDisposable is that it is explicitly invokeable,and not tied to the lifetime of the object. In particular, this adds anew "disposed" state to every disposable object. With the addition ofthis state, Microsoft recommends that every type implementingIDisposable will check in every method and property accessor tosee if it has been disposed, and throw an exception if it has. Ewww...This reminds me of a coworker I once had who insisted that we runmemory checksum algorithms every time we allocate memory, "justin case" the RAM is about to fail. In my opinion, checking for thedisposed state is just a waste of cycles, and would only be useful indebug code. If end users can't follow even the most basic softwarecontracts, they won't ever produce working code anyway.

    Instead of checking for the disposed state and throwing an

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 6/33

    exception, I recommend supporting "undefined behaviour".Accessing a disposed object is the modern equivalent of accessingdeallocated memory.

    IDisposable's Difficulties No Guarantees

    Since IDisposable is just an interface, objects implementingIDisposable only support deterministic deallocation; they cannotrequire it. Since it is considered perfectly acceptable for the enduserto not dispose of an object a convention I disagree with, anyIDisposable object must support extra logic to handleindeterministic deallocation as well as deterministic deallocation.Once again, indeterministic deallocation is the standard that every.NET object must support, and deterministic deallocation is just anoptional addition that cannot be enforced. True enforcement ofdeterministic resource deallocation would require referencecountedreferences.

    IDisposable's Difficulties Complexity ofImplementation

    Microsoft has a code pattern for implementing IDisposable. Notmany coders fully understand this code e.g. why a call toGC.KeepAlive is necessary, and why synchronization of thedisposed field is not. There are several other articles that describein detail how to implement this code pattern. The following are thereasons behind its somewhat obscure design:

    It's possible that IDisposable.Dispose may never be called,so the disposable object must include a finalizer that disposesresources. In other words, deterministic deallocation mustsupport indeterministic deallocation.IDisposable.Dispose may be called multiple times withoutadverse sideeffects, so any real disposal code needs to beprotected by a check that it has not already run.Because finalizers are run on all unreachable objects in anarbitrary order, finalizers may not access managed objects.Therefore, the resource deallocation method must be able tohandle both a "normal" dispose when called fromIDisposable.Dispose and an "unmanagedonly" disposewhen called from Object.Finalize.Since finalizers run on a separate threads, there is thepossibility that the finalizer may be called beforeIDisposable.Dispose returns. Judicial use ofGC.KeepAlive and GC.SuppressFinalize may be used toprevent race conditions.

    In addition, the following facts are often overlooked:

    Finalizers are called if constructors throw an exception anotherconvention I disagree with; therefore, the disposal code must

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 7/33

    gracefully handle partiallyconstructed objects.Implementing IDisposable on aCriticalFinalizerObjectderived type is tricky becausevoidDispose(booldisposing) is virtual, yet it mustrun within a Constrained Execution Region. This may require anexplicit call to RuntimeHelpers.PrepareMethod.

    The naming convention for the recommended IDisposable codepattern is confusing at best. The object is implementing the interfaceIDisposable, and needs a boolean field disposed. So far so good,but then: as part of the code pattern, the implementation ofIDisposable.Dispose calls the overloaded Dispose with aboolean disposing parameter that indicates what type of disposingcan take place not whether disposing is taking place. The namingconventions for this code pattern are confusing, even for veteran C#programmers, if they aren't constantly reviewing the code pattern.Any deviation is flagged by FxCop as a violation of this code pattern.

    This is one area where C++ again has a bit of an edge on C#. TheC++ compiler does much of the work of implementingIDisposable due to its destructor syntax. C# is a pure .NETlanguage, so in many ways, has a more natural syntax than C++.However, C++ does have two helpful advantages when it comes todeterministic resource deallocation: destructor syntax, and stacksemantics for reference types.

    Even for the perfect programmer, the complexity of therecommended IDisposable code pattern increases the possibilityof incorrect code written in libraries or by coworkers. It is a naturalimpulse to place shutdown logic within a Dispose method. However,since we cannot touch managed objects when in finalizers, and sinceIDisposable only supports deterministic deallocation rather thanenforcing it, this is often a mistake. The recommended IDisposablecode pattern can only be used to deallocate resources; it cannot beused to support general shutdown logic. This fact is all too oftenforgotten.

    Sometimes, for simplicity or by accident, IDisposable is justforgotten. FxCop catches some of these violations such as thecommon case where an object contains an IDisposable object, butit misses other cases. Microsoft coders themselves have fallen intothis trap: WeakReference does not implement IDisposable, and itcertainly should. Programmers who do not believe in the necessity ofdeterministic resource deallocation may simply ignore IDisposablealtogether.

    IDisposable's Difficulties Impossibility ofShutdown Logic Managed Finalization

    Shutdown logic is a common need in any realworld application. Thisis particularly true in asynchronous programming models. For

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 8/33

    example, a class that has its own child thread would wish to stop thatthread by setting a ManualResetEvent. While it is perfectlyreasonable and expected to do this when IDisposable.Dispose iscalled directly, this would be disastrous if called from a finalizer. SinceIDisposable does not enforce deterministic deallocation, there isnot even a warning to the enduser who forgets or neglects to callIDisposable.Dispose; their program just slowly leaks resources.This raises the question: is there any way for the finalizer code toaccess managed objects?

    In order to better understand the restrictions on finalizers, we mustunderstand the garbage collector. [Note: The description of garbagecollection and finalization given here is a simplification; the actualimplementation is considerably more complicated. Generations,resurrection, weak references, and several other topics are ignored.For the purpose of this article, however, this logical description iscorrect and reasonably complete.]

    The .NET garbage collector utilizes a mark/sweep algorithm.Specifically, it does the logical equivalent of the following:

    1. Suspends all threads except the finalizer threads.2. Creates a set of "root" objects. If the AppDomain is unloading

    or the CLR is shutting down, then there are no root objects. Fornormal garbage collections, the root objects are:

    Static fields.Method parameters and local variables for the whole callstack of each thread, unless the current CLI instructionhas already moved past the point of their last accesse.g. if a local variable is only used for the first half of afunction, then it is eligible for garbage collection for thesecond half of the function note that the this pointeris included here.Normal and pinned GCHandle table entries these areused for Interop code, so the GC doesn't remove objectsthat are referenced only by unmanaged code.

    3. Recursively marks each root object as "reachable": for eachreference field in the reachable object, the object referred toby the field is recursively marked as reachable if it isn'talready.

    4. Identifies the remaining, unmarked objects as "unreachable".5. Recursively marks each unreachable object with a finalizer that

    hasn't had GC.SuppressFinalize called on it as reachable,and places them on the "finalization reachable queue" in amostlyunpredictable order.

    In parallel with the garbage collection above, finalization is alsoconstantly running in the background:

    1. A finalizer thread takes an object from the finalizationreachable queue, and executes its finalizer note that multiple

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 9/33

    finalizer threads may be executing finalizers for differentobjects at any given time.

    2. The object is then ignored; if it is still reachable from anotherobject on the finalization reachable queue, then it will be keptin memory; otherwise, it will be considered unreachable, andwill be collected at the next garbage collection sweep.

    The reason that finalizers may not access managed objects is becausethey do not know what other finalizers have been run. Any objectthat has fields to other objects may access those fields, since theother objects will still be in memory, but nothing may be done withthem, since they may have already had their finalizers run thusdisposing them. Even calling Dispose would be an error, sinceDispose may already be running in the context of another finalizerthread. Calling Dispose would be meaningless anyway, since thoseobjects are either reachable from live code, or are already on thefinalization reachable queue. Also note that in the case of theAppDomain unloading or the CLR shutting down, all objects becomeeligible for garbage collection, including CLR runtime support objectsand static reference fields; not even static methods such asEventLog.WriteEntry may be called in this situation.

    There are a handful of exceptions where finalizers may accessmanaged objects:

    The finalization reachable queue is partially ordered: finalizersfor CriticalFinalizerObjectderived types are calledafter finalizers for nonCriticalFinalizerObjectderivedtypes. This means that, e.g. a class with a child thread may callManualResetEvent.Set for a containedManualResetEvent, as long as the class does not derive fromCriticalFinalizerObject.The Console object and some methods on the Thread objectare given special consideration. This explains why exampleprograms can create an object calling Console.WriteLine inits finalizer and then exit, but the same program won't workwith EventLog.WriteEntry.

    Generally speaking, finalizers may not access managed objects.However, support for shutdown logic is necessary for reasonablycomplex software. The Windows.Forms namespace handles this withApplication.Exit, which initiates an orderly shutdown. Whendesigning library components, it is helpful to have a way ofsupporting shutdown logic integrated with the existing logicallysimilar IDisposable this avoids having to define anIShutdownable interface without any builtin language support.This is usually done by supporting orderly shutdown whenIDisposable.Dispose is invoked, and an abortive shutdown whenit is not. It would be even better if the finalizer could be used to doan orderly shutdown whenever possible.

    Microsoft came up against this problem, too. The StreamWriter

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 10/33

    class owns a Stream object; StreamWriter.Close will flush itsbuffers and then call Stream.Close. However, if a StreamWriterwas not closed, its finalizer cannot flush its buffers. Microsoft "solved"this problem by not giving StreamWriter a finalizer, hoping thatprogrammers will notice the missing data and deduce their error. Thisis a perfect example of the need for shutdown logic.

    A Brief Hiatus Where We Are

    "But, of course, this is all far too messy. It runs counterto the goals for our new managed platform to forcedevelopers to worry about this sort of thing." ChrisBrumme, "Lifetime, GC.KeepAlive, handle recycling",blog post 20030419

    Ahh, yes. .NET sure has made it easy.Why, unmanaged destructors in C++ are so much more complicatedthan all of this. Seriously, this article could be muchlonger if it included the really complex issues such as resurrectionand the restrictions on finalizers that descend fromCriticalFinalizerObject.

    I'd like to take a moment to sing some praises of .NET and C#.Although I do disagree with a couple of Microsoft's decisions, on thewhole, they've done an excellent job. I'm a huge fan of any languagethat brings the procedural and functional families together in a moresynergetic union, and C# does an excellent job of that. The .NETFramework and runtime have a few rough corners, but overall, they'rebetter than what has come before, and they're obviously the waythings are going. So far, I've been pointing out the problems causedby IDisposable, and from this point forward, I'll start looking atsolving a couple of these problems.

    The fact is, IDisposable is now builtin to .NET languages thoughnot the runtime, and any solution needs to make use of thisinterface. We're stuck with it, so to speak, so let's make the best of it.

    The (Hopefully) Not-So-BoringStuff - IDisposable RegulatedThe first part of this article discussed the difficulties of IDisposable;this part will look at some "best practices" when writingIDisposable code.

    Solving IDisposable's Difficulties Minimize UseCases of IDisposable by Utilizing the DisposableDesign Principle

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 11/33

    One reason for the complexity of Microsoft's recommendedIDisposable code pattern is because they try to cover too manyuse cases. A bit of discipline in the design of IDisposable classeswill go a long way:

    For each unmanaged resource, create exactly one possiblyinternal IDisposable class that is responsible for freeingit. Microsoft followed this principle thoroughly in the BCLimplementation. Note that a wrapper type for an unmanagedresource is considered a managed resource.Never derive from an unmanaged resource wrapper type.Create other managed IDisposable types that either ownmanaged resources and/or derive from a type that ownsmanaged resources.Under no circumstances create a type that has to considerboth managed and unmanaged resources, when implementingIDisposable. This greatly simplifies the implementation,reducing possible errors.

    The Disposable Design Principle is built on these ideas:

    Level 0 types directly wrap unmanaged resources. These typesare generally sealed.Level 1 types are types that derive from Level 1 types and/orcontain field members that are Level 0 or Level 1 types.

    To expound on this design principle, the small private or internalLevel 0 classes that wrap unmanaged resources should be as close tothe native API as possible, and should only concern themselves withdisposing the resource correctly. All other APIs should be provided ina Level 1 class that has a Level 0 field member. This would result intwo looselyrelated classes or class hierarchies: one is onlyresponsible for wrapping the unmanaged resource, and the otheronly has to refer to a managed resource. This reduces our use casesfor IDisposable to only two:

    1. Level 0 types: only deal with unmanaged resources.2. Level 1 types: only deal with managed resources defined by a

    base type and/or in fields.

    Implementing IDisposable on Level 1 types is rather simple: justimplement IDisposable.Dispose as calling Dispose on anyIDisposable field, and then, if this type is derived from anIDisposable type, call base.Dispose. This is not the place forgeneral shutdown logic. Note the following for this simpleimplementation:

    Dispose is safe to be called multiple times because it is safeto call IDisposable.Dispose multiple times, and that's all itdoes.Level 1 type should not have finalizers; they wouldn't be ableto do anything anyway, since managed code cannot beaccessed.

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 12/33

    It is not necessary to call GC.KeepAlive(this) at the end ofDispose. Even though it is possible for the garbage collectorto collect this object while Dispose is still running, this is notdangerous since all the resources being disposed aremanaged, and neither this type nor any derived types havefinalizers.Calling GC.SuppressFinalize(this) is likewiseunnecessary because neither this type nor any derived typeshave finalizers.

    However, IDisposable is still difficult to implement correctly for thefirst use case. Due to the complexities of properly implementingIDisposable for unmanaged resources, it's actually best if we don'timplement it altogether. This can be accomplished through thediligent use of base types that handle the common logic, or throughthe use of helper classes that often remove the need forIDisposable.

    Solving IDisposable's Difficulties Helper Classesfor Avoiding Implementing IDisposable Directly

    It is very common to have to write an unmanaged resource wrapperclass for an unmanaged resource that is a pointer to some datastructure. For this common use case, a higherlevel abstraction isavailable through Microsoftprovided helper classes.System.Runtime.InteropServices.SafeHandle,System.Runtime.InteropServices.CriticalHandle, and theclasses in Microsoft.Win32.SafeHandles allow writing verysimple unmanaged resource wrappers if the unmanaged resourcemay be treated as an IntPtr. However, these are not supported onthe .NET Compact Framework; on that platform, I recommend writingyour own version of these extremely useful classes.

    Level 0 types, in the Disposable Design Principle, should alwaysderive from SafeHandle, if it is available on the target platform.SafeHandle and its derived classes have special P/Invoke support,which helps prevent leaking resources in some rare situations. Interopcode should define function parameters, and return types asSafeHandle or derived types rather than IntPtr. TheCriticalHandle class, in spite of the name, is actually less safe touse than SafeHandle, and should generally be avoided.

    The relationship between SafeWaitHandle and WaitHandle is aperfect example of the Disposable Design Principle:SafeWaitHandle is the Level 0 class, and WaitHandle is the Level 1class that provides the normal enduser API. SafeWaitHandle is inthe SafeHandle hierarchy, implementingSafeHandle.ReleaseHandle as a call to the Win32 CloseHandlefunction; it only concerns itself with how to free the resource. TheLevel 1 WaitHandle class, in contrast, is not in the SafeHandlehierarchy; and its hierarchy exposes a full API for waitable handles,

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 13/33

    such as WaitOne.

    This means there are four possibilities when having to write a newunmanaged resource wrapper in the order of ease ofimplementation:

    1. There is already a Level 0 type for the unmanaged resource. Inother words, the unmanaged resource is a pointer type that isalready covered by a class derived from SafeHandle.Microsoft has supplied several classes already, includingSafeFileHandle, SafePipeHandle, and SafeWaitHandle,among others. In this case, the programmer only needs tocreate a new Level 1 type.

    2. The unmanaged resource is a pointer type, but doesn't have asuitable Level 0 type already defined. In this case, theprogrammer needs to create two classes, one Level 0 and oneLevel 1.

    3. The unmanaged resource that needs wrapping is a simplepointer type along with some additional information such as asecondary pointer or integral "context" value. In this case, theprogrammer must also create two classes, but theimplementation details of the Level 0 type are more complex.

    4. The unmanaged resource is not a pointer type at all. In thiscase, the programmer must create two classes, and theimplementation details of both are much more complex.

    Note that when creating hierarchies of Level 1 types, it is commonpractice to declare a protected property in the possibly abstractbase Level 1 type, and this field should have the type and name ofthe related Level 0 type. For example, the Level 1 abstract base typeWaitHandle establishes the Level 1 hierarchy for waitable handles,and it has a protected property named SafeWaitHandle of typeSafeWaitHandle.

    Wrapping Unmanaged Resources Using Existing Level 0Types The Easy Case

    To define a new Level 1 type that uses a Level 0 type, extend anexisting Level 1 hierarchy, if possible.

    The example for using existing Level 0 SafeHandlederived types isManualResetTimer named to match the existingManualResetEvent. Of the many timers provided by the .NETframework, they did not include a WaitHandlebased timer that getssignalled when the timer goes off. This "Waitable Timer", as it iscalled by the SDK, is commonly used by asynchronous programs. Forsimplicity, this sample does not support periodic timers or timers withasynchronous callback functions.

    Note that ManualResetTimer derives from WaitHandle the Level1 hierarchy because the Level 0 SafeWaitHandle already correctlydisposes of the unmanaged resource. Because of the Level 0/Level 1

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 14/33

    class hierarchy division already in place, implementingManualResetTimer is quite straightforward.

    [SecurityPermission(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]internalstaticpartialclassNativeMethods{[DllImport("kernel32.dll",EntryPoint="CreateWaitableTimer",CharSet=CharSet.Auto,BestFitMapping=false,ThrowOnUnmappableChar=true,SetLastError=true),SuppressUnmanagedCodeSecurity]privatestaticexternSafeWaitHandleDoCreateWaitableTimer(IntPtrlpTimerAttributes,[MarshalAs(UnmanagedType.Bool)]boolbManualReset,stringlpTimerName);internalstaticSafeWaitHandleCreateWaitableTimer(IntPtrlpTimerAttributes,boolbManualReset,stringlpTimerName){SafeWaitHandleret=DoCreateWaitableTimer(lpTimerAttributes,bManualReset,lpTimerName);if(ret.IsInvalid)Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());returnret;}

    [DllImport("kernel32.dll",EntryPoint="CancelWaitableTimer",SetLastError=true),SuppressUnmanagedCodeSecurity][return:MarshalAs(UnmanagedType.Bool)]privatestaticexternboolDoCancelWaitableTimer(SafeWaitHandlehTimer);internalstaticvoidCancelWaitableTimer(SafeWaitHandlehTimer){if(!DoCancelWaitableTimer(hTimer))Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());}

    [DllImport("kernel32.dll",EntryPoint="SetWaitableTimer",SetLastError=true),SuppressUnmanagedCodeSecurity][return:MarshalAs(UnmanagedType.Bool)]privatestaticexternboolDoSetWaitableTimer(SafeWaitHandlehTimer,[In]reflongpDueTime,intlPeriod,IntPtrpfnCompletionRoutine,IntPtrlpArgToCompletionRoutine,[MarshalAs(UnmanagedType.Bool)]boolfResume);internalstaticvoidSetWaitableTimer(SafeWaitHandlehTimer,longpDueTime,intlPeriod,IntPtrpfnCompletionRoutine,IntPtrlpArgToCompletionRoutine,boolfResume){if(!DoSetWaitableTimer(hTimer,refpDueTime,lPeriod,pfnCompletionRoutine,lpArgToCompletionRoutine,fResume))Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());}}

    ///

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 15/33

    ///Amanualreset,nonperiodic,waitabletimer.///publicsealedclassManualResetTimer:WaitHandle{//////Createsanew.///publicManualResetTimer(){SafeWaitHandle=NativeMethods.CreateWaitableTimer(IntPtr.Zero,true,null);}

    //////Cancelsthetimer.Thisdoesnotchangethesignalledstate.///publicvoidCancel(){NativeMethods.CancelWaitableTimer(SafeWaitHandle);}

    //////Setsthetimertosignalatthespecifiedtime,///whichmaybeanabsolutetimeorarelative(negative)time.//////Thetime,interpreted///asavalueprivatevoidSet(longdueTime){NativeMethods.SetWaitableTimer(SafeWaitHandle,dueTime,0,IntPtr.Zero,IntPtr.Zero,false);}

    //////Setsthetimertosignalatthespecifiedtime.Resetsthesignalledstate.//////Thetimethatthis///timershouldbecomesignaled.publicvoidSet(DateTimewhen){Set(when.ToFileTimeUtc());}

    //////Setsthetimertosignalafteratimespan.Resetsthesignaledstate.//////Thetimespanafter///whichthetimerwillbecomesignaled.publicvoidSet(TimeSpanwhen){Set(when.Ticks);}}

    Note the following:

    Always use SafeHandle or derived types as parameters andreturn values for interop functions. For example, this samplecode uses SafeWaitHandle instead of IntPtr. This preventsresource leaks if a thread is unexpectedly aborted.Since a Level 1 hierarchy is already in place,ManualResetTimer doesn't have to deal with disposing, evenof its managed resources. This is all handled by the

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 16/33

    WaitHandle base type.

    Wrapping Unmanaged Resources Defining Level 0Types for Pointers The Intermediate Case

    There are many cases where a suitable Level 0 type doesn't exist.These situations require defining a Level 0 type and then defining aLevel 1 type or type hierarchy. Defining Level 0 types is morecomplicated than defining Level 1 types.

    The example for defining simple Level 0 types is a window stationobject. This is one of the many resources that is represented by asingle IntPtr handle. First, the Level 0 type must be defined:

    [SecurityPermission(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]internalstaticpartialclassNativeMethods{[DllImport("user32.dll",EntryPoint="CloseWindowStation",SetLastError=true),SuppressUnmanagedCodeSecurity][return:MarshalAs(UnmanagedType.Bool)]internalstaticexternboolCloseWindowStation(IntPtrhWinSta);}

    //////Level0typeforwindowstationhandles.///publicsealedclassSafeWindowStationHandle:SafeHandle{publicSafeWindowStationHandle():base(IntPtr.Zero,true){}publicoverrideboolIsInvalid{[ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]get{return(handle==IntPtr.Zero);}}

    [ReliabilityContract(Consistency.WillNotCorruptState,Cer.MayFail)][PrePrepareMethod]protectedoverrideboolReleaseHandle(){returnNativeMethods.CloseWindowStation(handle);}}

    Notes on the code:

    The unmanaged resource deallocation function in this case,NativeMethods.CloseWindowStation does take a regularIntPtr not a SafeWindowStationHandle to deallocate theresource.Since SafeHandle derives fromCriticalFinalizerObject, both IsInvalid andReleaseHandle may be run in a Constrained ExecutionRegion, meaning:

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 17/33

    They cannot allocate objects, box values, acquire locks,or call methods through delegates, function pointers, orReflection.They should be decorated with aReliabilityContractAttribute and aPrePrepareMethodAttribute.

    Both IsInvalid and ReleaseHandle may be run from afinalizer during system shutdown, so they may not access anymanaged objects whatsoever.

    Since a Level 0 type's ReleaseHandle only P/Invokes its resourcecleanup function and returns, the Constrained Execution Region andfinalizer restraints are not troublesome in practice. The onlyawkwardness is in the additional attributes that are necessary.

    Once the Level 0 type is completed, then the Level 1 type may bedefined:

    [SecurityPermission(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]internalstaticpartialclassNativeMethods{[DllImport("user32.dll",EntryPoint="OpenWindowStation",CharSet=CharSet.Auto,BestFitMapping=false,ThrowOnUnmappableChar=true,SetLastError=true),SuppressUnmanagedCodeSecurity]privatestaticexternSafeWindowStationHandleDoOpenWindowStation(stringlpszWinSta,[MarshalAs(UnmanagedType.Bool)]boolfInherit,uintdwDesiredAccess);internalstaticSafeWindowStationHandleOpenWindowStation(stringlpszWinSta,boolfInherit,uintdwDesiredAccess){SafeWindowStationHandleret=DoOpenWindowStation(lpszWinSta,fInherit,dwDesiredAccess);if(ret.IsInvalid)Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());returnret;}

    [DllImport("user32.dll",EntryPoint="SetProcessWindowStation",SetLastError=true),SuppressUnmanagedCodeSecurity][return:MarshalAs(UnmanagedType.Bool)]privatestaticexternboolDoSetProcessWindowStation(SafeWindowStationHandlehWinSta);internalstaticvoidSetProcessWindowStation(SafeWindowStationHandlehWinSta){if(!DoSetProcessWindowStation(hWinSta))Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());}}

    //////Awindowstation.///publicsealedclassWindowStation:IDisposable

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 18/33

    {//////Theunderlyingwindowstationhandle.///privateSafeWindowStationHandleSafeWindowStationHandle;

    //////ImplementationofIDisposable:closestheunderlyingwindowstationhandle.///publicvoidDispose(){SafeWindowStationHandle.Dispose();}

    //////Opensanexistingwindowstation.///publicWindowStation(stringname){//("0x37F"isWINSTA_ALL_ACCESS)SafeWindowStationHandle=NativeMethods.OpenWindowStation(name,false,0x37F);}

    //////Setsthiswindowstationastheactiveoneforthisprocess.///publicvoidSetAsActive(){NativeMethods.SetProcessWindowStation(SafeWindowStationHandle);}}

    Notes:

    The unmanaged native methods now all useSafeWindowStationHandle for their return values andarguments, rather than IntPtr. Only the resource deallocationfunction is passed an IntPtr.For simplicity, NativeMethods.OpenWindowStation takes auint as its desired access mask, rather than a properenumeration. A real enumeration should be used in productioncode.The implementation of IDisposable.Dispose isstraightforward: dispose of the underlying handle.A finalizer is not necessary becauseSafeWindowStationHandle has its own finalizer inheritedfrom SafeHandle which will dispose of the underlyinghandle.

    Since the window station is a simple example, there is only one Level1 class rather than a hierarchy of Level 1 classes. To define ahierarchy, the following code pattern should be used:

    //////Abaseclassforwindowstationtypes.///publicabstractclassWindowStationBase:IDisposable{

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 19/33

    //////Theunderlyingwindowstationhandle.///protectedSafeWindowStationHandleSafeWindowStationHandle{get;set;}

    //////ImplementationofIDisposable:closestheunderlyingwindowstationhandle.///publicvoidDispose(){DisposeManagedResources();}

    //////Disposesmanagedresourcesinthisclassandderivedclasses.///Whenoverridingthisinaderivedclass,///besuretocallbase.DisposeManagedResources()///protectedvirtualvoidDisposeManagedResources(){SafeWindowStationHandle.Dispose();}}

    //////Awindowstation.///publicsealedclassWindowStation:WindowStationBase{//////Opensanexistingwindowstation.///publicWindowStation(stringname){//("0x37F"isWINSTA_ALL_ACCESS)SafeWindowStationHandle=NativeMethods.OpenWindowStation(name,false,0x37F);}

    //////Setsthiswindowstationastheactiveoneforthisprocess.///publicvoidSetAsActive(){NativeMethods.SetProcessWindowStation(SafeWindowStationHandle);}}

    Notes:

    SafeWindowStationHandle is now a protected property.This should be a set by derived classes, usually in theirconstructors. Note that this may also be a public propertye.g. Microsoft chose to make WaitHandle.SafeWaitHandlepublic; however, I believe protected is the better choice.When implementing IDisposable in the base class, I assumethe Disposable Design Principle instead of using Microsoft'sIDisposable code pattern. As a result:

    Types derived from WindowStationBase may not

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 20/33

    directly own unmanaged resources, i.e., they must beLevel 1 types. Note that they may own Level 0 types,which may own unmanaged resources; they just can't beLevel 0 types themselves.There is no need for WindowStationBase or anyderived type to have a finalizer. ImplementingMicrosoft's IDisposable code pattern requires afinalizer.I chose to name the resource disposing functionDisposeManagedResources, which is logicallyequivalent to the Dispose(true) of Microsoft'sIDisposable code pattern.

    Wrapping Unmanaged Resources Defining Level 0Types for Pointers with Context Data The AdvancedCase

    Sometimes an unmanaged API requires additional contextinformation in order to deallocate a resource. This requires a Level 0type that has some additional information attached to it, and thisalways requires more complex interop code.

    The example for defining advanced Level 0 types is allocatingmemory in the context of another process. The other process' handleneeds to be associated with the allocated memory, and it needs to bepassed to the deallocation function. First, the Level 0 type:

    [SecurityPermission(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]internalstaticpartialclassNativeMethods{[DllImport("kernel32.dll",EntryPoint="VirtualFreeEx",SetLastError=true),SuppressUnmanagedCodeSecurity][return:MarshalAs(UnmanagedType.Bool)]internalstaticexternboolVirtualFreeEx(SafeHandlehProcess,IntPtrlpAddress,UIntPtrdwSize,uintdwFreeType);}

    //////Level0typeformemoryallocatedinanotherprocess.///publicsealedclassSafeRemoteMemoryHandle:SafeHandle{publicSafeHandleSafeProcessHandle{[ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]get;

    [ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]privateset;}privateboolReleaseSafeProcessHandle;

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 21/33

    publicSafeRemoteMemoryHandle():base(IntPtr.Zero,true){}

    publicoverrideboolIsInvalid{[ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]get{return(handle==IntPtr.Zero);}}

    [ReliabilityContract(Consistency.WillNotCorruptState,Cer.MayFail)][PrePrepareMethod]protectedoverrideboolReleaseHandle(){//(0x8000==MEM_RELEASE)boolret=NativeMethods.VirtualFreeEx(SafeProcessHandle,handle,UIntPtr.Zero,0x8000);if(ReleaseSafeProcessHandle)SafeProcessHandle.DangerousRelease();returnret;}

    //////Overwritesthehandlevalue(withoutreleasingit).///Thisshouldonlybecalledfromfunctionsactingasconstructors.///[ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]internalvoidSetHandle (IntPtrhandle_,SafeHandlesafeProcessHandle,refboolsuccess){handle=handle_;SafeProcessHandle=safeProcessHandle;SafeProcessHandle.DangerousAddRef(refReleaseSafeProcessHandle);success=ReleaseSafeProcessHandle;}}

    Notes:

    This is very similar to the Level 0 type defined earlier; only thisclass also keeps a SafeHandle reference to the remoteprocess, which must be passed to VirtualFreeEx.A Level 0 type may contain a reference to another Level 0 typein this example, SafeRemoteMemoryHandle has a field oftype SafeHandle. However, it must explicitly control thefield's reference count, which requires an additional booleanfield ReleaseSafeProcessHandle.The process handle is held as a SafeHandle, not an IntPtr.This is because SafeHandle internally implements referencecounting to prevent premature deallocation. This is useful bothwhile being held as a field in SafeRemoteMemoryHandle andbeing passed to VirtualFreeEx.Since SafeProcessHandle may be accessed during CERs, itsaccessors need the ReliabilityContract andPrePrepareMethod attributes.

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 22/33

    There is also an additional method,SafeRemoteMemoryHandle.SetHandle, which is designedto execute within a Constrained Execution Region, so it canatomically set both the remote process handle and theunmanaged handle together.Once again, proper enumerations are skipped for simplicity.Also, a more proper handling of the remote process handlewould require defining a SafeProcessHandle, and using thatin place of the SafeHandle in this sample. This sample hascompletely correct behavior, but does not provide full typesafety.

    The Level 1 type reveals the additional complexity needed forcreating SafeRemoteMemoryHandle objects:

    [SecurityPermission(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]internalstaticpartialclassNativeMethods{[DllImport("kernel32.dll",EntryPoint="VirtualAllocEx",SetLastError=true),SuppressUnmanagedCodeSecurity]privatestaticexternIntPtrDoVirtualAllocEx(SafeHandlehProcess,IntPtrlpAddress,UIntPtrdwSize,uintflAllocationType,uintflProtect);internalstaticSafeRemoteMemoryHandleVirtualAllocEx(SafeHandlehProcess,IntPtrlpAddress,UIntPtrdwSize,uintflAllocationType,uintflProtect){SafeRemoteMemoryHandleret=newSafeRemoteMemoryHandle();boolsuccess=false;

    //Atomicallygetthenativehandle//andassignitintoourreturnobject.RuntimeHelpers.PrepareConstrainedRegions();try{}finally{IntPtraddress=DoVirtualAllocEx(hProcess,lpAddress,dwSize,flAllocationType,flProtect);if(address!=IntPtr.Zero)ret.SetHandle(address,hProcess,refsuccess);if(!success)ret.Dispose();}

    //DoerrorhandlingaftertheCERif(!success)thrownewException("Failedtosethandlevalue");if(ret.IsInvalid)Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());returnret;}

    [DllImport("kernel32.dll",EntryPoint="WriteProcessMemory",SetLastError=true),SuppressUnmanagedCodeSecurity]

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 23/33

    [return:MarshalAs(UnmanagedType.Bool)]privatestaticexternboolDoWriteProcessMemory(SafeHandlehProcess,SafeRemoteMemoryHandlelpBaseAddress,IntPtrlpBuffer,UIntPtrnSize,outUIntPtrlpNumberOfBytesWritten);internalstaticvoidWriteProcessMemory(SafeRemoteMemoryHandleRemoteMemory,IntPtrlpBuffer,UIntPtrnSize){UIntPtrNumberOfBytesWritten;if(!DoWriteProcessMemory(RemoteMemory.SafeProcessHandle,RemoteMemory,lpBuffer,nSize,outNumberOfBytesWritten))Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());if(nSize!=NumberOfBytesWritten)thrownewException ("WriteProcessMemory:Failedtowriteallbytesrequested");}}

    //////Memoryallocatedinanotherprocess.///publicsealedclassRemoteMemory:IDisposable{//////Theunderlyingremotememoryhandle.///privateSafeRemoteMemoryHandleSafeRemoteMemoryHandle;

    //////Theassociatedprocesshandle.///publicSafeHandleSafeProcessHandle{get{returnSafeRemoteMemoryHandle.SafeProcessHandle;}}

    //////ImplementationofIDisposable:closestheunderlyingremotememoryhandle.///publicvoidDispose(){SafeRemoteMemoryHandle.Dispose();}

    //////Allocatesmemoryfromanotherprocess.///publicRemoteMemory(SafeHandleprocess,UIntPtrsize){//("0x3000"isMEM_COMMIT|MEM_RESERVE)//("0x04"isPAGE_READWRITE)SafeRemoteMemoryHandle=NativeMethods.VirtualAllocEx(process,IntPtr.Zero,size,0x3000,0x04);}

    //////Writestomemoryinanotherprocess.///Note:atleastbytesstarting///atmustbepinnedinmemory.///publicvoidWrite(IntPtrbuffer,UIntPtrsize){

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 24/33

    NativeMethods.WriteProcessMemory(SafeRemoteMemoryHandle,buffer,size);}}

    Notes:

    The first thing that should stand out is how much morecomplicated the allocation function is.NativeMethods.VirtualAllocEx is designed to partiallyrun within an explicit Constrained Execution Region.Specifically:

    It does all the necessary allocations before the CER. Inthis example, it only needs to allocate the returnedSafeRemoteMemoryHandle object.The call toRuntimeHelpers.PrepareConstrainedRegionsfollowed by the empty try block is the way of declaringthe finally block to be an explicit ConstrainedExecution Region. See MSDN for more details on thismethod.It performs error checking, including throwingexceptions which may allocate memory after the CER.

    The CER provides atomic execution: It guarantees that theIntPtr returned from the unmanaged VirtualAllocEx iswrapped in a SafeRemoteMemoryHandle object, even in thepresence of asynchronous exceptions e.g., if Thread.Abort iscalled on a thread in a CER, the CLR will wait until the CER iscompleted before asynchronously raising theThreadAbortException.CERs were not necessary for the simpler examples becauseSafeHandle is treated specially when returned from anunmanaged function: the returned value actually an IntPtris used to construct a new SafeHandle atomically. In otherwords, the CLR supports this behavior for SafeHandleautomatically, but now we have to force the same behaviorusing CERs.Another important note is that the interop code shouldcontinue to reference the Level 0 type e.g.,SafeRemoteMemoryHandle instead of just an IntPtr; thiskeeps SafeHandle's reference counting involved. Passing thecontext data e.g., SafeHandle or SafeProcessHandlealong with a plain IntPtr would be incorrect.The RemoteMemory Level 1 type does expose the additionalcontext property as RemoteMemory.SafeProcessHandle.This is not required, but often useful.

    Notes on how this example is simplified:

    For simplicity, this example only provides a single Level 1 classinstead of a class hierarchy. See the previous example for an

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 25/33

    example of the Level 1 hierarchy pattern.Again, the process SafeHandle should really be aSafeProcessHandle, and proper enumerations have beenomitted.This sample also does not expose a very userfriendly API; itshould include both reading and writing at various offsets, andshould accept byte arrays instead of prepinned memory.Exceptions of type Exception should not be thrown directly;this should be of a more specific type.

    Wrapping Unmanaged Resources Defining Level 0Types for NonPointer Data The Hard Case

    There are a handful of unmanaged APIs whose handle types are notpointers. Each of these handle types may either be converted to anIntPtr if they are smaller or equal to the IntPtr type or treatedas additional context data for a fake IntPtr.

    The example for nonpointer Level 0 types is the local atom table.There is no real reason to use this antiquated API in a modernprogram, but this example will illustrate how to handle APIs of thisnature. The ATOM type is an unsigned 16bit integer, and forillustration purposes, the sample is implemented twice: oncewidening the ushort to IntPtr, and the other treating the ushortas context data for a fake IntPtr.

    First, the Level 0 type for atoms, storing the ushort unmanagedhandle value inside the IntPtrSafeHandle.handle field:

    [SecurityPermission(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]internalstaticpartialclassNativeMethods{[DllImport("kernel32.dll",EntryPoint="DeleteAtom",SetLastError=true),SuppressUnmanagedCodeSecurity]internalstaticexternushortDeleteAtom(ushortnAtom);}

    //////Level0typeforlocalatoms(castingimplementation).///publicsealedclassSafeAtomHandle:SafeHandle{//////Internalunmanagedhandlevalue,translatedtothecorrecttype.///publicushortHandle{[ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]get{returnunchecked((ushort)(short)handle);}

    [ReliabilityContract(Consistency.WillNotCorruptState,

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 26/33

    Cer.Success)][PrePrepareMethod]internalset{handle=unchecked((IntPtr)(short)value);}}

    //////Defaultconstructorinitializingwithaninvalidhandlevalue.///publicSafeAtomHandle():base(IntPtr.Zero,true){}

    //////Whetherornotthehandleisinvalid.///publicoverrideboolIsInvalid{[ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]get{return(Handle==0);}}

    //////Releasesthehandle.///[ReliabilityContract(Consistency.WillNotCorruptState,Cer.MayFail)][PrePrepareMethod]protectedoverrideboolReleaseHandle(){return(NativeMethods.DeleteAtom(Handle)==0);}}

    The only difference of note is the addition of the Handle property,which provides access to the handle, treating it as a ushort. Notethe necessity of the ReliabilityContract andPrePrepareMethod attributes on the property accessors. TheIsInvalid and ReleaseHandle implementations use Handleinstead of handle for ease of implementation.

    The additional complexity comes into play with the interop code usedwith the Level 1 class:

    [SecurityPermission(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]internalstaticpartialclassNativeMethods{[DllImport("kernel32.dll",EntryPoint="AddAtom",CharSet=CharSet.Auto,BestFitMapping=false,ThrowOnUnmappableChar=true,SetLastError=true),SuppressUnmanagedCodeSecurity]privatestaticexternushortDoAddAtom(stringlpString);internalstaticSafeAtomHandleAddAtom(stringlpString){SafeAtomHandleret=newSafeAtomHandle();

    //Atomicallygetthenativehandle//andassignitintoourreturnobject.RuntimeHelpers.PrepareConstrainedRegions();try{}finally{

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 27/33

    ushortatom=DoAddAtom(lpString);if(atom!=0)ret.Handle=atom;}

    //DoerrorhandlingaftertheCERif(ret.IsInvalid)Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());returnret;}

    [DllImport("kernel32.dll",EntryPoint="GetAtomName",CharSet=CharSet.Auto,BestFitMapping=false,ThrowOnUnmappableChar=true,SetLastError=true),SuppressUnmanagedCodeSecurity]privatestaticexternuintDoGetAtomName(ushortnAtom,StringBuilderlpBuffer,intnSize);internalstaticstringGetAtomName(SafeAtomHandleatom){//Atomstringshaveamaximumsizeof255bytesStringBuildersb=newStringBuilder(255);uintret=0;boolsuccess=false;

    //AtomicallyincrementtheSafeHandlereferencecount,//callthenativefunction,anddecrementthecountRuntimeHelpers.PrepareConstrainedRegions();try{}finally{atom.DangerousAddRef(refsuccess);if(success){ret=DoGetAtomName(atom.Handle,sb,256);atom.DangerousRelease();}}

    //DoerrorhandlingaftertheCERif(!success)thrownewException("SafeHandle.DangerousAddReffailed");if(ret==0)Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

    sb.Length=(int)ret;returnsb.ToString();}}

    //////Atominthelocalatomtable.///publicsealedclassLocalAtom:IDisposable{//////Theunderlyingatomhandle.///privateSafeAtomHandleSafeAtomHandle;

    //////ImplementationofIDisposable:closestheunderlyingatomhandle.///publicvoidDispose(){SafeAtomHandle.Dispose();}

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 28/33

    //////Addsastringtotheatomtable,settingthislocalatomtopointtoit.///publicvoidAdd(stringname){SafeAtomHandle=NativeMethods.AddAtom(name);}

    publicstringName{get{returnNativeMethods.GetAtomName(SafeAtomHandle);}}}

    The primary difference between this example and the last one is theneed for CERs in every single interop call. The automatic referencecounting from SafeHandle is no longer automatic, so it must bedone by hand. Every time the underlying unmanaged handle needsto be passed to an unmanaged function, the example ofNativeMethods.GetAtomName should be followed:

    1. Initialize return values in this case, a return buffer and anyerror condition variables.

    2. Use a CER to atomically increment the SafeHandle referencecount, call the unmanaged function, and decrement theSafeHandle count. Note that incrementing the SafeHandlereference count may fail, which should abort the call.[Alternatively, the incrementing and unmanaged function callmay be placed within the try block, but the decrementingmust remain in the finally block.]

    3. Perform all error testing: both the SafeHandle increment aswell as the unmanaged function result must be considered.Remember that throwing Exception is not recommended inproduction code; a more specific type should be selectedinstead.

    The second implementation using context values instead of castingto/from IntPtr may be chosen if the casting would be awkward, orif the unmanaged handle type won't fit into a single IntPtr field. Itis possible to make the SafeHandle.handle field almostmeaningless by only assigning it 0 for invalid handle values or 1indicating the handle including the context values is valid:

    //////Level0typeforlocalatoms(contextimplementation).///publicsealedclassSafeAtomHandle:SafeHandle{publicushortHandle{[ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]get;

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 29/33

    [ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]privateset;}

    //////Defaultconstructorinitializingwithaninvalidhandlevalue.///publicSafeAtomHandle():base(IntPtr.Zero,true){}

    //////Whetherornotthehandleisinvalid.///publicoverrideboolIsInvalid{[ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]get{return(handle==IntPtr.Zero);}}

    //////Releasesthehandle.///[ReliabilityContract(Consistency.WillNotCorruptState,Cer.MayFail)][PrePrepareMethod]protectedoverrideboolReleaseHandle(){return(NativeMethods.DeleteAtom(Handle)==0);}

    //////Overwritesthehandlevalue(withoutreleasingit).///Thisshouldonlybecalledfromfunctionsactingasconstructors.///[ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)][PrePrepareMethod]internalvoidSetHandle(ushorthandle_){Handle=handle_;handle=(IntPtr)(1);}}

    Notes:

    The Handle property is now a context, stored separately fromhandle.The IsInvalid property tests the handle, which now onlyhas values of 0 or 1, but the ReleaseHandle method stilluses Handle for convenience.The Handle setter has been replaced by the SetHandlemethod. This is done in the example to reflect the fact thatmost of the time contexts are used, SetHandle will need totake more than one argument.

    The only change necessary in the rest of the example is the change inhow the handles are set in the NativeMethods.AddAtom

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 30/33

    constructor method:

    ret.Handle=atom;

    should be:

    ret.SetHandle(atom);

    Remember that in a realworld situation, SetHandle would be takingmore than one argument.

    SummaryTo summarize, prefer using the Disposable Design Principle. The DDPsplits up resource management responsibilities into Level 0 typeswhich handle unmanaged resources, and Level 1 types which arestill small wrapper classes that closely resemble the native API, butonly handle managed resources:

    1. Level 0 types directly wrap unmanaged resources, and are onlyconcerned with deallocation of their resource.

    1. Level 0 types are either abstract or sealed.2. Level 0 types must be designed to execute completely

    within an atomic execution region.

    For Constrained Execution Regions, this meansthat Level 0 types must derive from SafeHandlewhich derives fromCriticalFinalizerObject.For finally blocks, this means that Level 0 typesmust derive from a separatelydefinedSafeHandle type which implementsIDisposable to deallocate the unmanagedresource explicitly possibly called in the contextof a finally block or from a finalizer.

    3. Constructors for Level 0 types must be called fromwithin an atomic execution region.

    The special full framework interop handling ofSafeHandle return values is consideredunmanaged code and therefore an atomicexecution region of the strongest guarantee.

    4. Level 0 types may refer to other Level 0 types, but mustincrement the count of the referredto object as long asthe reference is needed.

    2. Level 1 types only deal with managed resources.

    1. Level 1 types are generally sealed unless they aredefining a base Level 1 type for a Level 1 hierarchy.

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 31/33

    2. Level 1 types derive from Level 1 types or fromIDisposable directly; they do not derive fromCriticalFinalizerObject or Level 0 types.

    3. Level 1 types may have fields that are Level 0 or Level 1types.

    4. Level 1 types implement IDisposable.Dispose bycalling Dispose on each of its Level 0 and Level 1 fields,and then calling base.Dispose if applicable.

    5. Level 1 types do not have finalizers.6. When defining a Level 1 type hierarchy, the abstract

    root base type should define a protected propertywith the name and type of the associated Level 0 type.

    Using the Disposable Design Principle instead of Microsoft'sIDisposable code pattern will make software more reliable andeasier to use.

    References and Further ReadingCLR via C# 2nd ed., Jeffrey Richter, Microsoft Press particularly:

    Garbage collection algorithm: pg 461465, 478, 495,538539.Special marshaling treatment of SafeHandle duringinterop: pg 473475.Finalization details: pg 475481.Microsoft's finalizer dependency problem and theirsolution: pg 492493.

    MSDN:

    Implementing a Dispose Method, and thecorresponding guidelines.Constrained Execution Regions.Keep Your Code Running with the Reliability Features ofthe .NET Framework, MSDN Magazine, October 2005.Safe Handles and Critical Finalization.

    Blogs:

    Chris Brumme, Lifetime, GC.KeepAlive, handle recycling20030419.Chris Brumme, Startup, Shutdown and related matters20030820.Chris Brumme, Finalization 20040220.Chris Lvon, Dispose Dos and Don'ts 20040923.Ravi Krishnaswamy BCL Team Blog, SafeHandles: thebest V2.0 feature of the .NET Framework 20050315.Brian Grunkemeyer BCL Team Blog, SafeHandle: AReliability Case Study 20051316.Brian Grunkemeyer BCL Team Blog, Constrained

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 32/33

    Execution Regions and other errata 20050614.

    Mailing lists:

    Brian Harry, Resource management 20001006. Brianexplains in this post why Microsoft decided deterministicfinalization was unnecessary.

    AfterwordIn a future article, I hope to address one further drawback toIDisposable: the lack of support for shutdown logic; and provide apartial solution. This was originally intended to be part of thisarticle, but it's already too long. I also hope to look at theSafeHandle alternatives for the .NET Compact Framework, whichsadly does not support SafeHandle or Constrained ExecutionRegions.

    I'd like to thank my loving almostwife Mandy Snell, for patientlyproofreading this article. On October 4th, 2008, she will officiallybecome Mandy Cleary. I also must state that everything good inmy life comes from Jesus Christ; He is the source of all wisdom, and Ithank Him for all His gifts. "For God giveth to a man that is good inhis sight wisdom, and knowledge, and joy" Ecc. 2:26.

    History20080927 Fixed bug in the advanced sample, rewrote thesummary of the DDP, and added the reference to Microsoft'srationale to not support reference counting20080922 Added the References and History sections20080921 Initial publication

    LicenseThis article, along with any associated source code and files, islicensed under The BSD License

    Share

    About the Author

  • 23/1/2015 IDisposable:WhatYourMotherNeverToldYouAboutResourceDeallocationCodeProject

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout?display=Print 33/33

    Permalink | Advertise | Privacy | Terms of Use | Mobile Web04 | 2.8.150121.1 | Last Updated 10 Nov 2014

    Article Copyright 2008 by Stephen ClearyEverything else Copyright CodeProject, 19992015

    Stephen ClearySoftware Developer Senior United States

    Stephen Cleary is a Christian, husband, father, and programmer livingin Northern Michigan. Personal home page including blog: http://www.stephencleary.com/

    Follow on Twitter

    Comments and Discussions 62 messages have been posted for this article Visit

    http://www.codeproject.com/Articles/29534/IDisposableWhatYourMotherNeverToldYouAbout to post and view comments on thisarticle, or click here to get a print view with messages.