How to download .NET Reflector 6 for free

If you like, you can skip the introduction and jump to Downloading .NET Reflector.

A brief history of .NET Reflector

In the beginning

In case you somehow missed it, .NET Reflector is a class browser, analyzer, and decompiler for .NET assemblies. Originally created by Lutz Roeder, it proved to be an extremely useful tool, making lists such as MSDN’s Ten Must-Have .NET Tools Every Developer Should Download Now. Best of all, .NET Reflector was released to the community for free, with Roeder developing and supporting the tool for over 8 years.

The downfall

However, in August 2008, Red Gate Software bought .NET Reflector. At the time, Roeder was told that Red Gate will continue to provide the free community version. Red Gate themselves stated that The first thing we are doing is continuing to offer the software to the community for free downloading. It didn’t stay that way. In February 2011, Red Gate announced that from version 7, .NET Reflector would no longer be free.

This might have been acceptable, but for the fact that free copies .NET Reflector expired after several months. Upon expiry, users were prompted to update to the latest version; if they declined, .NET Reflector would simply exit, although some versions contained a time-bomb that completely deleted .NET Reflector if it was too old. Effectively, all existing free users of .NET Reflector would be forced to either stop using .NET Reflector, or pay for version 7. The community backlash was immediate.

Scorched earth

After two months, Red Gate finally relented, partially reversing their decision to disable the free version of .NET Reflector. However, rather than once more releasing a free community edition, they instead only enabled existing free versions of .NET Reflector to update to a version that does not expire. For those users who had a self-deleting version, Red Gate offered no help. They also offered no way for new users to download the free version of .NET Reflector.

To date, Red Gate’s promise of maintaining a free community version remains broken. It has been more than a year since they first announced that they were going back on their word, but nothing has changed. While there are now more alternatives to .NET Reflector than ever, it is unfortunate that this whole situation came about in the first place.

A brief history of LookingGlass

Like most things developers create, LookingGlass was born out of curiosity. The question: Is it possible to download .NET Reflector from Red Gate without already having a copy? After all, old versions need a way to download updates somehow. Updating an old copy of .NET Reflector while running Wireshark quickly provided the answer: .NET Reflector makes two HTTP requests to Red Gate’s servers, the first to determine the URL of the update, and the second to actually download it.

LookingGlass is a small program that reproduces .NET Reflector’s requests to Red Gate’s servers and saves the result to a file. A few hacks were necessary to perfectly emulate how .NET Reflector makes its HTTP requests (See line 87), including not reusing the HTTP connection despite the Keep-Alive headers (.NET Reflector’s built-in updater launches as a separate process). From Red Gate’s side, it should be impossible to tell the difference between the two.

Downloading .NET Reflector

Simply save the below code as a file (For example, LookingGlass.cs), compile it and run the resulting program. It will save the downloaded file to the same directory as the program itself. If necessary, you can download Visual C# Express for free, or use the C# compiler (csc.exe) that is included with every version of the .NET framework to compile this code. LookingGlass should work perfectly on .NET 2.0 and above.

// This is free and unencumbered software released into the public domain.
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;

