Visual Studio Version Interoperability

Visual Studio Version Interoperability









Introduction

Microsoft officially released Visual Studio 2008 in February 2008 and Visual Studio 2010 in May 2010.  However, many users were disappointed that the file format for the older Visual Studio "project files" was not backwards compatible.

  • If you open an older Visual Studio project with a newer version of Visual Studio, you will be prompted for a conversion wizard.
  • The conversion is "one way"... once converted, the project can no longer be opened with the previous version.
  • If you selected "no" at the conversion wizard, Visual Studio just exits, and the project is not opened

So, what if you decided to do a phased rollout of the newer Visual Studio... where some programmers are still running VS2005/VS2008?  Any project that been touched by VS2008/VS2010 is now inaccessible to the users of VS2005/VS2008.

One solution would be to keep both the older Visual Studio on your system when you install the newer version.  Both versions co-exists peacefully.   However, that doesn't solve the problem of "accidently" converting a project.  Also, Visual Studio is huge (particularly if you install the MSDN documentation).

But wait... I heard that Visual Studio has the ability to target multiple versions of the  Framework!   Yes, that's true, but the project files that target the .Net Framework 2.0 are still not backwards compatible with the older versions.

Another problem exists when you download source code from the internet that's in a different format from what you've got installed.

Project Converter

The technique described in this article uses an external utility to convert between the Visual Studio project file formats.  It can be run on systems which do not have any version of Visual Stuido installed at all.

ProjectConverter Screen Shot

The program can be launched as a normal windows application or it can be installed in order to get a new Explorer "shell extension" that adds ProjectConverter to the "Open With" option when right-clicking on a *.sln file.

"Open With" screen shot

Note: This utility just edits the project files... it does not edit the source code itself.  It does not attempt to resolve references that may be specific to a particular version of the Framework.  For example, if you have a .Net Framework 3.5 application that uses LINQ, this utility will not somehow magically convert the source code so that it will compile on VS2005 .

The Solution (*.sln) file

The solution file is a text-based file that contains information about one or more projects.  Below is a typical solution file:

Microsoft Visual Studio Solution File, Format Version 10.00

# Visual Studio 2008

Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "ProjectConverter", 

 "ProjectConverter.vbproj", "{B637ACFD-0AFC-4FBB-A8C0-602B5ABA62F0}"

EndProject

Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "Setup", "Setup\Setup.vdproj", 

 "{09667F41-0E35-4D40-A0A9-E71BA6740D93}"

EndProject

Global

  GlobalSection(SolutionConfigurationPlatforms) = preSolution

    Debug|Any CPU = Debug|Any CPU

    Release|Any CPU = Release|Any CPU

  EndGlobalSection

  GlobalSection(ProjectConfigurationPlatforms) = postSolution

    {B637ACFD-0AFC-4FBB-A8C0-602B5ABA62F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

    {B637ACFD-0AFC-4FBB-A8C0-602B5ABA62F0}.Debug|Any CPU.Build.0 = Debug|Any CPU

    {B637ACFD-0AFC-4FBB-A8C0-602B5ABA62F0}.Release|Any CPU.ActiveCfg = Release|Any CPU

    {B637ACFD-0AFC-4FBB-A8C0-602B5ABA62F0}.Release|Any CPU.Build.0 =Release|Any CPU

    {09667F41-0E35-4D40-A0A9-E71BA6740D93}.Debug|Any CPU.ActiveCfg = Debug

    {09667F41-0E35-4D40-A0A9-E71BA6740D93}.Release|Any CPU.ActiveCfg = Release

  EndGlobalSection

  GlobalSection(SolutionProperties) = preSolution

    HideSolutionNode = FALSE

  EndGlobalSection

EndGlobal

You can see file file format marker and Project version is in the first few lines.   The version number in this file sets the number you see in the solution file's icon (er, well... using the following "translation"):

Product Name Product Version File Format
Visual Studio .Net v7.0 7
Visual Studio .Net 2003  v7.1 8
Visual Studio 2005 v8.0 9
Visual Studio 2008 v9.0 10
Visual Studio 2010 v10.0 11

The Project (*.vbproj, *.csproj, and *.vcproj) files

Below is a typical XML-based project file.   The items that require tweaking to be compatible between the different versions are highlighted in yellow.

<?xml version="1.0" encoding="utf-8"?>

<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>

    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>

    <ProductVersion>9.0.21022</ProductVersion>

    <SchemaVersion>2.0</SchemaVersion>

    <ProjectGuid>{B637ACFD-0AFC-4FBB-A8C0-602B5ABA62F0}</ProjectGuid>

    <OutputType>WinExe</OutputType>

    <StartupObject>ProjectConverter.My.MyApplication</StartupObject>

    <RootNamespace>ProjectConverter</RootNamespace>

    <AssemblyName>ProjectConverter</AssemblyName>

    <FileAlignment>512</FileAlignment>

    <MyType>WindowsForms</MyType>

    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>

    <OptionExplicit>On</OptionExplicit>

    <OptionCompare>Binary</OptionCompare>

    <OptionStrict>Off</OptionStrict>

    <OptionInfer>On</OptionInfer>

    <ApplicationIcon>Icon1.ico</ApplicationIcon>

  </PropertyGroup>

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

    <DebugSymbols>true</DebugSymbols>

    <DebugType>full</DebugType>

    <DefineDebug>true</DefineDebug>

    <DefineTrace>true</DefineTrace>

    <OutputPath>bin\Debug\</OutputPath>

    <DocumentationFile>

    </DocumentationFile>

    <NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>

  </PropertyGroup>

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">

    <DebugType>pdbonly</DebugType>

    <DefineDebug>false</DefineDebug>

    <DefineTrace>true</DefineTrace>

    <Optimize>true</Optimize>

    <OutputPath>bin\Release\</OutputPath>

    <DocumentationFile>

    </DocumentationFile>

    <NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>

  </PropertyGroup>

  <ItemGroup>

    <Reference Include="System" />

    <Reference Include="System.Data" />

    <Reference Include="System.Deployment" />

    <Reference Include="System.Drawing" />

    <Reference Include="System.Windows.Forms" />

    <Reference Include="System.Xml" />

  </ItemGroup>

  <ItemGroup>

    <Import Include="Microsoft.VisualBasic" />

    <Import Include="System" />

    <Import Include="System.Collections" />

    <Import Include="System.Collections.Generic" />

    <Import Include="System.Data" />

    <Import Include="System.Drawing" />

    <Import Include="System.Diagnostics" />

    <Import Include="System.Windows.Forms" />

  </ItemGroup>

  <ItemGroup>

    <Compile Include="fmMain.vb">

      <SubType>Form</SubType>

    </Compile>

    <Compile Include="fmMain.Designer.vb">

      <DependentUpon>fmMain.vb</DependentUpon>

      <SubType>Form</SubType>

    </Compile>

    <Compile Include="My Project\AssemblyInfo.vb" />

    <Compile Include="My Project\Application.Designer.vb">

      <AutoGen>True</AutoGen>

      <DependentUpon>Application.myapp</DependentUpon>

    </Compile>

    <Compile Include="My Project\Resources.Designer.vb">

      <AutoGen>True</AutoGen>

      <DesignTime>True</DesignTime>

      <DependentUpon>Resources.resx</DependentUpon>

    </Compile>

    <Compile Include="My Project\Settings.Designer.vb">

      <AutoGen>True</AutoGen>

      <DependentUpon>Settings.settings</DependentUpon>

      <DesignTimeSharedInput>True</DesignTimeSharedInput>

    </Compile>

  </ItemGroup>

  <ItemGroup>

    <EmbeddedResource Include="fmMain.resx">

      <DependentUpon>fmMain.vb</DependentUpon>

      <SubType>Designer</SubType>

    </EmbeddedResource>

    <EmbeddedResource Include="My Project\Resources.resx">

      <Generator>VbMyResourcesResXFileCodeGenerator</Generator>

      <LastGenOutput>Resources.Designer.vb</LastGenOutput>

      <CustomToolNamespace>My.Resources</CustomToolNamespace>

      <SubType>Designer</SubType>

    </EmbeddedResource>

  </ItemGroup>

  <ItemGroup>

    <None Include="app.config" />

    <None Include="My Project\Application.myapp">

      <Generator>MyApplicationCodeGenerator</Generator>

      <LastGenOutput>Application.Designer.vb</LastGenOutput>

    </None>

    <None Include="My Project\Settings.settings">

      <Generator>SettingsSingleFileGenerator</Generator>

      <CustomToolNamespace>My</CustomToolNamespace>

      <LastGenOutput>Settings.Designer.vb</LastGenOutput>

    </None>

  </ItemGroup>

  <ItemGroup>

    <Content Include="Icon1.ico" />

  </ItemGroup>

  <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />

  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

       Other similar extension points exist, see Microsoft.Common.targets.

  <Target Name="BeforeBuild">

  </Target>

  <Target Name="AfterBuild">

  </Target>

  -->

</Project>

The differences in the two file formats are summarized below:

Element VS2005 VS2008 VS2010
Tools Version absent 3.5 4.0
Product Version 8.0.50727  9.0.21022 absent
Old Tools Version absent 2.0 (if converted) 3.5
Target Framework Version absent 2.0 4.0
Bootstrapper .NET Framework 2.0 .NET Framework 2.0 (x86) many
Import Project $(MSBuildBinPath) $(MSBuildToolsPath) $(MSBuildToolsPath

Other Project types

The Deployment Project type (*.vdproj) files appear to be backward compatible and therefore do not require conversion.

There is a completely new project file format for C/C++ in VS2010, so this utility will not be able to convert C/C++ projects to or from this new format. Sorry...

Note: No other project types are supported by this utility.

Source Code

If you're interested in the inner-workings of the ProjectConverter application, take a look at the following, otherwise just skip this section and download the installer file below.

We edit the text-based solution file first and get the list of subordinate projects that are associated with the solution.  The solution file has a binary "marker" at the beginning of  the file... so that means we need to use both the binary and text readers/writers to manipulate the file.

' First we read the solution file and build a list of 

' project files that we find inside.  We need both a binary

' reader and stream reader.

fs = New FileStream(tbSolutionFile.Text, FileMode.Open)

sr = New StreamReader(fs)

br = New BinaryReader(fs)



' let's read Unicode Byte Order Mark (with CRLF)

bom = br.ReadBytes(5)



' if we don't have a BOM, we create a default one

If bom(0) <> &HEF Then

    bom(0) = &HEF

    bom(1) = &HBB

    bom(2) = &HBF

    bom(3) = &HD

    bom(4) = &HA



    ' rewind the streamreaders

    fs.Seek(0, SeekOrigin.Begin)

End If



sc = New StringCollection

Do While sr.Peek() >= 0

    buf = sr.ReadLine



    ' no need for any fancy parsing routines

    If buf.StartsWith("Microsoft Visual Studio Solution File, Format Version") Then

        ' Yes, I know, this looks counter intuitive... but format version numbers

        ' do not match the Visual Studio version numbers.

        Select Case ConvertTo

            Case Versions.Version8

                sc.Add("Microsoft Visual Studio Solution File, Format Version 9.00")

            Case Versions.Version9

                sc.Add("Microsoft Visual Studio Solution File, Format Version 10.00")

            Case Versions.Version10

                sc.Add("Microsoft Visual Studio Solution File, Format Version 11.00")

        End Select

        Continue Do

    End If



    If buf.StartsWith("# Visual") Then

        Select Case ConvertTo

            Case Versions.Version8

                sc.Add("# Visual Studio 2005")

            Case Versions.Version9

                sc.Add("# Visual Studio 2008")

            Case Versions.Version10

                sc.Add("# Visual Studio 2010")

        End Select

        Continue Do

    End If



    sc.Add(buf)



    ' parse the project files

    If buf.StartsWith("Project(") Then

        Dim ProjParts(), ProjFile, ProjExt As String



        ProjParts = buf.Split(","c)

        If ProjParts.Length = 3 Then

            ProjFile = Path.GetDirectoryName(tbSolutionFile.Text) & "\" & _

             ProjParts(1).Trim.Trim(Chr(34))

            ' we only support VB, C#, and C/C++ project types so that means 

            ' we do not attempt to convert Deployment Projects (*.vdproj)

            Try

                ProjExt = Path.GetExtension(ProjFile)

                If ProjExt = ".vbproj" Or ProjExt = ".csproj" Then

                    ' convert the VB/C# project files

                    If ConvertProject(ProjFile, ExistingVersion) Then

                        ProjCount += 1

                    End If

                ElseIf ProjExt = ".vcproj" Or ProjExt = ".vcxproj" Then

                    ' convert the C/C++project files

                    If ConvertVCProject(ProjFile, ExistingVersion) Then

                        ProjCount += 1

                    End If

                End If

            Catch ex As Exception

                MsgBox("Could not convert project file" & vbCr & ex.Message, _

                 MsgBoxStyle.Critical, "Error")

                ' TODO:  Perform a "rollback" on those files that

                ' got converted so far?

                Exit Sub

            End Try

        End If

    End If

Loop

fs.Close()



' OK now it's time to save the converted Solution file

fs = New FileStream(tbSolutionFile.Text, FileMode.Open)

sw = New StreamWriter(fs)

bw = New BinaryWriter(fs)



' write the BOM bytes (using the BinaryWriter)

bw.Write(bom)

bw.Flush()



' write the remaining text (using the StreamWriter)

For Each buf In sc

    sw.WriteLine(buf)

Next

sw.Flush()

fs.Close()

Next, we edit the XML-based project files using the XML Document Object Model (XDOM) routines.  Admittedly, using XDOM is a bit more complicated, since you're selecting elements and attributes to add/delete/modify.

'

' Convert the VB and C# Project file

'

Private Function ConvertProject(ByVal ProjFile As String, ByVal ExistingVersion As Double) _

 As Boolean

    Dim xd As Xml.XmlDocument

    Dim xnsm As Xml.XmlNamespaceManager

    Dim xn, xtemp As Xml.XmlNode

    Dim temp As String



    xd = New Xml.XmlDocument

    xd.Load(ProjFile)



    xnsm = New Xml.XmlNamespaceManager(New Xml.NameTable())

    xnsm.AddNamespace("prj", "http://schemas.microsoft.com/developer/msbuild/2003")



    ' let's do a quick sanity check to make sure we've got the

    ' version that we're expecting.  Hey, it happens

    xn = xd.SelectSingleNode("/prj:Project", xnsm)

    If xn IsNot Nothing Then

        xtemp = xn.Attributes("ToolsVersion")

        If xtemp IsNot Nothing Then

            ' converting to VS2008, but project is already at VS2008

            If ConvertTo = Versions.Version9 And xtemp.InnerText = "3.5" Then

                ' exit quietly

                Return False

            End If

            ' converting to VS2010, but project is already at VS2010

            If ConvertTo = Versions.Version10 And xtemp.InnerText = "4.0" Then

                ' exit quietly

                Return False

            End If

        Else

            ' If converting to VS2005, but project is already at VS2005

            If ConvertTo = Versions.Version8 Then

                ' exit quietly

                Return False

            End If

        End If

    Else

        ' no such node?  That's bad, very bad...

        Throw New ApplicationException("Invalid project file")

    End If



    ' the ToolsVersion attribute (we already have selected that node)

    xn.Attributes.Remove(xn.Attributes("ToolsVersion"))

    Select Case ConvertTo

        Case Versions.Version8

            ' it gets removed

        Case Versions.Version9

            ' add the attribute

            xtemp = xd.CreateAttribute("ToolsVersion")

            xtemp.Value = "3.5"

            xn.Attributes.Append(CType(xtemp, Xml.XmlAttribute))

        Case Versions.Version10

            ' add the attribute

            xtemp = xd.CreateAttribute("ToolsVersion")

            xtemp.Value = "4.0"

            xn.Attributes.Append(CType(xtemp, Xml.XmlAttribute))

    End Select



    ' the ProjectVersion element

    xn = xd.SelectSingleNode("/prj:Project/prj:PropertyGroup/prj:ProductVersion", xnsm)

    If xn IsNot Nothing Then

        Select Case ConvertTo

            Case Versions.Version8

                xn.InnerText = My.Settings.VS2005_Version

            Case Versions.Version9

                xn.InnerText = My.Settings.VS2008_Version

            Case Versions.Version10

                ' not used... strange

                xn.InnerText = ""

        End Select

    End If



    ' the OldToolsVersion element in the first PropertyGoup

    xn = xd.SelectSingleNode("/prj:Project/prj:PropertyGroup", xnsm)

    xtemp = xd.SelectSingleNode("/prj:Project/prj:PropertyGroup/prj:OldToolsVersion", xnsm)

    If xtemp IsNot Nothing Then

        xn.RemoveChild(xtemp)

    End If

    Select Case ConvertTo

        Case Versions.Version8

            ' it gets removed

        Case Versions.Version9

            ' add a new element

            ' Note: this doesn't appear to be added in every project type, 

            ' but I bet it's harmless

            xtemp = xd.CreateElement("OldToolsVersion", xnsm.LookupNamespace("prj"))

            xtemp.AppendChild(xd.CreateTextNode("2.0"))

            xn.AppendChild(xtemp)

        Case Versions.Version10

            ' add a new element

            xtemp = xd.CreateElement("OldToolsVersion", xnsm.LookupNamespace("prj"))

            xtemp.AppendChild(xd.CreateTextNode("3.5"))

            xn.AppendChild(xtemp)

    End Select



    ' remove/tweak the optional TargetFrameworkVersion element

    xn = xd.SelectSingleNode("/prj:Project/prj:PropertyGroup", xnsm)

    xtemp = xd.SelectSingleNode("/prj:Project/prj:PropertyGroup/prj:TargetFrameworkVersion", _

     xnsm)

    If xtemp IsNot Nothing Then

        xn.RemoveChild(xtemp)

    End If

    Select Case ConvertTo

        Case Versions.Version8

            ' it gets removed

        Case Versions.Version9

            xtemp = xd.CreateElement("TargetFrameworkVersion", xnsm.LookupNamespace("prj"))

            xtemp.AppendChild(xd.CreateTextNode("v3.5"))

            xn.AppendChild(xtemp)

        Case Versions.Version10

            xtemp = xd.CreateElement("TargetFrameworkVersion", xnsm.LookupNamespace("prj"))

            xtemp.AppendChild(xd.CreateTextNode("v4.0"))

            xn.AppendChild(xtemp)

    End Select



    ' the optional BootStrapper elements.  I only remove the inappropriate values, I

    ' do not attempt to add the newer values

    Select Case ConvertTo

        Case Versions.Version8

            ' alter the value for the ProductName element using the older framework tag

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Net.Framework.2.0""]", xnsm)

            If xn IsNot Nothing Then

                xtemp = xn.SelectSingleNode("prj:ProductName", xnsm)

                xtemp.FirstChild.Value = ".NET Framework 2.0"

            End If

            ' remove the newer framework options

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Net.Framework.3.0""]", xnsm)

            If Not xn Is Nothing Then

                xn.ParentNode.RemoveChild(xn)

            End If

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Net.Framework.3.5""]", xnsm)

            If Not xn Is Nothing Then

                xn.ParentNode.RemoveChild(xn)

            End If

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Net.Client.3.5""]", xnsm)

            If Not xn Is Nothing Then

                xn.ParentNode.RemoveChild(xn)

            End If

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Net.Framework.3.5.SP1""]", xnsm)

            If Not xn Is Nothing Then

                xn.ParentNode.RemoveChild(xn)

            End If

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Windows.Installer.3.1""]", xnsm)

            If Not xn Is Nothing Then

                xn.ParentNode.RemoveChild(xn)

            End If

        Case Versions.Version9

            ' alter the value for the ProjectName using the newer framework tag

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Net.Framework.2.0""]", xnsm)

            If xn IsNot Nothing Then

                xtemp = xn.SelectSingleNode("prj:ProductName", xnsm)

                xtemp.FirstChild.Value = ".NET Framework 2.0 %28x86%29"

            End If



            ' remove the newer framework options

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Net.Client.3.5""]", xnsm)

            If Not xn Is Nothing Then

                xn.ParentNode.RemoveChild(xn)

            End If

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Net.Framework.3.5.SP1""]", xnsm)

            If Not xn Is Nothing Then

                xn.ParentNode.RemoveChild(xn)

            End If

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Windows.Installer.3.1""]", xnsm)

            If Not xn Is Nothing Then

                xn.ParentNode.RemoveChild(xn)

            End If

        Case Versions.Version10

            ' alter the value for the ProjectName using the newer framework tag

            xn = xd.SelectSingleNode("/prj:Project/prj:ItemGroup/prj:BootstrapperPackage" & _

             "[@Include=""Microsoft.Net.Framework.2.0""]", xnsm)

            If xn IsNot Nothing Then

                xtemp = xn.SelectSingleNode("prj:ProductName", xnsm)

                xtemp.FirstChild.Value = ".NET Framework 2.0 %28x86%29"

            End If

    End Select



    ' The MSBuildToolsPath vs MSBuildBinPath environmental variable.  Oddly enough 

    ' a fully patched VS2005 uses the newer MSBuildToolsPath.  So, this should only

    ' be required if you don't have VS2005 SP1 installed. However, I can't detect 

    ' that, so we take the worst case scenario, and use the older version

    For Each xn In xd.SelectNodes("/prj:Project/prj:Import", xnsm)

        xtemp = xn.Attributes("Project")

        If xtemp IsNot Nothing Then

            temp = xn.Attributes("Project").Value

            If ConvertTo >= Versions.Version9 Then

                ' convert it to the newer MSBuildToolsPath

                If temp.Contains("MSBuildBinPath") Then

                    temp = temp.Replace("MSBuildBinPath", "MSBuildToolsPath")

                    xtemp.Value = temp

                End If

            Else

                ' convert it to the older MSBuildBinPath

                If temp.Contains("MSBuildToolsPath") Then

                    temp = temp.Replace("MSBuildToolsPath", "MSBuildBinPath")

                    xtemp.Value = temp

                End If

            End If

        End If

    Next



    xd.Save(ProjFile)

    Return True

End Function

Downloads/Links

Download the MSI Installer file: ProjectConverterSetup.msi
Download the VB.Net Source code for this program: ProjectConverter2.zip
The C# source code version will be available shortly

Last Modified: 5 May 10

你可能感兴趣的:(version)