在Visual Studio解决方案中更改所有项目的目标框架

110

我需要更改所有项目的目标框架。我有许多解决方案,包含数百个项目。

这里是否有任何新的方法,或者我必须更改每个单独的项目?

10个回答

81

刚刚发布了目标框架迁移器,这是一个Visual Studio扩展,可以同时更改多个.NET项目的目标框架。


这在2013年还不能正常运行。 - Jon Egerton
1
只需要在 vsixmanifest 中更改单个版本号即可使其在 VS 2013 中运行。 - Panagiotis Kanavos
3
针对VS2015版本:下载、解压缩,在vsixmanifest文件中修改InstallationTarget的版本为14.0,依赖项修改为4.6。 - Jeroen K
5
已上传支持VS2015和4.6版本的新版到库。 - Pavel Samokha
@CuongLe TFM 不会改变除框架以外的任何东西 - 因此,如果有什么变化,那是 VS 做的,而不是 TFM。如果项目运行时间过长 - 这通常是由于与其他 VS 插件冲突造成的,请尝试暂时禁用它们。 - Pavel Samokha
显示剩余6条评论

39

您可以使用Scott Dorman在CodeProject上提供的Visual Studio宏来完成此操作:

Visual Studio 2010和目标框架版本

以下是代码,将其下载到您的<UserProfile>\Documents\Visual Studio 2010\Projects\VSMacros80\MyMacros文件夹中,打开Visual Studio宏IDE(Alt-F11),将其作为现有项添加到“MyMacros”项目中:

'------------------------------------------------------------------------------
' Visual Studio 2008 Macros
'
' ProjectUtilities.vb
'
'------------------------------------------------------------------------------
' Copyright (C) 2007-2008 Scott Dorman (sj_dorman@hotmail.com)
'
' This library is free software; you can redistribute it and/or
' modify it under the terms of the Microsoft Public License (Ms-PL).
'
' This library is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
' Microsoft Public License (Ms-PL) for more details.
'------------------------------------------------------------------------------
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics

