Pages

Tuesday, October 28, 2008

.NET Reflector: Soup to Nuts by Andrew Clarke

I found this great post last week on simple-talk and I decided to repost it here on my blog. Happy Learning!

Andrew Clarke

.NET Reflector: Soup to Nuts

22 October 2008

by Andrew Clarke

Nobody could  accuse .NET Reflector of being over-documented. The information is around, but has never, up until now, been pulled together into one place.  We decided to try to make a start! This article supplements the demonstration video by Jason Crease, which comprehensively covers the basics of how to use .NET Reflector, and which should be seen first if you are new to NET Reflector.

NET Reflector has a special place in the pantheon of NET Development tools. It was first written by Lutz Roeder when the .NET Framework was still in beta, and since then has developed alongside it. So ubiquitous has it become that .NET Developers use its' name as a verb, as in 'let's reflector it'. What they mean is to browse and investigate the assemblies that comprise the code with a tool that is capable of showing what's there, and how classes, methods, or entire assemblies relate to each other, within the context of the application.

Why use .NET Reflector?

NET Reflector gives you the means to inspect, analyze, and browse the contents of a .NET component, such as an assembly. It will show you the metadata, IL instructions, resources and XML documentation. This tool can disassemble the instructions into source code in a variety of .NET languages, and translates the other binary information into an intelligible form. It does this through a process called 'reflection', which retrieves information about the various classes, methods, and properties included in a particular assembly (hence the name of the tool).

Most likely, you'll need .NET Reflector to track down performance problems and bugs. It is great for browsing classes, and maintaining, or becoming familiar with, code bases. Some of its features include:

  • An Analyzer option, which can be used to find assembly dependencies, and even windows DLL dependencies.
  • A call tree and inheritance-browser, which will pick up documentation or comments, stored in the xml files that are used to drive Intellisense inside Visual Studio. It will then display this information alongside the associated assemblies.
  • Ability to cross-navigate related documentation (xmldoc), searching for specific types, members and references.
  • Ability to convert your source between languages, such as C# and VB!

All sorts of questions crop up during development that can be answered with .NET Reflector:

  • What line of code is producing that error message?
  • What uses a particular piece of code, and what code does it, in turn, use?
  • How does a class, method, or entire assembly fit into your application?
  • What are the differences between two versions of the same assembly?
  • What features of the API of some legacy code can you use?
  • How do the various parts of your system interact with each other?

When you're working on a large team-based project, .NET Reflector is often the quickest way of getting a clear insight into how the application is working and where the bugs and weaknesses are. There is a great difference between knowing the public interface of a module and actually seeing what the code does.

Once .NET Reflector is combined with add-ins, it can become a tool to facilitate testing and make team-working more effective. There have been many occasions when .NET Reflector has assisted in the recovery of source code after it has been lost.

What is in a .NET Assembly?

Essentially, .NET Reflector allows you to 'see' what is in a .NET assembly.

A .NET language compiler will produce binary CIL (MSIL) code, and any resources that are required, in a file called an 'assembly'. The binary CIL code is written for a theoretical stack-based processor, to make it easy to run assemblies securely on different processors, in any environment supporting the .NET framework.

Then, a just-in-time (JIT) compiler will compile this abstracted, language-independent binary code, and optimise it into actual machine 'native' code for the target processor, and compile all the resources used. The .NET Common Language Runtime (CLR) supplies at least one JIT compiler for every NET-supported computer architecture, so the same set of CIL can be JIT-compiled and run on different architectures.

The CIL includes instructions for loading, storing, initializing, and calling methods on objects, as well as instructions for arithmetic and logical operations, control flow, direct memory access, exception handling, and other operations. It is designed to support polymorphism, inheritance, abstract types, and so on. When a high level language, such as C#, compiles code, it converts it to CIL. The process is reversible but because it is possible for two different high-level syntaxes to produce the same CIL, it may not be exactly the same as the original code.

As well as producing CIL, a .NET compiler will also produce metadata. This will contain everything needed for runtime, sufficient for the code to describe itself during execution without needing type libraries or the Interface Definition Language (IDL), and allowing JIT compilation, linking and introspection to work.

All the classes and class members that are defined in the assembly, as well as the external classes and class members called from the assembly, are described in the metadata, as are all the class methods. This includes details of its parameters, the return type, and the assembly in which it belongs. When the CLR executes CIL, it checks that the metadata of the called method matches that of the calling method. This metadata is used by the process of reflection, and is read by ..NET Reflector.

As well as the metadata, the assembly will also contain any required resources such as attributes, custom attributes, images and strings, and will also contain security information. You can browse this information from within reflector, as shown in Figure 1:

Figure 1: .NET Reflector displaying string resources

