Message: Safe handle has been closed error while hosting enterprise library in IIS

Topics: Exception Handling Application Block
Sep 6, 2012 at 4:10 PM
Edited Sep 6, 2012 at 4:12 PM

I am using enterprise library 5.0 for developing web application. While hosting in IIS, where ever am handling exception, am getting the following error in the ever viewer.
An unhandled exception occurred and the process was terminated.Process ID: 3480Exception: System.ObjectDisposedExceptionMessage: Safe handle has been closedStackTrace:    at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success)   at Microsoft.Win32.Win32Native.GetTokenInformation(SafeTokenHandle TokenHandle, UInt32 TokenInformationClass, SafeLocalAllocHandle TokenInformation, UInt32 TokenInformationLength, UInt32& ReturnLength)   at System.Security.Principal.WindowsIdentity.GetTokenInformation(SafeTokenHandle tokenHandle, TokenInformationClass tokenInformationClass)   at System.Security.Principal.WindowsIdentity.get_User()   at System.Security.Principal.WindowsIdentity.GetName()   at System.Security.Principal.WindowsIdentity.get_Name()   at Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionFormatter.get_AdditionalInfo() in e:\Builds\EntLib\Latest\Source\Blocks\ExceptionHandling\Src\ExceptionHandling\ExceptionFormatter.cs:line 92   at Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionFormatter.WriteException(Exception exceptionToFormat, Exception outerException) in e:\Builds\EntLib\Latest\Source\Blocks\ExceptionHandling\Src\ExceptionHandling\ExceptionFormatter.cs:line 162   at Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionFormatter.Format() in e:\Builds\EntLib\Latest\Source\Blocks\ExceptionHandling\Src\ExceptionHandling\ExceptionFormatter.cs:line 107   at Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter.Format() in e:\Builds\EntLib\Latest\Source\Blocks\ExceptionHandling\Src\ExceptionHandling\TextExceptionFormatter.cs:line 78at Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionUtility.FormatHandlingException(StringWriter writer, String header, Exception ex) in e:\Builds\EntLib\Latest\Source\Blocks\ExceptionHandling\Src\ExceptionHandling\ExceptionUtility.cs:line 87   at Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionUtility.FormatExceptionHandlingExceptionMessage(String policyName, Exception offendingException, Exception chainException, Exception originalException) in e:\Builds\EntLib\Latest\Source\Blocks\ExceptionHandling\Src\ExceptionHandling\ExceptionUtility.cs:line 64   at Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionPolicyEntry.ExecuteHandlerChain(Exception ex, Guid handlingInstanceID) in e:\Builds\EntLib\Latest\Source\Blocks\ExceptionHandling\Src\ExceptionHandling\ExceptionPolicyEntry.cs:line 139   at Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionPolicyEntry.Handle(Exception exceptionToHandle) in e:\Builds\EntLib\Latest\Source\Blocks\ExceptionHandling\Src\ExceptionHandling\ExceptionPolicyEntry.cs:line 87
I am using custom tracelistener to tracedata to the log table.
Please help me urgently.

Sep 6, 2012 at 4:54 PM

Can you provide more information?  For example:

  • What version of IIS?  
  • What is the pipeline mode? 
  • What type of authentication settings are being used?
  • Any uses of multi-threading/new threads?

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Sep 6, 2012 at 5:07 PM

Hi,

 

Please find the details below.

Can you provide more information?  For example:

  • What version of IIS?    

IIS7

  • What is the pipeline mode? 

Integrated

  • What type of authentication settings are being used?

Windows 

  • Any uses of multi-threading/new threads?

No

 

Thanks in advance

Sep 7, 2012 at 6:07 AM

I would guess that somehow the identity information is being Disposed so this error is occurring.  I'm not sure why though since if you are on the primary asp.net thread then references to the identity should still exist so it should not be garbage collected.

A quick fix would be to change the pipeline mode to ASP.NET Classic.

If you have a project that reproduces the issue it would be helpful.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Sep 7, 2012 at 5:54 PM

