Click here to Skip to main content
Click here to Skip to main content
Go to top

SolutionConverter

, 27 May 2010
Rate this:
Please Sign up or sign in to vote.
A Visual Studio solution version converter to upgrade/downgrade solutions between VS versions.

Introduction

SolutionConverter will convert a complete Visual Studio solution from one version to another; it allows you to convert your solutions to both older and newer versions. Currently, Visual Studio 2005, 2008, and 2010 are supported, including Visual C# Express and Visual Basic Express editions. Visual C++ project conversion is not yet supported.

SolutionConverter

Background

I recently upgraded to Visual Studio 2010 at home and encountered the dreaded problem of opening my solutions in older Visual Studio versions. I attend H.I.T. where they have yet to install the new version, so I simply can't open my work on the institute's computers. After doing some research, I found this very nice article, but unfortunately, the application didn't support Visual Studio 2010 solutions when I started working on this project. Another thing I noticed that was missing was that it only changed the solution file and did not touch the project files, which also have to be edited for a smooth conversion.

So I decided to write my own tool with a view into the future, with simpler code which should allow easier extension in the future.

Looking through Google some more, this page popped up, explaining what exactly needed to be changed in the solution and project files to make the conversion work without any issues. I recommend going over it before continuing with the article to easily understand why the code is written the way it is.

Using the Code

This code will demonstrate the following:

  • Loading and analyzing a solution file. Detecting the version of Visual Studio it was intended for, and extracting the list of projects the solution contains.
  • Loading and analyzing the project files.
  • Converting the Solution file into the target version.
  • Working with the XML file, and converting the project files into the target version.

Loading the solution file and determining the solution version:

/// Retrieves the solution version from the file.
protected VisualStudioVersion GetSolutionVersion()
{
    VisualStudioVersion solVer = VisualStudioVersion.Unknown;
    TextReader reader = new StreamReader(this.solutionPath);
    string sln = reader.ReadToEnd();
    reader.Close();

    // Here we loop through the possible Visual Studio versions. 
    // Trying to determine the version of our solution.
    foreach (VisualStudioVersion version in Enum.GetValues(
             typeof(VisualStudioVersion)))
    {
        if (sln.Contains(version.GetStringValue()))
        {
            solVer = version;
        }
    }

    return solVer;
}

VisualStudioVersion is an enum which represents the possible Visual Studio versions. The enum values use a custom attribute called StringValueAttribute, which allows an enum value to have a sort of 'ToString' method, GetStringValue(). GetStringValue() is a generic extension method which extracts the string value from the first StringValueAttribute occurrence on the specific enum value. To make it clearer, here is the enum definition:

/// Lists all possible solution versions.
public enum VisualStudioVersion
{
    /// Unknown Visual Studio version.
    [StringValue("Unknown")]
    Unknown,        
    /// Visual Studio 2002.
    [StringValue("Format Version 7.00")]
    VisualStudio2002 = 2002,
    /// Visual Studio 2003.
    [StringValue("Format Version 8.00")]
    VisualStudio2003 = 2003,
    /// Visual Studio 2005.
    [StringValue("Format Version 9.00")]
    VisualStudio2005 = 2005,
    /// Visual Studio 2008.
    [StringValue("Format Version 10.00")]
    VisualStudio2008 = 2008,
    /// Visual Studio 2010.
    [StringValue("Format Version 11.00")]
    VisualStudio2010 = 2010,
}

As you can see, the string values simply represent the values that will appear in the solution file. By simply checking which of these strings the solution file contains, we can determine the Visual Studio version.

In a similar fashion, we can determine the specific IDE the solution was created in, such as Visual Studio or some of the Express editions.

/// Gets the IDE version.
protected IdeVersion GetIdeVersion()
{
    IdeVersion ideVer = IdeVersion.Default;
    TextReader reader = new StreamReader(this.solutionPath);
    string sln = reader.ReadToEnd();
    reader.Close();

    // Here we loop through the possible Visual Studio versions. 
    // Trying to determine the IDE version.
    foreach (IdeVersion version in Enum.GetValues(typeof(IdeVersion)))
    {
        if (sln.Contains(version.GetStringValue()))
        {
            ideVer = version;
        }
    }
    
    return ideVer;
}

/// Lists all possible IDE versions.
public enum IdeVersion
{
    /// Using the default version or Unknown.
    [StringValue(null)]
    Default,
    /// The full Visual Studio version.
    [StringValue("Visual Studio")]
    VisualStudio,
    /// C# Express Edition.
    [StringValue("Visual C# Express")]
    CSExpress,
    /// Visual Basic Express Edition.
    [StringValue("Visual Basic Express")]
    VBExpress
}

Now that we know what the environment in which the solution was created in, we can continue and convert the solution into the target version in which the solution was created in.