The whole assembly is put in an extended version of the portable executable (PE) file, used for executables, object code, and DLLs, and which enables the operating system to recognize common language runtime images. The PE file has only one import – mscoree.dll – which then loads the CLR Header and Data sections, and runs the assembly's just-in-time (JIT) compiler. The PE header's data directory contains a .NET directory entry, which points to a new header in the file containing everything the operating system needs to run it.

.NET Reflector, ILASM and ILDASM

Microsoft supplies two tools for investigating assemblies:

  1. ILASM, the Microsoft IL Assembler. It will take an ASCIII assembly language source-code file, and produce binary IL (MSIL) code. It adds in all the specified resources to create the assembly.
  2. ILDASM, the .NET disassembler. This is part of the .NET framework, and works the other way round. It will produce an 'assembly' file from an IL file. This assembly file is very much like a traditional Assembly source code file.

They are designed to be complementary, so that the output of one can go into the other to produce an identical file to the original.

The ILASM and ILDASM tools are useful, but not sufficient by themselves. Unfortunately, ILDASM is best only with CLI assembly sections, whereas EXE files are in PE format, which ILDASM may not extract correctly. The tool will show method signatures and member variables, but the code will be in .NET byte code.

To be able to extract everything you want from a NET assembly, in the language that was used originally to create the assembly, you will need .NET Reflector. This will also allow you to browse and inspect the other resources in the assembly, and even the XML documentation used by the IDE that created the assembly.

.NET Reflector, as we've said, uses 'Reflection' to do its work. Reflection is used to retrieve information about the various classes, methods, and properties included in a particular assembly. Reflection has to determine the interface, structure, enumeration, or delegate of a type at runtime. This process can be used to examine any .NET code, whether a single class or an entire assembly, and .NET Reflector uses this process to make sense of the contents of an assembly.

Reflector cannot view itself.  It needs to use reflection but, because .NET Reflector needs to work with all versions of the .NET Framework, it uses its own assembly loading infrastructure, which does not rely on the Reflection API. This allows .NET Reflector to load .NET Framework 2.0 assemblies without having the .NET Framework 2.0 installed. However, it also makes it difficult for anyone trying to explore how .NET Reflector works!

Installing, Registering and Launching .NET Reflector

.NET Reflector was written by Lutz Roeder, and has evolved over the entire life of the .NET Framework. It was first released in October 2000 running on .NET Framework 1.0 Beta. It has been updated continuously to work with each new release of the framework and is now on Version 5. This current version supports query expressions and other concepts introduced in C# 3.5 but only if you select ".NET 3.5" as the optimisation within the menu (under View |Options | Disassembler | Optimization). Along the way, a number of additions have been made, on request, sometimes for very general requirements, but occasionally for supporting special uses.

Lutz provided licenses only to users who register their names and email addresses. Registration was, and remains, free, but the software was never open-source or in the public domain. The new owners, Red Gate, will continue with this policy.

The application comes as a zipped package without an installer. It consists of .NET Reflector, the configuration file, license file and readme file.

Reflector can be launched, unregistered, just by clicking on the file in Explorer. However, there are many advantages to registering it with the Windows Shell.

You can register Reflector simply by issuing the following from the command line:

Reflector.exe /register

Once Reflector is integrated into the Windows shell, you can simply right-click on any DLL to open and browse it using Reflector, as shown in figure 2:

Figure 2: Immediately you've registered the program, life becomes easier (Note the context menu)

You can unregister Reflector by issuing:

Reflector.exe /unregister

As discussed, once registered, you'll generally launch Reflector just by clicking on the file in Explorer. However, if you are using reflector as a tool for several projects you may want to invoke it from the command line, using a particular configuration file (more on this later) for each project:

Reflector.exe  /configuration:<filename>

Or, to specify the assembly you wish to examine:

Reflector  <assembly>

This syntax is used by the operating system to display assemblies on startup of .NET Reflector once .NET Reflector is registered.

A Quick Spin through .NET Reflector

The following sections provide a simple example of the "disassembling" power of Reflector, followed by a brief tour of some of the useful features that are enabled once you register the tool with the operating system.

Disassembling Hello World

We can create the simplest possible program, and assemble it:

Figure 3: Assembler source code for  Hello World

And, at the command-line, execute:

C:\WINNT\Microsoft.NET\Framework\v2.0.50727\ilasm.exe helloWorld.il

Run the program:

HelloWorld.exe

You can then do one of the following…

  • Drag and drop it into Reflector
  • Use 'File-> Open' in Reflector (or Ctrl O)
  • Use the following URI (from Notepad, for example):
    code ://HelloWorld:1.0.0.1/<Module>/main()
  • Pop the above URI in your browser (if you ran Reflector.exe /register)
  • Run reflector from the command line

And this is what you'd see…

Figure 4: Hello World in Reflector, with disassembler window set to IL