Public Module ProjectUtilities

    Private Class ProjectGuids
        Public Const vsWindowsCSharp As String = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"
        Public Const vsWindowsVBNET As String = "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}"
        Public Const vsWindowsVisualCPP As String = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"
        Public Const vsWebApplication As String = "{349C5851-65DF-11DA-9384-00065B846F21}"
        Public Const vsWebSite As String = "{E24C65DC-7377-472B-9ABA-BC803B73C61A}"
        Public Const vsDistributedSystem As String = "{F135691A-BF7E-435D-8960-F99683D2D49C}"
        Public Const vsWCF As String = "{3D9AD99F-2412-4246-B90B-4EAA41C64699}"
        Public Const vsWPF As String = "{60DC8134-EBA5-43B8-BCC9-BB4BC16C2548}"
        Public Const vsVisualDatabaseTools As String = "{C252FEB5-A946-4202-B1D4-9916A0590387}"
        Public Const vsDatabase As String = "{A9ACE9BB-CECE-4E62-9AA4-C7E7C5BD2124}"
        Public Const vsDatabaseOther As String = "{4F174C21-8C12-11D0-8340-0000F80270F8}"
        Public Const vsTest As String = "{3AC096D0-A1C2-E12C-1390-A8335801FDAB}"
        Public Const vsLegacy2003SmartDeviceCSharp As String = "{20D4826A-C6FA-45DB-90F4-C717570B9F32}"
        Public Const vsLegacy2003SmartDeviceVBNET As String = "{CB4CE8C6-1BDB-4DC7-A4D3-65A1999772F8}"
        Public Const vsSmartDeviceCSharp As String = "{4D628B5B-2FBC-4AA6-8C16-197242AEB884}"
        Public Const vsSmartDeviceVBNET As String = "{68B1623D-7FB9-47D8-8664-7ECEA3297D4F}"
        Public Const vsWorkflowCSharp As String = "{14822709-B5A1-4724-98CA-57A101D1B079}"
        Public Const vsWorkflowVBNET As String = "{D59BE175-2ED0-4C54-BE3D-CDAA9F3214C8}"
        Public Const vsDeploymentMergeModule As String = "{06A35CCD-C46D-44D5-987B-CF40FF872267}"
        Public Const vsDeploymentCab As String = "{3EA9E505-35AC-4774-B492-AD1749C4943A}"
        Public Const vsDeploymentSetup As String = "{978C614F-708E-4E1A-B201-565925725DBA}"
        Public Const vsDeploymentSmartDeviceCab As String = "{AB322303-2255-48EF-A496-5904EB18DA55}"
        Public Const vsVSTA As String = "{A860303F-1F3F-4691-B57E-529FC101A107}"
        Public Const vsVSTO As String = "{BAA0C2D2-18E2-41B9-852F-F413020CAA33}"
        Public Const vsSharePointWorkflow As String = "{F8810EC1-6754-47FC-A15F-DFABD2E3FA90}"
    End Class

    '' Defines the valid target framework values.
    Enum TargetFramework
        Fx40 = 262144
        Fx35 = 196613
        Fx30 = 196608
        Fx20 = 131072
    End Enum

    '' Change the target framework for all projects in the current solution.
    Sub ChangeTargetFrameworkForAllProjects()
        Dim project As EnvDTE.Project
        Dim clientProfile As Boolean = False

        Write("--------- CHANGING TARGET .NET FRAMEWORK VERSION -------------")
        Try
            If Not DTE.Solution.IsOpen Then
                Write("There is no solution open.")
            Else              
                Dim targetFrameworkInput As String = InputBox("Enter the target framework version (Fx40, Fx35, Fx30, Fx20):", "Target Framework", "Fx40")
                Dim targetFramework As TargetFramework = [Enum].Parse(GetType(TargetFramework), targetFrameworkInput)

                If targetFramework = ProjectUtilities.TargetFramework.Fx35 Or targetFramework = ProjectUtilities.TargetFramework.Fx40 Then
                    Dim result As MsgBoxResult = MsgBox("The .NET Framework version chosen supports a Client Profile. Would you like to use that profile?", MsgBoxStyle.Question Or MsgBoxStyle.YesNo, "Target Framework Profile")
                    If result = MsgBoxResult.Yes Then
                        clientProfile = True
                    End If
                End If

                For Each project In DTE.Solution.Projects
                    If project.Kind <> Constants.vsProjectKindSolutionItems And project.Kind <> Constants.vsProjectKindMisc Then
                        ChangeTargetFramework(project, targetFramework, clientProfile)
                    Else
                        For Each projectItem In project.ProjectItems
                            If Not (projectItem.SubProject Is Nothing) Then
                                ChangeTargetFramework(projectItem.SubProject, targetFramework, clientProfile)
                            End If
                        Next

                    End If
                Next
            End If
        Catch ex As System.Exception
            Write(ex.Message)
        End Try
    End Sub

    '' Change the target framework for a project.
    Function ChangeTargetFramework(ByVal project As EnvDTE.Project, ByVal targetFramework As TargetFramework, ByVal clientProfile As Boolean) As Boolean
        Dim changed As Boolean = True

        If project.Kind = Constants.vsProjectKindSolutionItems Or project.Kind = Constants.vsProjectKindMisc Then
            For Each projectItem In project.ProjectItems
                If Not (projectItem.SubProject Is Nothing) Then
                    ChangeTargetFramework(projectItem.SubProject, targetFramework, clientProfile)
                End If
            Next
        Else
            Try
                If IsLegalProjectType(project) Then
                    SetTargetFramework(project, targetFramework, clientProfile)
                Else
                    Write("Skipping project: " + project.Name + " (" + project.Kind + ")")
                End If
            Catch ex As Exception
                Write(ex.Message)
                changed = False
            End Try
        End If

        Return changed
    End Function

    '' Determines if the project is a project that actually supports changing the target framework.
    Function IsLegalProjectType(ByVal proejct As EnvDTE.Project) As Boolean
        Dim legalProjectType As Boolean = True

        Select Case proejct.Kind
            Case ProjectGuids.vsDatabase
                legalProjectType = False
            Case ProjectGuids.vsDatabaseOther
                legalProjectType = False
            Case ProjectGuids.vsDeploymentCab
                legalProjectType = False
            Case ProjectGuids.vsDeploymentMergeModule
                legalProjectType = False
            Case ProjectGuids.vsDeploymentSetup
                legalProjectType = False
            Case ProjectGuids.vsDeploymentSmartDeviceCab
                legalProjectType = False
            Case ProjectGuids.vsDistributedSystem
                legalProjectType = False
            Case ProjectGuids.vsLegacy2003SmartDeviceCSharp
                legalProjectType = False
            Case ProjectGuids.vsLegacy2003SmartDeviceVBNET
                legalProjectType = False
            Case ProjectGuids.vsSharePointWorkflow
                legalProjectType = False
            Case ProjectGuids.vsSmartDeviceCSharp
                legalProjectType = True
            Case ProjectGuids.vsSmartDeviceVBNET
                legalProjectType = True
            Case ProjectGuids.vsTest
                legalProjectType = False
            Case ProjectGuids.vsVisualDatabaseTools
                legalProjectType = False
            Case ProjectGuids.vsVSTA
                legalProjectType = True
            Case ProjectGuids.vsVSTO
                legalProjectType = True
            Case ProjectGuids.vsWCF
                legalProjectType = True
            Case ProjectGuids.vsWebApplication
                legalProjectType = True
            Case ProjectGuids.vsWebSite
                legalProjectType = True
            Case ProjectGuids.vsWindowsCSharp
                legalProjectType = True
            Case ProjectGuids.vsWindowsVBNET
                legalProjectType = True
            Case ProjectGuids.vsWindowsVisualCPP
                legalProjectType = True
            Case ProjectGuids.vsWorkflowCSharp
                legalProjectType = False
            Case ProjectGuids.vsWorkflowVBNET
                legalProjectType = False
            Case ProjectGuids.vsWPF
                legalProjectType = True
            Case Else
                legalProjectType = False
        End Select
        Return legalProjectType
    End Function

    '' Sets the target framework for the project to the specified framework.
    Sub SetTargetFramework(ByVal project As EnvDTE.Project, ByVal targetFramework As TargetFramework, ByVal clientProfile As Boolean)
        Dim currentTargetFramework As TargetFramework = CType(project.Properties.Item("TargetFramework").Value, TargetFramework)
        Dim targetMoniker As String = GetTargetFrameworkMoniker(targetFramework, clientProfile)
        Dim currentMoniker As String = project.Properties.Item("TargetFrameworkMoniker").Value

        If currentMoniker <> targetMoniker Then
            Write("Changing project: " + project.Name + " from " + currentMoniker + " to " + targetMoniker + ".")
            project.Properties.Item("TargetFrameworkMoniker").Value = targetMoniker
            project.Properties.Item("TargetFramework").Value = targetFramework
        Else
            Write("Skipping project: " + project.Name + ", already at the correct target framework.")
        End If
    End Sub

    Function GetTargetFrameworkMoniker(ByVal targetFramework As TargetFramework, ByVal clientProfile As Boolean) As String
        Dim moniker As String = ".NETFramework,Version=v"
        Select Case targetFramework
            Case ProjectUtilities.TargetFramework.Fx20
                moniker += "2.0"

            Case ProjectUtilities.TargetFramework.Fx30
                moniker += "3.0"

            Case ProjectUtilities.TargetFramework.Fx35
                moniker += "3.5"

            Case ProjectUtilities.TargetFramework.Fx40
                moniker += "4.0"

        End Select

        If clientProfile Then
            moniker += ",Profile=Client"
        End If

        Return moniker
    End Function

    '' Writes a message to the output window
    Sub Write(ByVal s As String)
        Dim out As OutputWindowPane = GetOutputWindowPane("Change Target Framework", True)
        out.OutputString(s)
        out.OutputString(vbCrLf)
    End Sub

    '' Gets an instance of the output window
    Function GetOutputWindowPane(ByVal Name As String, Optional ByVal show As Boolean = True) As OutputWindowPane
        Dim win As Window = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput)
        If show Then win.Visible = True
        Dim ow As OutputWindow = win.Object
        Dim owpane As OutputWindowPane
        Try
            owpane = ow.OutputWindowPanes.Item(Name)
        Catch e As System.Exception
            owpane = ow.OutputWindowPanes.Add(Name)
        End Try
        owpane.Activate()
        Return owpane
    End Function

