.NET Reflector Through the Looking Glass: The Hors d'Oeuvres

14 January 2013

by Michael Sorens

Almost everywhere that .NET applications are developed, there is .NET Reflector. Although there have been some blogs on how to use it, there has never been a documentation. Michael Sorens puts the matter right!

Part 1: Installation and Basic Usage

.NET Reflector is a utility for .NET developers to enable them to explore compiled code as though it was source code. Since its creation more than a decade ago by Lutz Roeder (currently a principal software architect at Microsoft), .NET Reflector has continued to win praise for doing two things, but doing them exceedingly well. The first feature, available since Reflector was first developed, is exploration of compiled code, i.e. examining the source code for assemblies and executables. The second—introduced with version 7—is debugging into compiled code as easily as if you were debugging your own code. It is important to emphasize that both of these tasks are accomplished even when—or particularly when—you do not have the source code! Reflector decompiles any .NET code on the fly into the language of your choice, be it C#, Visual Basic, F#, or IL.

Organization

This series is organized into five parts. The first part discusses obtaining and installing Reflector, as well as getting started with both the desktop edition and the Visual Studio extension. Parts 2, 3, and 4 go through the main features of Reflector. Part 2 covers the “meat and potatoes”, i.e. viewing, filtering, navigating, and debugging. Part 3 goes further into analyzing and exploring assemblies, showing you how to “slice and dice” to get to what you need. Part 4 rounds out the review of features explaining how to exercise the code in front of you or even any arbitrary code on the fly, as well as comparing and managing assemblies. The final part is a one-page companion wallchart that summarizes every feature in parts 2 through 4 and illustrates the key points of navigation in Reflector.

Choosing an Edition

.NET Reflector comes in three editions: Standard, VS, and VSPro, differentiated very simply in Table 1. The standard edition provides just the desktop package that allows you to select any assembly, decompile it, and examine the regenerated code. The other two editions additionally provide a Visual Studio extension to give you a much more integrated experience; you can decompile third-party code on the fly and examine it all within the confines of Visual Studio. This VS extension comes in two editions, one to just view code and one to allow you to step into and debug the code. For expert developers, the debugging capability is so useful as to make the latter option the obvious choice.

Features

Standard

VS

VSPro

Desktop (to view code)

VS Extension (to view code)

VS Extension (to view code)

Table 1 Differentiating .NET Reflector Editions.

Installation

You can purchase Reflector by itself or as part of a .NET tool collection. Red Gate offers a variety of packages to suit almost any audience, detailed in Table 2.

Table 2 Products in the .NET tool collections; these all includes the top-end version of .NET Reflector.

When you launch Red Gate’s installer, it itemizes the products you have purchased, allowing you to select which to install. In the case of Reflector, though, the installer will actually present two options, one for the desktop version and one for the Visual Studio extension (Figure 1). Note that you see these same two choices irrespective of the edition you purchased. Thus, if you purchased the standard edition you may still install the extension as a trial edition, and you get a trial of VSPro. If you purchased the VS edition you still get a trial of VSPro. Not until you enter your license key does it lock in the features of what you actually purchased. Finally, if you purchased a tool collection, the bundle always includes the VS Pro edition.

Figure 1 The installation wizard. You can individually select the desktop module and/or the VS extension. Though it is not obvious from the screen, the VS extension is always the VSPro extension—if you purchased the regular VS extension, it morphs into that when you register your license code.

The installation wizard is very simple. The only choice you have to make, on the second page of the wizard, is where to install to. Upon completion, you should see a report indicating a successful installation (Figure 2).

This presupposes that you want to dig into the .NET framework itself, which may or may not be the case. If so, select the appropriate one. If not, just press Cancel. In most cases you will have a particular third-party DLL that you want to examine. Use the File >> Open Assembly… menu choice (or Ctrl+O) to select each DLL of interest. Your selected assemblies are shown in the Assembly Browser panel (Figure 4, upper left). You can then drill down from the assembly to the namespace to a class and to members of that class.

Figure 4 .NET Reflector desktop layout. The Assembly Browser doubles as the navigation panel; other panels (those with white labels) are auto-populated based on its selection. The panels with black text are either independent of the assembly browser’s selection or not auto-populated.

The Assembly Browser panel is also your navigation panel: what you select in the assembly browser is expanded in these other panels:

