Getting Visual Studio version of a Solution file

4 minute read

In the previous post I talked about how we could determine the installation path of a given Visual Studio version, in this post I’m going to talk a little how we can determine the version of a VS solution file (.sln).

Determining the file version isn’t of relevance if you are opening solutions via the file explorer. If you have multiple versions of VS installed side by side and try to open a solution, the VS Version selector automatically chooses the right visual studio version for you.

However if you intend to automate operations involving solutions files with visual studio (devenv.exe) like cleaning solutions,building solutions,etc. it matters the version file, since only the right VS version will be able to read it (without upgrading the file that is) the file.

The solution file is a text file. The file version (which maps one to one to a VS version) is stored on the first line with the following format:

Microsoft Visual Studio Solution File, Format Version XX

whereas XX is the file version format (not the VS internal version mind you).

Visual Studio has an internal version number and an external name (let’s call it the marketing version). The following table has the mapping between the marketing name and the internal version number

Visual Studio 2010 10.0
Visual Studio 2008 9.0
Visual Studio 2005 8.0
Visual Studio .Net 2003 7.1
Visual Studio .Net 2002 7.0

 

To make things less boring the file version is different from the internal VS version which is different from the marketing version.Smile

For example format version 8.0 corresponds to Visual Studio .Net 2003 (which internally has version 7.1)

The following table contains the marketing version, the internal version number and the file format number for the solution.

Marketing Name Internal Number File Format Number
Visual Studio 2010 10.0 11.00
Visual Studio 2008 9.0 10.00
Visual Studio 2005 8.0 9.00
Visual Studio .Net 2003 7.1 8.00
Visual Studio .Net 2002 7.0 7.00

 

Starting with Visual Studio 2005 there is also a line saying (under the first line)

# Visual Studio YYYY

where YYYY represents the visual studio version we are accustomed to (2005,2008,…)

So in order to determine a version of a given solution file, we could write a small parser (or a regular expression).

What would be great is if Microsoft provided a parser to cope with all version differences, since parsing a solution file is some work and there is always (a slim) possibility that the file format will change in the future.

Unfortunately Microsoft doesn’t provides us a parser. At least publically. MSBuild ships with an internal parser that we can leverage if we are willing to bend them a little by using reflection.Smile (Note: internal methods are not documented for a reason. If we take this path there is always a risk that this will not work when the next version is released).

The code to get information from a file is quite simple (simple snippet ahead)

Note: This code requires .NET 4.0

using System;

using System.Reflection;
using System.IO;


public enum VisualStudioVersion { VS2010 = 100, VS2008 = 90, VS2005 = 80, VSNet2003 = 71, VSNet2002 = 70, Previous = 0, VSNext = 666 };

public class SolutionHelper
{

    public static VisualStudioVersion GetSolutionVersion(string solutionFile)
    {
        Type _solutionParser;
        PropertyInfo solutionParser_version;
        PropertyInfo solutionParser_solutionReader;
        MethodInfo solutionParser_parseSolution;


        _solutionParser = Type.GetType("Microsoft.Build.Construction.SolutionParser, Microsoft.Build, version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);

        if (_solutionParser == null)
        {

            throw new Exception("Can't load msbuild assembly. Is .Net FX 4.0 installed?");
        }

        solutionParser_solutionReader = _solutionParser.GetProperty("SolutionReader", BindingFlags.NonPublic | BindingFlags.Instance);
        solutionParser_version = _solutionParser.GetProperty("version", BindingFlags.NonPublic | BindingFlags.Instance);
        solutionParser_parseSolution = _solutionParser.GetMethod("ParseSolution", BindingFlags.NonPublic | BindingFlags.Instance);
        solutionParser_version = _solutionParser.GetProperty("Version", BindingFlags.NonPublic | BindingFlags.Instance);


        var solutionParserInstance = _solutionParser.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0].Invoke(null);
        using (var streamReader = new StreamReader(solutionFile))
        {
            solutionParser_solutionReader.SetValue(solutionParserInstance, streamReader, null);
            solutionParser_parseSolution.Invoke(solutionParserInstance, null);
        }


        var solutionFileFormatNumber = solutionParser_version.GetValue(solutionParserInstance, null);

        return GetVSVersion(Convert.ToInt32(solutionFileFormatNumber));

    }

    private static VisualStudioVersion GetVSVersion(int version)
    {
        if (version > 11) return VisualStudioVersion.VSNext;

        if (version == 11) return VisualStudioVersion.VS2010;
        if (version == 10) return VisualStudioVersion.VS2008;
        if (version == 9) return VisualStudioVersion.VS2005;
        if (version == 8) return VisualStudioVersion.VSNet2003;
        if (version == 7) return VisualStudioVersion.VSNet2002;

        return VisualStudioVersion.Previous;
    }
}

This snippet is a little basic. It is not production ready. The final code sample available for download will be a little different, more feature complete and robust.

I will also use this post to slip some news that has gone unnoticed to a lot of people. The next version of Visual Studio (dev11) will allows us to use VS2010 solutions without having to do conversions. This will allow us to do full roundt tripping by using the same solution on VS2010 and Dev11  (I guess I can publically talk about this since Dave Mendlen announced this on this year VSLive at Las Vegas this year)

If this post seems of small value, I hope it’s only a temporary thing, since like the previous one it’s part of a series that will culminate in a post with a very usable scenario in Visual Studio. Stay put (hopefully) in the end all things will make senseSmile