20110328

Microsoft.Web.Infrastructure + System.Web.PreApplicationStartMethod

What follows is interesting & a neat trick, but I do not claim that it is a good idea to do this in production code.  It may be useful, it may not.

I have been playing around with a few of the new bits that are available for .NET web development recently and I came across a neat trick.  I had a project that I wanted to debug, but I didn't have access to the source directly and the app itself was handling the error and in the process eating some of the detail I needed.

I made a httpmodule that was decorated with System.Web.PreApplicationStartMethod This is a framework 4 bit that allows a httpmodule to run a shared sub BEFORE all the stack gets built and locked in place.

I then took and used reflection to load and invoke Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule.  This allows the httpmodule to place itself in the modules collection without altering the web config.  Loading it through reflection allows me to just drag the dll into the bin folder along with the httpmodule dll.  This is handy because web.infrastructure is part of the MVC framework and the problem I was having was on a non-mvc machine.

A little poking about in reflector and I came up with this function to get the existing event handlers



Dim t As Type = target.[GetType]()
Public Function GetEventSubscribers(ByVal target As Object, ByVal eventName As String) As [Delegate]()
    Dim w = CType(t.GetField("_events", BindingFlags.Instance Or BindingFlags.Static Or BindingFlags.NonPublic).
        GetValue(target), System.ComponentModel.EventHandlerList)
    Dim k = t.GetFields(BindingFlags.[Static] Or BindingFlags.Instance Or BindingFlags.NonPublic).
        Where(Function(x) x.Name.StartsWith("Event" & eventName)).Select(Function(x) x.GetValue(target)).ToList()
    Dim d() As [Delegate] = k.SelectMany(Function(x)
                                             If w(x) Is Nothing Then
                                                 Return New [Delegate]() {}
                                             Else
                                                 Return w(x).GetInvocationList()
                                             End If
                                         End Function).ToArray
    Return d
End Function

If you pass the httpapplication instance to it with an eventname you get all the registered handler delegates, which will allow you to call .removeeventhandler() on each of them.  

If you do that to the error event, then add your own handler, then re-add the pre-existing delegates in the correct order then your handler fires first, before any of the other handlers has a chance to mangle the even state and the rest of the application seems to be none the wiser as long as you don't alter the even state yourself.

In my example I just put a trace in that wrote to a text file and after a few passes at the broken bits I got a nice clear picture of what was going on.

The approach seems to work in ASPX, MVC, WCF and Sharepoint and only involves copying a couple dlls to the bin directory in the way of altering the original project.

Again, I haven't used this too much yet, so your mileage may vary, but even if it doesn't always work I think it is an interesting approach to spy on a misbehaving build without changing too many variables.

1 comment:

Firefox Feedly RSS option

If you use Firefox with a RSS button and want the default RSS page to offer a Feedly option here is what you need to do: go to the about:c...