The Signature panel reveals the signature of the selected item such as a class, a method or a property. That panel may also provide other information, depending upon your current selection. If a class is selected, the full name of the selected item and its containing assembly are also displayed in this panel.

On the right hand side, the Decompilation (or Source) panel reveals the source code for the current item. If you have selected a class, as in the figure, you will see the signatures of its properties and methods along with its fields and nested types; at the very bottom, though, is a live link labeled Expand Methods that is a convenience feature to let you decompile the entire class with one click. You can still select individual methods in the assembly browser tree to bring them instantly into view in the source panel, but it is often quite convenient to have your list of methods scrollable in one text box. If, on the other hand, your current item is a namespace, the Decompilation panel summarizes the types (classes) in the namespace, and provides a live link labeled Expand Types at the bottom for similar convenience.

Another handy feature is the ability to control the verbosity of the decompiled source to a limited degree. By default, you get all the code associated with the currently selected item, public or otherwise. But if you go to the visibility option (Tools >> Options >> Browser >> Visibility) you can change this All Items default to Public Items Only if desired.

The middle panel on the right, Documentation, only appears when you have a documentation file associated with your assembly. If it is your own assembly, that entails enabling documentation generation for your project so you get an assembly.XML file generated when you create your assembly.DLL file. If it is a third-party assembly you are examining, you need to see if they have supplied the XML file along with the DLL. If so, the XML file must be in the same directory from which you load the DLL into .NET Reflector to be recognized. You have a couple options for viewing the documentation (Tools >> Options >> Disassembler >> Documentation). The default choice for that dropdown is Formatted, meaning the documentation is formatted (as if by Sandcastle or equivalent) and appears in the Documentation panel. Alternatively, if you select XML comments, the XML documentation comments are re-inserted into the decompiled source code. The documentation thus appears in its raw form, in line with the code, and the Documentation panel is suppressed. The documentation option also allows you to turn off documentation display entirely with the None choice.

The final panel on the right, Dependencies, appears only after you open the Analyzer command either through the Tools menu, the keyboard shortcut (Ctrl+R), or the context menu of the current item in the assembly browser. That panel reveals valuable diagnostic information about the consumers of the current item as well as what the current item consumes. Note, however, that unlike the other panels, the Dependency panel does not auto-update when you select a different item in the assembly browser.

While Figure 4 is likely the most common display configuration of .NET Reflector, the panels on the right half of the window vary depending on what you have selected in the assembly browser. Figure 5, for example, displays a resource list where I have selected an item in the Resources folder of the assembly.

Figure 5 When a resource set is selected in the assembly browser, the right hand side shows a list of its resources. This example shows several graphic icons (bitmaps) and one XML file included as resources.

.NET Reflector from Visual Studio

Opening up Visual Studio after installing the .NET Reflector Visual Studio extension you will typically see the .NET Reflector object browser (Figure 6). If it is not open, you can open it at any time from the new .NET Reflector menu.

Figure 6 The .NET Reflector object browser in Visual Studio before a solution is opened.

Yet Another Object Browser

Once you open a project, the .NET Reflector object browser populates with your assemblies (Figure 7), appearing essentially the same as that in the desktop edition: you can drill down from an assembly to a namespace, a type, or a member. At any point you can double-click an item to go to that item in source. The first time you do this for an assembly it will take anywhere from a few seconds to a few minutes, depending on the complexity of the assembly. It decompiles the entire assembly and caches it so that subsequent attempts to reference code are as quick as jumping through your own code.

Figure 7 The .NET Reflector object browser populated with a sample project, showing a drill down within an assembly.

Important note: The Visual Studio object browser is quite separate and distinct from the .NET Reflector object browser. On the surface they appear quite similar, allowing you to drill down through assemblies, namespaces, and classes, even using the same icons. The Visual Studio object browser is actually more similar to the .NET Reflector desktop edition, in that when you select objects in the assembly browser/navigation panel, it displays information about these in its other panels. But it does not have the ability to display source code, only meta-data. For an item in your own solution you can double-click it and Visual Studio takes you to that code; however, double-clicking an item outside your solution does nothing.

Enabling Debugging

