Revisiting Hosting Reflector

You can automate .NET Reflector processes, and run .NET Reflector from within a .NET application, from within ASP.NET or even within ASP.NET MVC. What is more you can host a reflector Add-in. This opens up many possibilities as Nick Harrison points out.

Three years ago, I wrote about hosting Reflector in your own application. At that time, we created a User Control that used Reflector to show the disassembled code for whatever method we specified. A lot has changed in the intervening years.

If you are not already familiar with Reflector, it is a wonderful tool that allows you to disassemble compiled code and navigate through this output. There is also a wide array of plug ins available to extend the functionality to. Here we will exploit the same features that allow you to write plug ins to drive Reflector.

Reflector has gone through a couple of major version upgrades and more than its share of controversy. There are lots of new language features in the DotNet framework and asp.net is no longer the only game in town for building a Web UI in a DotNet world.

So, how do we go about harnessing the power of Reflector for our own purposes, and what has changed in the past three years? We will explore this and a couple of other questions as we update the code from this older article to the latest version of both Reflector and the DotNet framework.

Setting up the Environment

At first glance, it looks like nothing has changed at all, and this is close to the truth. All of the interfaces that we used before are still there and they still have the same meaning. In fact, if you start with the code from the original article and simply replace the referenced to Reflector with the most current version and switch the Target Framework to 4.0 everything compiles. This gives us a warm fuzzy feeling, but all is not right.

When we run the original code, we are immediately presented with a most unsightly error.

1450-image001small.png

My initial solution was to simply add the ASP COM Compatibility attribute to my page directive since I know that this will force the web page to operate as an STA thread. So, I add this:

And when I refresh the page the error goes away and I get the same output that I got three years ago.

1450-image002small.png

And it looks like all is right with the world, until I refresh the page:

1450-image003small.png

This is an even uglier error than the first one. Clearly, the page attribute did not solve the problem. Fortunately we get a clue in that both times, the error message is on the same line of code.

Turning our attention to this line, we can discover that there is an overload to the constructor. This overload exposes a new Boolean parameter named embedded.

1450-image004.png

Well, this looks promising. So we change the errant line to this

Now when we rerun the application, we get the expected results and we can refresh the page to our heart’s content.

Considering all of the changes that Reflector has been through, this is not a lot of changes to make three year old code work with the latest version.

Cleaning up the Code

Well the code now works and runs as expected, but looking back at it, it seems a little dated. It does not take advantage of any of the new compiler syntax candy that Microsoft has passed out recently. Let’s see what we can do.

The for loop to initialize the language can be rewritten from:

To a single LINQ statement like this:

…or this depending on your preferred syntax.

This is only a stylistic preference because both ways compile to the same IL and Reflector will not be able to tell the difference.

1450-image005small.png

You may notice that this is not what it looks like from Reflector:

1450-image006small.png

Why is that? I suspect that I am missing a setting in the ConfigureLanguageConfiguration method, but I have not figured out which one yet. This is a frustrating inconsistency between Reflector and our hosted implementation. Surprisingly, evaluating LINQ style expressions have been the only inconsistencies that I have found.

Reflector Meets MVC

As I mentioned earlier, ASP.Net is also a little dated. The cool kids these days use MVC and use custom editors instead of WebControls. How will that look different?

The first thing that we need to do is move much of the heavy lifting out of the existing ReflectorComponent class. Let’s create a new class called ReflectorTools. Into this class we will move most of the constructor code for ReflectorComponent and the DisassembleMethod itself.

The ReflectorComponent now looks like this:

With this code pulled out of the UserControl, we are free to reuse it in our MVC application.

This makes our MVC very straightforward to implement. To duplicate the functionality from our ASP.Net application, we need a controller like this:

We will pass the selected language to the View. We start by passing in 0 to default to the first language. Subsequent post backs will pass in the selected item from the language drop down back to the controller which we will pass back to the View.

The View may look like this:

There are a couple of interesting things to note here. We make the drop down list behave like an autopostback by setting the onchange to submit the form. The call to BeginForm ensures that the form will post back to the same controller.

The final piece to add is the extension methods to the HTMLHelper.

The LanguageDropDown will simply pull from the property exposed by the ReflectorSupport class:

The ShowCode method is very simple since the ReflectorSupport class is doing all of the work:

This also showcases the appeal of the MVC framework. Comparing the two implementations shows how much simpler it can be to implement the same functionality with MVC

Hosting a Add in

Disassembling code is fun and informative, but it is not the only trick up Reflector’s sleeve. There are also a wide array of add ins available. We can also host these in our application. One of my favorite add ins is Code Metrics . It allows you to easily track key software metrics such as cyclomatic complexity, coupling, and number of instructions. In an earlier article, I provided a good overview of how to use this add in to help identify where to focus your refactoring efforts. It can be very helpful to host this add in to automate gathering these metrics..

Let’s start by running Reflector on the add in code. Because this is an add-in, we want to look for the class that implements the IPackage interface. We can quickly see that this is the CodeMetricPackage object. This object includes the two methods Load and Unload to implement the interface. The Load method, handles hooking the add in into the Reflector runtime. Because we are hosting Reflector, it is up to us to create the environment that the add in needs to operate.

Actually, we have it a lot easier than Reflector does because we are not targeting to host every add-in. We are targeting to host and manipulate only this single add in that has caught our eye.

1450-image007.png

From the implementation of the CodeMetricPackage, we see that the Load and Unload methods deal with wiring up event handlers. From this code, we learn that the user interacts with the add in through the CodeMetricWindow. We can skip most of the details in the CodeMetricPackage and redirect our attention to this CodeMetricWindow.

In looking at this object, we see that it is internal sealed, so we cannot instantiate it directly, but we can investigate how the window works and replicate the code on our own. Remember we are not trying to duplicate the usage and behavior of the add in. We only want to access some of the functionality. We can ignore the logic associated with displaying a list of all accessible assemblies. We are not really interested in allowing the user to sort the metric results and we don’t care about the status updates. This allows us to ignore much of the code and hone in on the StartAnalysis method.

In looking at this method, we learn that the heavy lifting is done by the Analyze method of the CodeMetricManager. From this method, we see that the CodeMetricManager includes a collection of Assemblies and a collection of CodeMetrics. Again we aren’t interested in the details in the UI feedback from the event handers, but we are interested in how the CodeMetrics and the Assemblies get populated.

The CodeMetrics collection is initialized in the constructor for the CodeMetricWindow. Reflector registers three different classes of metrics. We are only interested in the MethodCodeMetric.

1450-image008.png

Looking around a bit further, we see that the CodeMetricManager also includes a couple of methods for maintaining the list of Assemblies. We will call the AddAssembly method to specify which assemblies to analyze. Once again, our job is easy because we won’t allow the user to interactively pick an Assembly. We will specify an Assembly, analyze it, and report the results.

We are now ready to add a CodeMetrics method to the ReflectorSupport class by piecing together the pieces that we have found so far.

We will come back to the MethodMetricData class shortly, but so far everything looks like the same code used to initialize the environment earlier.

Now we are ready to initialize and configure the CodeMetricManager. Unlike Reflector, we will only register the one metric. We could change this line to pull in other metrics

Now we let Reflector load the specified assembly and pass it to the codeMetricManager. The CodeMetrics collection serves double duty. Not only does it identify the metrics to run but it also tracks the results. We explicitly add our assembly of interest to the CodeMetricManager, call the Analyze method and gather the results.

The metricResult uses a custom type defined in the Reflector.CodeMember assembly, but it is not the most intuitive to use for our perspective. Plus we don’t want to create an external dependency to Reflector or the CodeMetric add in. This is why we created the MetricMethodData. It will be a simple POCO object (Plain Old CLR Object) that will expose a property for each metric that the MethodCodeMetric calculates.

The MethodMetricData is also suitable for use as a Model in our MVC application.

Our controller may look like this:

For the Index method, we create a simple model with two properties, DisplayName and Location. The view will show the DisplayName and use the location as the name parameter to the Detail action.

1450-image009small.png

In the Detail method we filter the results to show only the results where the Cyclomatic Complexity is greater than 10 or the Number of Instructions is greater than 200. As we outlined in the Metric Driven Refactoring article, these methods warrant a close review and may need to be refactored.

In this example, we are simply displaying the results on a web page. This could easily be incorporated into a daily build to track when key metrics fall outside of standards. There are lots of useful information available from Reflector using similar

Conclusion

While this is not a widely publicized feature, the ability to host .NET Reflector within a .NET application opens up some interesting possibilities. I’ve shown you how to get started, but there are many potentially interesting ways that .NET Reflector can be used, with the methods I’ve described here.

Downloads

Tags: , , , , , ,

  • 6495 views

  • Rate
    [Total: 11    Average: 4.9/5]