Thursday, 6 December 2012

"System.ObjectDisposedException: Safe handle has been closed" when running a background thread



When using an ASP.NET page you may see the "System.ObjectDisposedException: Safe handle has been closed" when running a background thread.

Typically this is seen when you try to access the name property - for example.

Thread.CurrentPrincipal.Identity.Name
 
 
What appears to happen is that IIS has initiated the new background thread and the thread identity flows down to that new thread. The HTTP connection closes down and control is restored to the browser (which is probably what you wanted if you're using a background thread).

Unfortunately the thread identity is closed down and disposed of as part of this process so you can't access it.

To work around this problem it is possible to use clone the thread's CurrentPrincipal on the webform and pass this to the background thread and set to using the Thread.CurrentPrincipal property.

This is possible as the threads are running in the same process - this method wouldn't work across process boundaries.


    /// <summary>
    /// Occurs when the user clicks the webform button, starting the long running background process
    /// </summary>
    protected void Button1_Click(object sender, EventArgs e)
    {
        AutomationDelegate Delegate = new AutomationDelegate(BackgroundProcess);
        WindowsIdentity Cloned = (WindowsIdentity)CloneObject(Thread.CurrentPrincipal.Identity);
        Delegate.BeginInvoke(Cloned, null, null);
    }

 
    /// <summary>
    /// The delegate to call
    /// </summary>
    /// <param name="ThreadIdentity">The thread identity to be persisted to the background thread</param>
    public delegate void AutomationDelegate(WindowsIdentity ThreadIdentity);


    /// <summary>
    /// The background process
    /// </summary>
    /// <param name="ThreadIdentity">The threadidentity to persist</param>
    void BackgroundProcess(WindowsIdentity ThreadIdentity)
    {
        Thread.CurrentPrincipal = new WindowsPrincipal(ThreadIdentity);
        Log(Thread.CurrentPrincipal.Identity.Name);
        Thread.Sleep(5000);
    }



    /// <summary>
    /// Creates a clone of this Configuration Type object
    /// </summary>
    /// <param name="Source">The source object to clone</param>
    public static Object CloneObject(Object Source)
    {
        MemoryStream Stream = new MemoryStream();
        BinaryFormatter Formatter = new BinaryFormatter();
        Formatter.Serialize(Stream, Source);
        Stream.Position = 0;
        object Clone = (object)Formatter.Deserialize(Stream);
        Stream.Close(); Stream.Dispose();
        return Clone;
    }

  
 
 

3 comments:

  1. Please let me know if you have any comments on this thread (no pun intended!)

    ReplyDelete
  2. Thank you so much, this really saved my bacon :)

    ReplyDelete