End Module

3
因为CodeProject上的链接似乎已经失效,所以给代码点个赞(+1)。 - Hannele

38

我用过的PowerShell脚本。承认有点粗暴。

Get-ChildItem . -Recurse -Filter *.*proj |ForEach {
    $content = Get-Content $_.FullName
    $content |ForEach {
        $_.Replace("<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>", "<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>")
    } |Set-Content $_.FullName
}

1
很好,但在我的系统上出现了错误“无法加载script1.ps1,因为在此系统上禁用了运行脚本。有关更多信息,请参见about_Execution_Policies”,所以我运行命令set-executionpolicy remotesigned来解决这个问题。 - angularrocks.com
是的,bigb。不能默认在系统上运行脚本确实是一件我认为理所当然的麻烦事。感谢您指出这一点。对于所有人来说,“Set-ExecutionPolicy RemoteSigned”允许您在不签名证书的情况下运行本地PowerShell脚本。有关详细信息,请参见此处:http://technet.microsoft.com/en-us/library/ee176961.aspx - Russell B
3
这正是我一直在寻找的。最优雅和灵活的解决方案,可以更改所有那些.proj文件。 - ovm
1
对我来说,迁移会修改项目文件、app.configs和设计师。 - Tomas Kubes

11

总有一些简单的方法。像Notepad++这样的文本编辑器将包括一个在文件中查找和替换的功能。只需在您的csproj/vbproj文件中搜索当前版本字符串:

