navigation
 Thursday, August 30, 2007

I was debugging some ASP.Net 2.0 code that renders a PDF to the response buffer of a page request, using code like this (byte[] bytes is the PDF file, which was rendered using Microsoft Reporting Services Web Service interface) :

public static void DownloadBytes(byte[] bytes, string mimeType, string fileName) {
  HttpResponse r = HttpContext.Current.Response;
  r.ContentType = mimeType;
  r.AppendHeader("Content-Disposition", "attachment;filename=" + fileName);
  r.BinaryWrite(bytes);
  r.Flush();
  r.End();
}

I found that an exception was being raised when r.End() was called, and it turned out to be a sneaky little guy by the name of System.Threading.ThreadAbortException. Apparently the call to Response.End() aborts the thread that is processing the web request. No worries, I just added a try..catch block to catch that exception.

Not!

To my surprise, the exception was re-raised after being caught! After checking the online documentation of this exception here, I found this interesting tidbit of information:

ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. When this exception is raised, the runtime executes all the finally blocks before killing the thread.

Hmm! So there was obviously not much point catching this exception further down in my layers of code, because it would just magically appear! In fact, in every layer of the code that has exception handling in the call stack, if I wanted this exception to slip through (and not be processed by the regular exception handling block) I needed to add a specific block to ignore this exception. For instance:

private void ibExecute_Click(object sender, System.Web.UI.ImageClickEventArgs e)
{
  try
  {
    // Code here sometimes calls the code above the ends the response
  }
  catch (System.Threading.ThreadAbortException)
  {
    // Do nothing.
  }
  catch (Exception E)
  {
    DisplayError(E.Message);
  }
}

Why would I want to let this exception slip through? Well, for other kinds of exceptions, I catch then and display the error on the form. But in this case, there is no form, and even it there was, it is too late, the Response has ended. So I don't want to call DisplayError.

You have to admit that this is a pretty sneaky exception!