namespace LookingGlass
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 0)
                QueryDownloadUrl(@"http://reflector.red-gate.com/Reflector.version");
            else
                Download(args[0]);
        }

        static void QueryDownloadUrl(string versionUrl)
        {
            Console.WriteLine("Phase 1: Getting download URL...");

            HttpWebRequest request = GetHttpWebRequest(versionUrl);
            string downloadUrl = null;
            string data;

            using (WebResponse response = request.GetResponse())
            {
                data = ReadResponse(response);

                foreach (string line in data.Split('\n'))
                {
                    if (line.StartsWith("http", StringComparison.OrdinalIgnoreCase))
                    {
                        // If this version number stops working, search for other known .NET Reflector version numbers; Red Gate's own forums mention a few
                        downloadUrl = line.Trim('\r').Replace("{Client}", "Reflector").Replace("{Version}", "6.1.0.11");
                        break;
                    }
                }
            }

            if (downloadUrl == null)
            {
                Console.WriteLine("Failed to find download URL. Returned data was:");
                Console.WriteLine(data);
                Console.ReadLine();
            }
            else
            {
                Console.WriteLine("Launching download process...");
                Process.Start(Process.GetCurrentProcess().MainModule.FileName, downloadUrl);
                Console.WriteLine("Phase 1 complete.");
            }
        }

        static void Download(string downloadUrl)
        {
            Console.WriteLine("Phase 2: Downloading...");

            HttpWebRequest request = GetHttpWebRequest(downloadUrl);
            string path;

            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                path = GetDownloadFilename(response);

                if (response.StatusCode == HttpStatusCode.OK && !string.IsNullOrEmpty(path))
                {
                    Console.WriteLine("Saving to {0}...", path);
                    SaveResponse(response, path);
                    Console.WriteLine("Done!");
                }
                else
                {
                    Console.WriteLine("Download failed; server returned status code {0}. Returned data was:", response.StatusCode);
                    Console.WriteLine(ReadResponse(response));
                    Console.ReadLine();
                }
            }
        }

        static HttpWebRequest GetHttpWebRequest(string url)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Headers.Add("Cache-Control: no-cache, no-store, max-age=0");
            // Emulate the manner in which the headers are handled
            FieldInfo usesProxySemantics = typeof(ServicePoint).GetField("m_ProxyServicePoint", BindingFlags.NonPublic | BindingFlags.Instance);
            usesProxySemantics.SetValue(request.ServicePoint, true);
            return request;
        }

        static string GetDownloadFilename(HttpWebResponse response)
        {
            string contentDisposition = response.Headers["content-disposition"];

            if (string.IsNullOrEmpty(contentDisposition))
                return null;

            string filenameField = "filename=";
            int index = contentDisposition.IndexOf(filenameField, StringComparison.CurrentCultureIgnoreCase);

            if (index < 0)
                return null;

            return contentDisposition.Substring(index + filenameField.Length).Trim('"');
        }

        static string ReadResponse(WebResponse response)
        {
            using (Stream netStream = response.GetResponseStream())
            {
                using (StreamReader reader = new StreamReader(netStream))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        static void SaveResponse(WebResponse response, string path)
        {
            byte[] buffer = new byte[4 * 1024];
            int bytesRead;

            using (Stream netStream = response.GetResponseStream())
            {
                if (File.Exists(path))
                    File.Delete(path);

                using (FileStream fileStream = File.Create(path))
                {
                    while ((bytesRead = netStream.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        Console.Write(".");
                        fileStream.Write(buffer, 0, bytesRead);
                    }
                }
            }

            Console.WriteLine();
        }
    }
}

Where to from here?

Once you have .NET Reflector up and running, be sure to try out some of the many free community add-ins available on .NET Reflector’s site and Codeplex. Tell your friends about it. And above all, don’t always believe a company when they tell you they’ll keep a product free.

Announcing Manta

It’s taken much too long, but I’ve finally created an online project for Manta, which until recently didn’t even have an official name. Manta is an open source .NET library that currently features a variety of I/O- and web-related classes that simplify and ease solving hard problems.

On the I/O side, Manta offers support for managing hardlinks, reparse points, junctions, symbolic links. It also comes with unit tests and documented methods and classes.

On the web side, Manta enables easy manipulation of URLs, RSS feeds and phpBB posts. It too features unit tests and documentation.

You can find out more about Manta or download it from CodePlex.

C# Quines

Writing quines is a fun way to while away an afternoon, especially when trying to make them as short as possible. This is the result of my attempt at writing the shortest C# quine possible (155 characters, source file):

class q{static void Main(){string s="class q{{static void Main(){{string s={0}{1}{0};System.Console.Write(s,'{0}',s);}}}}";System.Console.Write(s,'"',s);}}

PS: I came across a shorter C# 3.5 quine (149 characters), which uses var instead of string. Clever. Oh well, c’est la vie.

Crouching Enumerator, Hidden Boxing

A few months ago, I was playing around with a simple C# permutation generator to build a word list, with each character position having its own list of characters to iterate through:

List.Enumerator enumerator = characterList.GetEnumerator();

while (condition) {
	if (!enumerator.MoveNext()) {
		enumerator.Reset();
		enumerator.MoveNext();
	}

	Consume(enumerator.Current);
}

Oddly, I was getting a compilation error on line 5, as enumerator doesn’t have a Reset() method despite the IEnumerator interface defining one. MSDN quickly cleared things up, though, revealing that List<T>.Enumerator explicitly implements the method as void IEnumerator.Reset(), which is implicitly private. You can still call private interface methods if you first cast to that interface, so I changed line 5 to the following:

		((IEnumerator) enumerator).Reset();

I thought that was the end of it, but bizarely, the second call to MoveNext() on line 6 also returns false. I confirmed that the private Reset() method actually implements reset functionality, which it certainly does, yet it seemed to have no effect.

The answer lies in the fact that List<T>.Enumerator is a struct. This means that behind the scenes, the cast to IEnumerator is creating a boxed copy of enumerator, and that’s what’s being reset. The original is left untouched, so the call to MoveNext() will naturally return false. Rather than trying to keep the boxed copy that you get from the cast, the correct solution is to use IEnumerator<T> from the outset, rather than List<T>.Enumerator:

IEnumerator enumerator = characterList.GetEnumerator();

while (condition) {
	if (!enumerator.MoveNext()) {
		enumerator.Reset();
		enumerator.MoveNext();
	}

	Consume(enumerator.Current);
}

All this drama could have been avoided if I hadn’t checked the return type for List<T>.GetEnumerator() and used that. So much for more explicit typing being helpful.

Still, it’s rather odd that List<T>.Enumerator is both public and a struct. The former encourages its direct use, and the latter results in the problem I was experiencing. Sadly, this design is constant throughout the System.Collections.Generic namespace. By contrast, the equivalent non-generic collection is ArrayList, and there, the GetEnumerator() method returns an IEnumerator, which is implemented by a private class nested within the ArrayList type – a design that is constant throughout the rest of the System.Collections namespace, and is, in my opinion, better for everyone.