<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>

并将其替换为新版本

<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>

不过先检查一下是一个好主意...


1
这是我找到的最好的方法,我使用Sublime来更改所有内容。 - cuongle
不知道 NP++ 还能这样做。谢谢! - Matt M

7

来自bash:

$find . -name "*.csproj" -exec sed -b -i "s,<TargetFrameworkVersion>[^<]*</TargetFrameworkVersion>,<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>," {} \;

6

我认为迄今为止最简单的方法是使用搜索和替换工具。如果支持正则表达式,则具有优势。

可能有很多这样的工具 - 我测试过的第一个对我有用,链接在此:http://www.ecobyte.com/replacetext/

有一条注释说它在Win7上存在一些问题,但我没有遇到过。

该工具的逐步说明:

  1. 替换 | 添加组 | 命名 (例如 "MyGroup")
  2. 右键单击 MyGroup | 添加文件...
  3. 选择源 (例如使用文件夹,浏览到要更改的项目的根文件夹)
  4. 如有必要,设置包含文件过滤器 (例如 *.csproj)
  5. 右键单击 Original Text 下面的行 | 高级编辑...
  6. 在搜索文本框中输入您的正则表达式 (例如 <TargetFrameworkVersion>.*</TargetFrameworkVersion>)
  7. 在搜索文本下面的组合框中选择 "正则表达式搜索"
  8. 输入替换文本 (例如 <TargetFrameworkVersion>4.0</TargetFrameworkVersion>)
  9. 选择目标和备份设置 (默认情况下会创建备份)
  10. 开始替换 (Ctrl+R)

现在,如果出于某种原因您需要在代码中执行此操作,我可能也能做到 (这就是我找到这个问题的方式)。在这种情况下,请在评论中请求。


enter image description here


好的,我刚刚读了ShellShock回答下面0xA3的评论。我想那可能是个问题。在尝试编写代码解决方案之前,我会尝试想出一个正则表达式来解决它。 - Mike Fuchs

5

在VS 2015上运行得非常好! - Jose Parra

2

条件正则表达式让我很头疼,因此我提供了一个编码解决方案来进行搜索和替换(尽可能避免使用EnvDTE)。

项目文件条目的顺序似乎不重要

可以尝试类似以下的内容:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace TextReplaceDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ReplaceTargetFrameworkVersion("v4.0", "c:/projekt/2005", "*.csproj");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Inserts the denoted targetFramework into all occurrences of TargetFrameworkVersion.
        /// If the TargetFrameworkVersion is not present in the file, the method searches for the 
        /// OutputType tag, which should be present, and inserts the TargetFrameworkVersion before that.
        /// </summary>
        /// <param name="targetFramework">New target framework (e.g. "v4.0")</param>
        /// <param name="rootDirectory">Root directory for the file search (e.g. "c:\Projects\2005")</param>
        /// <param name="fileSearchPattern">Pattern to find the project files (e.g. "*.csproj). 
        /// Will get all files for empty parameter.</param>
        public static void ReplaceTargetFrameworkVersion(string targetFramework, string rootDirectory, string fileSearchPattern)
        {
            if (string.IsNullOrEmpty(targetFramework)) throw new ArgumentNullException("targetFramework");
            if (string.IsNullOrEmpty(rootDirectory)) throw new ArgumentNullException("rootDirectory");
            if (string.IsNullOrEmpty(fileSearchPattern)) fileSearchPattern = "*.*";

            string regexPattern = "<TargetFrameworkVersion>.*</TargetFrameworkVersion>";
            string insertText = string.Format("<TargetFrameworkVersion>{0}</TargetFrameworkVersion>", targetFramework);
            string alternativeMarker = "<OutputType>";

            // get all files
            List<FileInfo> files = GetAllFiles(rootDirectory, fileSearchPattern);

            // iterate over found files
            foreach (var file in files)
            {
                string fileContent = File.ReadAllText(file.FullName);
                Match match = Regex.Match(fileContent, regexPattern);
                string newfileContent = null;
                if (match.Success)
                {
                    // replace <TargetFrameworkVersion>
                    newfileContent = fileContent.Replace(match.Value, insertText);
                }
                else if (fileContent.Contains(alternativeMarker))
                {
                    // create <TargetFrameworkVersion>
                    newfileContent = fileContent.Replace(alternativeMarker,
                        insertText + Environment.NewLine + "    " + alternativeMarker);
                }

                // overwrite file
                if (newfileContent != null)
                    File.WriteAllText(file.FullName, newfileContent);
            }
        }


        /// <summary>
        /// Recursive function to find all files in a directory by a searchPattern
        /// </summary>
        /// <param name="path">Path to the root directory</param>
        /// <param name="searchPattern">Pattern for the file search, e.g. "*.txt"</param>
        public static List<FileInfo> GetAllFiles(string path, string searchPattern)
        {
            List<FileInfo> files = new List<FileInfo>();

            DirectoryInfo dir = new DirectoryInfo(path);

            if (dir.Exists)
            {
                // get all files in directory
                files.AddRange(dir.GetFiles(searchPattern));

                // get all files of subdirectories
                foreach (var subDir in dir.GetDirectories())
                {
                    files.AddRange(GetAllFiles(subDir.FullName, searchPattern));
                }
            }
            return files;
        }
    }
}

1

你可以使用宏来实现这个功能,或者记住VS项目文件是文本文件,这意味着一个简单的全局搜索和替换可以达到相同的效果,并且是一种更通用的技术,适用于许多项目文件进行相同的更改。

首先备份现有的项目文件,然后进行所需更改(例如,更改目标框架)。使用WinDiff或WinMerge比较新项目文件和备份。这将告诉您需要进行的更改。然后使用Visual Studio IDE的“在文件中查找和替换”功能对所有项目文件进行更改。


2
一个简单的搜索和替换对于没有指定目标框架的项目文件(例如在VS 2005中创建的文件)将会失败。对于这样的项目文件,您需要找到正确的位置来插入目标框架元素,使用更复杂的搜索和替换表达式。 - Dirk Vollmar

1

一个没有外部工具的替代方案(并且可以更改其他设置,例如ToolsVersion):

  1. 使用Visual Studio,在临时目录中创建一个新的c#控制台项目“ManageCSProjFiles”(或者您喜欢的任何名称)。确保选中“将解决方案和项目放在同一个目录中”
  2. 从项目中删除Program.cs和Properties/AssemblyInfo.cs。不需要编译该项目。
  3. 保存项目并关闭Visual Studio。
  4. 使用文本编辑器打开ManageCSProjFiles.csproj。在最后一行之前,在底部添加以下行:
  <ItemGroup>
    <None Include="**\*.csproj" />
  </ItemGroup>
  1. 将ManageCSProjFiles.sln和ManageCSProjFiles.csproj复制到您解决方案树的最顶层目录。
  2. 如果在Visual Studio中加载ManageCSProjFiles解决方案,现在会显示所有的.csproj文件,您可以使用Visual Studio中的搜索/替换工具对它们进行更改。

这个方法也可以轻松扩展到其他项目类型。


这是一个很酷的选项。当我执行这些步骤时,所有项目中的所有文件都会被加载,而不仅仅是 .csproj 文件。理想情况下,我可以使用任一行为,但我想知道是否有任何简单的步骤可以强制将 .csproj 文件作为纯文本加载,就像上面描述的那样?使用 VS 16.10.3 - John Lewin
我花了一些时间才意识到这只是因为我的项目是一个SDK风格的.csproj,它会加载项目下的所有内容。添加一个默认排除语句就可以创建我想要的体验: <DefaultItemExcludes>**/*;$(DefaultItemExcludes)</DefaultItemExcludes> - John Lewin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接