public ConversionResult ConvertTo(VisualStudioVersion solutionVersion, IdeVersion ideVersion)
{
    ...

    bool success = true;
    StreamReader reader = new StreamReader(this.solutionPath);
    FileStream stream = null;
    TextWriter writer = null;
    string sln = reader.ReadToEnd();
    reader.Close();

    // Replace the solution version.
    sln = sln.Replace(this.VisualStudioVersion.GetStringValue(), 
                      solutionVersion.GetStringValue());
    
    // If possible, also update the IDE version as well.
    if (ideVersion != IdeVersion.Default && this.IdeVersion != IdeVersion.Default)
    {
        string oldVersion, newVersion;
        oldVersion = this.IdeVersion.GetStringValue() + " " + 
                        ((int)this.VisualStudioVersion).ToString();
        newVersion = ideVersion.GetStringValue() + " " + 
                   ((int)solutionVersion).ToString();
        sln = sln.Replace(oldVersion, newVersion);
    }
    
    // Now write the file back to the hard drive.
    try
    {
        stream = File.OpenWrite(this.solutionPath);
        writer = new StreamWriter(stream, Encoding.UTF8);
        writer.Write(sln);
    }
    catch (Exception)
    {
        success = false;
    }
    finally
    {
        this.IsReady = false;
        if (stream != null)
        {
            writer.Close();
        }
    }
    
    ...
}

It is that simple, in C#, to convert the solution from one version to another. Now, we also need to convert the projects. We use a Regex expression and a LINQ query to retrieve the path and filename of every project in the solution:

private void ConvertProjects(VisualStudioVersion solutionVersion)
{
    using (StreamReader reader = File.OpenText(this.solutionPath))
    {
        List<string> projectStrings = new List<string>();
        IConverter projectConverter;
        string projectName, path;
        int index;

        // A bit messy, but gets the job done.
        // Retrieve the actual path of the solution.
        path = this.solutionPath.Remove(this.solutionPath.IndexOf(
                  Path.GetFileName(this.solutionPath)), 
                  Path.GetFileName(this.solutionPath).Length);
        this.projectConversionResults.Clear();
        
        // Selects all the strings that contain the "Project("{xxxxxxxx
        //    -xxxx-xxxx-xxxx-xxxxxxxxxxxx}") = "Name", "Name\Name.xxproj","
        // signature.
        projectStrings.AddRange(
           from Match match in this.projectPathRegex.Matches(reader.ReadToEnd())
           where match.Success
           select match.Value);

        // Here we attempt to convert all the projects
        // and save the conversion results.
        foreach (string projectString in projectStrings)
        {
            // Cut string down to only contain the solution relative path.
            projectName = projectString.TrimEnd(new char[] { ' ', ',', '\"' });
            index = projectName.LastIndexOf('\"') + 1;
            projectName = path + projectName.Substring
            (index, projectName.Length - index);
            projectConverter = null;

            // Make sure we don't crash the program
            // by trying to convert a Visual C++ project
            if (!Path.GetExtension(projectName).Contains("vc"))
            {
                projectConverter = new ProjectConverter(projectName);
            }
            else
            {
                // Reserved for a Visual C++ Project converter here.
            }

            if (projectConverter != null)
            {
                this.projectConversionResults.Add(
                         projectConverter.ConvertTo(solutionVersion));
            }
        }
    }
}

The code above creates a ProjectConverter object for each of the solution's projects, invokes the Convert method, and adds the conversion result to the ConversionResults collection.

Now we need to identify the project version we are working on. Since the solution file is a formatted text file, all we have to do is read it, replace the relevant values, and save it. Things change when working on project files. Project files in Visual Studio are XML files that contain all the relevant information and settings for the project. These files also rely on the XML namespace 'http://schemas.microsoft.com/developer/msbuild/2003'. There are four elements and attributes that we are going to need access to (as per this):

  • ToolsVersion attribute
  • ProductVersion element
  • TargetFrameworkVersion element
  • Project attribute of the Import element

Reading the information from the file and into the XElement and XAttribute objects:

public bool Reload()
{
    try
    {
        this.project = XDocument.Load(this.projectPath);
    }
    catch (Exception)
    {
        this.project = null;
    }

    bool ret = false;
    this.vsVersion = VisualStudioVersion.Unknown;
    this.IsReady = false;
    if (this.project != null)
    {
        this.toolsVersion = this.project.Root.Attribute(XName.Get("ToolsVersion"));
        this.productVersion = this.project.Root.Element(XName.Get("PropertyGroup", 
                              this.namespaceName)).Element(XName.Get(
                              "ProductVersion", this.namespaceName));
        this.targetFrameworkVersion = this.project.Root.Element
                (XName.Get("PropertyGroup", 
                                     this.namespaceName)).Element(XName.Get(
                                     "TargetFrameworkVersion", this.namespaceName));
        this.importProject = this.project.Root.Element(XName.Get("Import", 
                             this.namespaceName)).Attribute(XName.Get("Project"));
        this.Name = Path.GetFileNameWithoutExtension(this.projectPath);

        this.vsVersion = this.GetVsVersion();
        this.IsReady = true;
        ret = true;
    }

    return ret;
}