I got where the exception is happening. it is throwing exception in the  this.additionalInfo.Add("ThreadIdentity", Thread.CurrentPrincipal.Identity.Name); line of exception handler.cs file. which says Safe handle has been closed.

 

Please help me on this.

Sep 8, 2012 at 6:36 AM

Also , as you asked Thread executeThread = new Thread(new ParameterizedThreadStart(objClass) we are calling. Kindly let me know how to resolve this issue.

Sep 8, 2012 at 3:15 PM
Edited Sep 9, 2012 at 3:16 AM
hunter123 wrote:

Also , as you asked Thread executeThread = new Thread(new ParameterizedThreadStart(objClass) we are calling. Kindly let me know how to resolve this issue.

OK, that makes more sense.

What is happening is that:

  1. A request comes into IIS/ASP.NET (since pipeline is integrated mode)
  2. A new thread (#1) is spawned to handle the incoming request and assigned a WindowsIdentity (since Windows Authentication is being used)
  3. Your application code is manually starting a new thread (#2) to run application logic
  4. The main ASP.NET thread (#1) completes and resources are freed.  This includes Disposing of the WindowsIdentity.
  5. The application thread (#2) attempts to access the Disposed WindowsIdentity information and throws an ObjectDisposedException.

Here is a console application that reproduces the issue:

    class Program
    {
        static void Main(string[] args)
        {
            Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());             

            ParameterizedThreadStart objClass = new ParameterizedThreadStart(MyClass.Process);

            Thread executeThread = new Thread(objClass);

            var windowsIdentity = Thread.CurrentPrincipal.Identity as WindowsIdentity;
            windowsIdentity.Dispose();

            MyThreadData data = new MyThreadData("My Custom Data");
            executeThread.Start(data);

            executeThread.Join();

            Console.WriteLine("Done mainline");
        }
    }

    public class MyClass
    {
        public static void Process(object data)
        {
            Console.WriteLine("Starting thread processing with data = " + data);
        }
    }

    public class MyThreadData
    {
        public MyThreadData(object data)
        {
            Data = data;
        }

        public object Data { get; private set; }

        public override string ToString()
        {
            return Thread.CurrentPrincipal.Identity.Name;
        }
    }

To work around this scenario, you can create a copy of the WindowsIdentity to use on your thread and then set the principal when the thread starts to run.  Something like this:

    class Program
    {
        static void Main(string[] args)
        {
            Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());             

            ParameterizedThreadStart objClass = new ParameterizedThreadStart(MyClass.Process);
            Thread executeThread = new Thread(objClass);

            // Copy principal information
            MyThreadData data = new MyThreadData(WindowsIdentity.GetCurrent(), "My Custom Data");

            var windowsIdentity = Thread.CurrentPrincipal.Identity as WindowsIdentity;
            windowsIdentity.Dispose();

            executeThread.Start(data);
            executeThread.Join();

            Console.WriteLine("Done mainline");
        }
    }

    public class MyClass
    {
        public static void Process(object data)
        {
            using (MyThreadData myThreadData = data as MyThreadData)
            {
                Thread.CurrentPrincipal = new WindowsPrincipal(myThreadData.Identity);

                Console.WriteLine("Starting thread processing with data = " + data);
            }
        }
    }

    public class MyThreadData : IDisposable
    {
        public MyThreadData(WindowsIdentity identity, object data)
        {
            Identity = identity;
            Data = data;
        }

        public WindowsIdentity Identity { get; private set; }
        public object Data { get; private set; }

        public override string ToString()
        {
            return Identity.Name + " with Data " + Data.ToString();
        }
    
        public void Dispose()
        {
            Console.WriteLine("Disposing WindowsIdentity");

            if (Identity != null)
            {
                Identity.Dispose();
            }
        }
    }

Some caveats: this is a naive implementation assuming that WindowsIdentity is always used (this may not always be the case), nulls are not checked, and the Dispose implementation is not complete.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com