Notice that the disassembly is not perfect, because some symbolic information is always lost in the original compilation.

Ever wondered how what the PowerShell 'Hello world' looks like?

Figure 5: PowerShell Hello world!

You can right-click any type or Assembly, or press Ctrl-R, to see what the class depends on, and what is exposed by, instantiated by, and assigned by the class.

Figure 6: The analyser looking at class dependencies

Maintaining Assembly lists

When you launch Reflector for the first time, you can choose a default set of assemblies. With .NET Reflector, you can create the groups of assemblies that you are interested in, and open/browse them as a set. The list of assemblies is stored in the Reflector configuration file, Reflector.cfg, and will be loaded next time you open the program. Reflector allows you to create as many assembly lists as you want.

As discussed earlier, not only can you define and store an assembly set into a file, but you can also launch reflector.exe with a particular assembly list file, via the command line.

Creating, selecting and deleting assembly lists in Reflector can be slightly confusing at first because it's not immediately obvious that the place to go to do it is the File | Open List dialog box. You can create new lists from the box by clicking 'Add' to clone the current list.You can also remove assembly lists from the same dialog box.

Figure 7: Managing your assembly lists!

You can switch between assembly lists with the same dialog box, or from the command line. To choose a different set of default assemblies for the current assembly list you should:

  1. Remove all assemblies from the list, by selecting them and using the Delete key
  2. Invoke the Refresh command.
  3. Add the assemblies to make up the set

This feature makes it easy to browse a variety of projects and frameworks, even if they have conflicting names

Sharing URIs

One of the great advantages of registering Reflector is that you can then, from within reflector, 'bookmark' any item within an assembly just by recording a custom URI for it (i.e. putting the URI on the clipboard by pressing Ctrl-Alt-C). Once a URI is clicked on, .NET Reflector displays the item. The URI includes the version and hash value for the assembly.

The URI can be shared with fellow developers to provide a link to the item, since it only requires the other person to have Reflector and the same .dll/.exe to which you created a shortcut. There is also a CodeShortcut add-in that allows you to save URIs on the desktop.

So, if you click on the following URI:

code://System.Xml:2.0.0.0:b77a5c561934e089/System.Xml.XmlConvert

You will see something similar to figure 7:

Figure 7: Opening a file in Reflector from a URI

Reflector Add-ins

A number of add-ins have been written for Reflector most, but not all, of which are open-source. There are many different add-ins and it is best to get an up-to-date listing and description than to rely on an article like this that will soon get out-of-date. An up-to-date listing is always kept at CodeProject:

http://www.codeplex.com/reflectoraddins

Some of the add-ins extend the language that Reflector disassembled to, such as PowerShell, Delphi, and MC++. Other add-ins will analyse assemblies in different ways, such as providing quality metrics, sequence diagrams, class diagrams, dependency structure matrices or dependency graphs. It is possible to use add-ins to search text, save disassembled code to disk, export an assembly to XMI/UML, to do side-by-side comparisons of different versions of code, or to search code.

Add-ins are being added the whole time and include those that: allow the debugging of processes, Facilitate testing by creating stubs and wrappers and allow browsing assemblies in other .NET foundations, such as Silverlight, WWF, Biztalk, SQL Server,  or windows Forms

There are also a range of utility add-ins, one of which allows .NET Reflector to be used within Visual Studio, and another that allows you to save the URI shortcuts of an item onto the desktop

Conclusions

.NET Reflector has always been an essential tool for understanding NET assemblies. There are many reasons for wanting to look at what is going on in an assembly. Any developer will need to do so, even if it is just a tool to help with understanding performance issues. With all the features that have been added over the years in response to requests by developers, and the add-in contributions, it has become a tool that it is impossible to be without when doing .NET software development.

Andrew has, for a while, been helping to flesh out the Wikipedia entry for .NET Reflector, and has modified parts of this article to do so. We're very conscious that .NET Reflector is generally very easy to use, and in that sense doesn't need documentation, but some features, such as the use of URIs, aren't intuitive! If you would like to add to this article, or you spot errors, then send an edited version to editor@Simple-Talk.com and we'll update the article and add you to an acknowledgements section.

Acknowledgements

  • Lutz Roeder, Jason Haley, and countless bloggers about .NET Reflector for supplying material
  • Bart Read for checking the first draft
  • Tony Davis for proofing the Article
  • Colin Patel-Murray for spotting a typo

Andrew Clarke

Author profile: Andrew Clarke

Andrew Clarke has been developing software applications for over 25 years. He is a database expert, but has a particular interest in website-publishing and ECommerce. He recently worked with the first two winners of the 'Apprentice' program at Amstrad, creating various business applications, and since worked with Tim Campbell setting up the 'Bright Ideas Trust'. He also subedits and reviews articles for Simple-Talk.

Search for other articles by Andrew Clarke