Here is the code that we can use to determine which Visual Studio version this project belongs to:

/// Gets the Visual Studio version of the current project.
private VisualStudioVersion GetVsVersion()
{
    VisualStudioVersion vsVer = VisualStudioVersion.Unknown;

    double product;
    if (Double.TryParse(this.productVersion.Value, out product))
    {
        if (product == 7)
        {
            vsVer = VisualStudioVersion.VisualStudio2002;
        }
        else if (product == 7.1)
        {
            vsVer = VisualStudioVersion.VisualStudio2003;
        }
        else if (product == 8)
        {
            vsVer = VisualStudioVersion.VisualStudio2005;
        }
        else if (product == 9)
        {
            vsVer = VisualStudioVersion.VisualStudio2008;
        }
        else if (product == 10)
        {
            vsVer = VisualStudioVersion.VisualStudio2010;
        }
    }

    return vsVer;
}

And here is the code used to convert the projects. What we do is remove or add the relevant attributes or elements to the XML file, saving it afterwards. Since we are working directly with an XML object, we will receive a valid XML file after this procedure, ready to be used in a different Visual Studio version.

public ConversionResult ConvertTo(VisualStudioVersion vsVersion)
{
    ...
    
    // Calling the relevant method for editing
    // the attributes and elements of the XML.
    switch (vsVersion)
    {
        case VisualStudioVersion.VisualStudio2002:
            this.ConvertToVs2002(vsVersion);
            break;
        case VisualStudioVersion.VisualStudio2003:
            this.ConvertToVs2003(vsVersion);
            break;
        case VisualStudioVersion.VisualStudio2005:
            this.ConvertToVs2005(vsVersion);
            break;
        case VisualStudioVersion.VisualStudio2008:
            this.ConvertToVs2008(vsVersion);
            break;
        case VisualStudioVersion.VisualStudio2010:
            this.ConvertToVs2010(vsVersion);
            break;
    }

    // Saving the Project XML file.
    FileStream fileStream = null;
    XmlTextWriter writer = null;
    ret.ConverterReference = this;
    try
    {
        fileStream = new FileStream(this.projectPath, FileMode.Create);
        writer = new XmlTextWriter(fileStream, Encoding.UTF8);
        writer.Formatting = Formatting.Indented;
        this.project.WriteTo(writer);
        ret.ConversionStatus = ConversionStatus.Succeeded;
    }
    catch (Exception)
    {
        ret.ConversionStatus = ConversionStatus.Failed;
    }
    finally
    {
        this.IsReady = false;
        if (writer != null)
        {
            writer.Close();
        }
    }

    return ret;
}
    
/// Converts to VS2008 version.
private void ConvertToVs2008(VisualStudioVersion vsVersion)
{
    if (this.toolsVersion != null)
    {
        double tools;
        if (Double.TryParse(this.toolsVersion.Value, out tools) && tools != 3.5)
        {
            this.toolsVersion.Value = "3.5";
        }
    }
    else
    {
        this.project.Root.Add(new XAttribute(XName.Get("ToolsVersion"), "3.5"));
    }

    if (this.productVersion != null)
    {
        this.productVersion.Value = "9.0.21022";
    }

    if (this.targetFrameworkVersion != null)
    {
        double framework;
        if (Double.TryParse(
            this.targetFrameworkVersion.Value.Substring(1, 
                   this.targetFrameworkVersion.Value.Length - 2),
                   out framework) && framework > 3.5)
        {
            this.targetFrameworkVersion.Value = "v3.5";
        }
    }
    else
    {
        XElement newToolsVersion = new XElement(XName.Get("TargetFrameworkVersion", 
                                       this.namespaceName), "v2.0");
        this.project.Root.Element(XName.Get("PropertyGroup", 
                                  this.namespaceName)).Add(newToolsVersion);
    }

    if (this.importProject != null && 
             !this.importProject.Value.Contains("$(MSBuildToolsPath)"))
    {
        this.importProject.Value = 
          this.importProject.Value.Replace("$(MSBuildBinPath)", "$(MSBuildToolsPath)");
    }
}

References and Inspirations

History

  • 6 May, 2010 - Version 1.0.3779.3862 - Initial release.
  • 9 May, 2010 - Updated source code (included DLL).
  • 27 May, 2010 - Updated article text.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Arthur Liberman
Software Developer
Israel Israel
A 2nd year student majoring in Computer Science at Holon Institute of Technology.

