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.