The true power of the VS extension, though, is not to just look at code—it is to actually debug it. If you set a breakpoint in your code and then attempt to step into your third-party assembly, you will likely get one of the dialogs in Figure 8. Without .NET Reflector installed, the live links in the lower dialog might be of some limited use. But with Reflector installed, for either of these dialogs, proceed directly to the .NET Reflector object browser and enable debugging for the assembly of interest.

Figure 8 Until you enable an assembly for debugging Visual Studio does not have access to the source code. Either Reflector (top) or Visual Studio (bottom) will give you friendly advice on what to do. In either case, if you have Reflector installed, follow the advice of Reflector to enable debugging!

Enabling debugging for “foreign” assemblies is simply adjusting a Visual Studio option, but .NET Reflector has made this even more convenient. In the object browser, open the context menu and select Enable Debugging. By default Visual Studio is configured to debug only your code, not .NET framework nor third-party assemblies. Unless you have changed that option (Tools >> Options >> Debugging >> General) .NET Reflector displays the dialog in Figure 9.

If you press the big button to enable assembly debugging, then Reflector continues with the next step automatically: decompiling your selected assembly (Figure 10). If you have already viewed code in the assembly as described above, Reflector accesses that generated source from cache; it does not decompile it again. If at any time you want to refresh the cache, select Clear the cache from the .NET Reflector menu; the next time you attempt to access code in that assembly it will decompile it anew.

Figure 10 Enabling debugging is one way to launch the Reflector’s decompilation progress, resulting in this progress dialog.

Managing Multiple Assemblies

Once you have decompiled an assembly you can then view, step through, and set breakpoints in the generated source code just as if you were working with your own solution’s code. Note, however, that this applies only to the one assembly you have decompiled. If you attempt to step into code in a different assembly that you have not yet enabled for debugging, Visual Studio will prompt with one of the dialogs in Figure 8. Unfortunately if you are in the middle of debugging that means you need to stop debugging, enable the assembly of interest for debugging, then restart debugging and work your way back to the point where you were interrupted.

To avoid this interruption, you can prepare all your assemblies before you start debugging. You can do this individually in the .NET Reflector object browser, or you can do them all at once via the (.NET Reflector >> Generate PDBs…) menu choice. That opens the dialog in Figure 11, allowing you to select all the assemblies you might want to debug then decompile them all at once when you select Continue. You will then see the progress report detailed in Figure 10. Depending on the size and complexity, this may take several minutes but it is a one-time operation.

Figure 11 Use the “Generate PDBs” menu choice to select multiple assemblies to decompile at one time, resulting in this selection dialog. After you select one or more assemblies and press Continue, the progress indicator in Figure 10 displays.

You can tell which assemblies you have enabled for debugging in a couple ways. When you open the above menu choice (.NET Reflector >> Generate PDBs…), look for the assemblies that are checked off. Or, to check a single assembly, open its context menu either in the .NET Reflector object browser or even in Visual Studio’s regular solution explorer (under the References folder). In either case look for the presence or absence of the .NET Reflector choice to enable debugging (Figure 12). But take care when using solution explorer. As the figure shows, it also provides a View in Object Browser menu choice. That choice, however, is for the Visual Studio object browser and has nothing to do with the .NET Reflector object browser or with debugging.

Figure 12 You can check whether a single assembly has debugging enabled in Visual Studio’s solution explorer by the presence of the “Enable Debugging” choice. Watch out for the “View in Object Browser” choice though, which refers to Visual Studio’s—not Reflector’s—object browser.

The True Value of Reflector

With an understanding of the setup and configuration of .NET Reflector, now you are ready to do your debugging! Set an appropriate breakpoint in your own code to get you to that call to the third-party assembly you have been itching to open up. Then just step right into it with your debugger. On the one hand it is rather anticlimactic because it is so seamless and works so well. But on the other hand, that very elegance is what makes me still marvel at the power at my fingertips, thanks to Reflector.

By allowing you to step into and debug foreign assemblies without having the original source code, Reflector provides substantial productivity gains. Thus, if you set a breakpoint in your code on a line that calls a foreign assembly, just use Visual Studio’s standard Step Into command when you reach that line while debugging and you automatically navigate to the decompiled source code for that assembly, as generated by Reflector. Most often you would want to do this if you are experiencing some exception from third-party code. When you can follow the code execution right into the foreign assembly, you can likely find the cause of an exception in short order indeed!