Comments and Discussions

 
Generalgood work Pinmemberpo17252-Jul-13 5:22 
had little luck wanted 2003 to 2010, and does not :(
GeneralRe: good work PinmemberArthur Liberman2-Jul-13 5:33 
GeneralMy vote of 5 Pinmemberravithejag17-Feb-13 17:07 
GeneralMy vote of 5 Pinmembercjb11029-Jan-13 21:43 
QuestionVS 2012 Pinmemberknoami10-Dec-12 5:37 
QuestionIt works not well Pinmemberbubifengyun18-Sep-12 21:27 
AnswerRe: It works not well PinmemberArthur Liberman18-Sep-12 22:05 
GeneralRe: It works not well Pinmemberbubifengyun19-Sep-12 18:11 
QuestionRegarding convertion of VS unmanaged C++ projects Pinmemberkartsam21-Aug-12 1:51 
AnswerRe: Regarding convertion of VS unmanaged C++ projects PinmemberArthur Liberman21-Aug-12 3:14 
GeneralMy vote of 5 PinmemberSharjith10-Jul-12 8:47 
GeneralMy vote of 5 PinmemberJαved6-Jun-12 9:17 
QuestionWill you implement Visual Studio 11 to Visual Studio 10 convert? PinmemberFrewCen15-May-12 9:00 
QuestionNewer versions? PinmemberAlbert van Peppen11-Apr-12 0:41 
QuestionThe type initializer for '<Module>' threw an exception. Pinmemberclover4leaves2-Feb-12 18:47 
AnswerRe: The type initializer for '' threw an exception. PinmemberArthur Liberman2-Feb-12 23:25 
GeneralI'm not happy because I use c++ PinmemberMartial Spirit31-Aug-11 18:26 
GeneralRe: I'm not happy because I use c++ PinmemberArthur Liberman30-Dec-11 2:33 
GeneralRe: I'm not happy because I use c++ PinmemberAlbert van Peppen11-Apr-12 0:43 
GeneralRe: I'm not happy because I use c++ PinmemberAlex Oren12-Dec-12 4:40 
BugBug Pinmemberwmq_whu15-Jul-11 19:20 
GeneralRe: Bug PinmemberArthur Liberman15-Jul-11 21:42 
GeneralMy vote of 5 Pinmemberipadilla17-May-11 8:20 
GeneralMy vote of 4 Pinmembermagicpapacy17-Jan-11 21:05 
GeneralRe: My vote of 4 PinmemberSAKryukov9-May-11 15:35 
GeneralMy vote of 5 PinmemberUser97010-Aug-10 7:32 
GeneralMy vote of 5 Pinmemberszcomma7-Jul-10 17:39 
GeneralSimply Awesome PinmemberChagantiSri14-Jun-10 23:17 
GeneralMy vote of 1 Pinmembermheskol6-Jun-10 1:16 
GeneralRe: My vote of 1 PinmemberArthur Liberman6-Jun-10 12:06 
Generalconverting just the project file Pinmemberjpeterson26-May-10 10:12 
GeneralRe: converting just the project file PinmemberArthur Liberman26-May-10 11:31 
GeneralRe: converting just the project file PinmemberManish7930-May-10 22:38 
GeneralRidiculous. PinmemberQwertie17-May-10 18:55 
GeneralThis is funny! PinmemberEstys14-May-10 23:07 
GeneralRe: This is funny! PinmemberArthur Liberman14-May-10 23:09 
GeneralRe: This is funny! PinmemberEstys14-May-10 23:47 
GeneralIf u use SharpDevelop IDE you will not have to even think about this PinmemberCuchuk Sergey10-May-10 8:17 
GeneralProject files should be writable PinmemberPerlDev10-May-10 3:18 
GeneralMono PinmemberDaniel Fisher (lennybacon)10-May-10 1:42 
GeneralRe: Mono PinmemberArthur Liberman10-May-10 3:16 
GeneralRe: Mono PinmemberDaniel Fisher (lennybacon)10-May-10 4:56 
GeneralRe: Mono PinmemberPaul Selormey10-May-10 14:47 
GeneralGreate PinmemberKhaniya9-May-10 19:32 
GeneralProblems with the source code PinprotectorMarc Clifton9-May-10 12:30 
GeneralRe: Problems with the source code [modified] PinmemberArthur Liberman9-May-10 12:42 
GeneralRe: Problems with the source code PinprotectorMarc Clifton9-May-10 13:52 
GeneralRe: Problems with the source code PinmvpLuc Pattyn10-May-10 13:45 
GeneralNice work and a feature wish ;-) Pinmemberndinges9-May-10 5:26 
GeneralRe: Nice work and a feature wish ;-) PinmemberDaniel Fisher (lennybacon)10-May-10 1:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 27 May 2010
Article Copyright 2010 by Arthur Liberman
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid