【问题标题】:Parsing Visual Studio Solution files解析 Visual Studio 解决方案文件
【发布时间】:2010-10-16 23:10:54
【问题描述】:

如何在 .NET 中解析 Visual Studio 解决方案 (SLN) 文件? 我想编写一个应用程序,将多个解决方案合并为一个,同时保存相关的构建顺序。

【问题讨论】:

    标签: c# .net visual-studio parsing


    【解决方案1】:

    我不知道是否有人仍在寻找解决此问题的方法,但我遇到了一个似乎可以满足需要的项目。

    https://slntools.codeplex.com/ 被迁移到https://github.com/mtherien/slntools

    此工具的功能之一是将多个解决方案合并在一起。

    【讨论】:

    • 在我测试过的解决方案文件上,这个 slntools 实际上提供了比 ReSharper 库更多的细节。
    【解决方案2】:

    感谢@John Leidegren,他提供了一种有效的方法。 我写了一个 hlper 类,因为我无法使用他无法找到 s_SolutionParser_configurations 的代码和没有 FullName 的项目。

    代码在github,可以获取全名项目。

    并且代码无法获取SolutionConfiguration。

    但是当你开发一个 vsx 时,vs 会说找不到Microsoft.Build.dll,所以你可以尝试使用 dte 来获取所有项目。

    使用dte获取所有项目的代码在github

    【讨论】:

    • @NP83 谢谢你,我删除了链接
    【解决方案3】:

    为了它的价值,我现在创建了一个小项目来读取 nuget 上可用的 sln 和 proj 文件:

    https://www.nuget.org/packages/ByteDev.DotNet/

    【讨论】:

    • 干得好,但没有项目源文件?
    • 该项目主要是为非 .NET Framework(.NET Core/Standard/.NET 5 等)项目构建的,这些项目没有从项目文件中显式引用源文件。相反,他们假设所有文件都添加到项目中,而指定要忽略的文件。
    【解决方案4】:

    Microsoft.Build 程序集的 .NET 4.0 版本在 Microsoft.Build.Construction 命名空间中包含一个解析 Visual Studio 解决方案文件的 SolutionParser 类。

    不幸的是,这个类是内部的,但我已经将其中的一些功能封装在一个类中,该类使用反射来获取一些您可能会发现有用的常见属性。

    public class Solution
    {
        //internal class SolutionParser
        //Name: Microsoft.Build.Construction.SolutionParser
        //Assembly: Microsoft.Build, Version=4.0.0.0
    
        static readonly Type s_SolutionParser;
        static readonly PropertyInfo s_SolutionParser_solutionReader;
        static readonly MethodInfo s_SolutionParser_parseSolution;
        static readonly PropertyInfo s_SolutionParser_projects;
    
        static Solution()
        {
            s_SolutionParser = Type.GetType("Microsoft.Build.Construction.SolutionParser, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);
            if (s_SolutionParser != null)
            {
                s_SolutionParser_solutionReader = s_SolutionParser.GetProperty("SolutionReader", BindingFlags.NonPublic | BindingFlags.Instance);
                s_SolutionParser_projects = s_SolutionParser.GetProperty("Projects", BindingFlags.NonPublic | BindingFlags.Instance);
                s_SolutionParser_parseSolution = s_SolutionParser.GetMethod("ParseSolution", BindingFlags.NonPublic | BindingFlags.Instance);
            }
        }
    
        public List<SolutionProject> Projects { get; private set; }
    
        public Solution(string solutionFileName)
        {
            if (s_SolutionParser == null)
            {
                throw new InvalidOperationException("Can not find type 'Microsoft.Build.Construction.SolutionParser' are you missing a assembly reference to 'Microsoft.Build.dll'?");
            }
            var solutionParser = s_SolutionParser.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).First().Invoke(null);
            using (var streamReader = new StreamReader(solutionFileName))
            {
                s_SolutionParser_solutionReader.SetValue(solutionParser, streamReader, null);
                s_SolutionParser_parseSolution.Invoke(solutionParser, null);
            }
            var projects = new List<SolutionProject>();
            var array = (Array)s_SolutionParser_projects.GetValue(solutionParser, null);
            for (int i = 0; i < array.Length; i++)
            {
                projects.Add(new SolutionProject(array.GetValue(i)));
            }
            this.Projects = projects;
        }
    }
    
    [DebuggerDisplay("{ProjectName}, {RelativePath}, {ProjectGuid}")]
    public class SolutionProject
    {
        static readonly Type s_ProjectInSolution;
        static readonly PropertyInfo s_ProjectInSolution_ProjectName;
        static readonly PropertyInfo s_ProjectInSolution_RelativePath;
        static readonly PropertyInfo s_ProjectInSolution_ProjectGuid;
        static readonly PropertyInfo s_ProjectInSolution_ProjectType;
    
        static SolutionProject()
        {
            s_ProjectInSolution = Type.GetType("Microsoft.Build.Construction.ProjectInSolution, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);
            if (s_ProjectInSolution != null)
            {
                s_ProjectInSolution_ProjectName = s_ProjectInSolution.GetProperty("ProjectName", BindingFlags.NonPublic | BindingFlags.Instance);
                s_ProjectInSolution_RelativePath = s_ProjectInSolution.GetProperty("RelativePath", BindingFlags.NonPublic | BindingFlags.Instance);
                s_ProjectInSolution_ProjectGuid = s_ProjectInSolution.GetProperty("ProjectGuid", BindingFlags.NonPublic | BindingFlags.Instance);
                s_ProjectInSolution_ProjectType = s_ProjectInSolution.GetProperty("ProjectType", BindingFlags.NonPublic | BindingFlags.Instance);
            }
        }
    
        public string ProjectName { get; private set; }
        public string RelativePath { get; private set; }
        public string ProjectGuid { get; private set; }
        public string ProjectType { get; private set; }
    
        public SolutionProject(object solutionProject)
        {
            this.ProjectName = s_ProjectInSolution_ProjectName.GetValue(solutionProject, null) as string;
            this.RelativePath = s_ProjectInSolution_RelativePath.GetValue(solutionProject, null) as string;
            this.ProjectGuid = s_ProjectInSolution_ProjectGuid.GetValue(solutionProject, null) as string;
            this.ProjectType = s_ProjectInSolution_ProjectType.GetValue(solutionProject, null).ToString();
        }
    }
    

    请注意,您必须将目标框架更改为“.NET Framework 4”(不是客户端配置文件)才能将 Microsoft.Build 引用添加到您的项目。

    【讨论】:

    • 这很好,但是它将“解决方案项目”组显示为“项目”,这是不正确的。
    • 这里是要添加的“使用”语句: using System;使用 System.Reflection;使用 System.Collections.Generic;使用 System.Diagnostics;使用 System.IO;使用 System.Linq;
    • @Kiquenet - 您可以检查 s_ProjectInSolution 对象的“ProjectType”属性,方法与公开的其他属性相同。这将返回一个枚举,但只是 ToString() 它。我尝试编辑帖子两次以包含此内容,但每次都被击落。
    • @oasten 虽然出于善意的 SO 社区不赞成此类编辑,但如果您想了解更多信息,您应该参与元讨论。我个人觉得有时有点疯狂。请注意,我与您的编辑被关闭完全无关。尽管我认为添加编辑的正确方法实际上是将所有内容重新发布为另一个答案。这样一来,很明显您是我最初提供的贡献者。这是有道理的,但审核工具并没有真正提供良好的反馈机制。
    • Microsoft.Build.dll 中引入了一个新的公共类 SolutionFile,它随 Visual Studio 2015 一起安装(请参阅 msdn.microsoft.com/en-us/library/…
    【解决方案5】:

    一切都很好,但我也想获得 sln 生成功能 - 在上面的代码快照中,你只是解析 .sln 文件 - 我想做类似的事情,除了能够通过轻微修改重新生成 sln 回到.sln 文件。例如,这种情况可能是为不同的 .NET 平台移植相同的项目。目前只是重新生成 sln,但稍后我会将其扩展到项目。

    我想我还想展示正则表达式和本机接口的强大功能。 (代码量少,功能多)

    更新 4.1.2017 我为解析 .sln 解决方案创建了单独的 svn 存储库: https://sourceforge.net/p/syncproj/code/HEAD/tree/

    下面是我自己的代码示例 sn-p(前身)。您可以随意使用其中的任何一个。

    未来基于 svn 的解决方案解析代码可能也会更新为生成功能。

    更新 4.2.2017 SVN 中的源代码也支持 .sln 生成。

    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.IO;
    using System.Diagnostics;
    using System.Text.RegularExpressions;
    using System.Text;
    
    
    public class Program
    {
        [DebuggerDisplay("{ProjectName}, {RelativePath}, {ProjectGuid}")]
        public class SolutionProject
        {
            public string ParentProjectGuid;
            public string ProjectName;
            public string RelativePath;
            public string ProjectGuid;
    
            public string AsSlnString()
            { 
                return "Project(\"" + ParentProjectGuid + "\") = \"" + ProjectName + "\", \"" + RelativePath + "\", \"" + ProjectGuid + "\"";
            }
        }
    
    /// <summary>
    /// .sln loaded into class.
    /// </summary>
    public class Solution
    {
        public List<object> slnLines;       // List of either String (line format is not intresting to us), or SolutionProject.
    
        /// <summary>
        /// Loads visual studio .sln solution
        /// </summary>
        /// <param name="solutionFileName"></param>
        /// <exception cref="System.IO.FileNotFoundException">The file specified in path was not found.</exception>
        public Solution( string solutionFileName )
        {
            slnLines = new List<object>();
            String slnTxt = File.ReadAllText(solutionFileName);
            string[] lines = slnTxt.Split('\n');
            //Match string like: Project("{66666666-7777-8888-9999-AAAAAAAAAAAA}") = "ProjectName", "projectpath.csproj", "{11111111-2222-3333-4444-555555555555}"
            Regex projMatcher = new Regex("Project\\(\"(?<ParentProjectGuid>{[A-F0-9-]+})\"\\) = \"(?<ProjectName>.*?)\", \"(?<RelativePath>.*?)\", \"(?<ProjectGuid>{[A-F0-9-]+})");
    
            Regex.Replace(slnTxt, "^(.*?)[\n\r]*$", new MatchEvaluator(m =>
                {
                    String line = m.Groups[1].Value;
    
                    Match m2 = projMatcher.Match(line);
                    if (m2.Groups.Count < 2)
                    {
                        slnLines.Add(line);
                        return "";
                    }
    
                    SolutionProject s = new SolutionProject();
                    foreach (String g in projMatcher.GetGroupNames().Where(x => x != "0")) /* "0" - RegEx special kind of group */
                        s.GetType().GetField(g).SetValue(s, m2.Groups[g].ToString());
    
                    slnLines.Add(s);
                    return "";
                }), 
                RegexOptions.Multiline
            );
        }
    
        /// <summary>
        /// Gets list of sub-projects in solution.
        /// </summary>
        /// <param name="bGetAlsoFolders">true if get also sub-folders.</param>
        public List<SolutionProject> GetProjects( bool bGetAlsoFolders = false )
        {
            var q = slnLines.Where( x => x is SolutionProject ).Select( i => i as SolutionProject );
    
            if( !bGetAlsoFolders )  // Filter away folder names in solution.
                q = q.Where( x => x.RelativePath != x.ProjectName );
    
            return q.ToList();
        }
    
        /// <summary>
        /// Saves solution as file.
        /// </summary>
        public void SaveAs( String asFilename )
        {
            StringBuilder s = new StringBuilder();
    
            for( int i = 0; i < slnLines.Count; i++ )
            {
                if( slnLines[i] is String ) 
                    s.Append(slnLines[i]);
                else
                    s.Append((slnLines[i] as SolutionProject).AsSlnString() );
    
                if( i != slnLines.Count )
                    s.AppendLine();
            }
    
            File.WriteAllText(asFilename, s.ToString());
        }
    }
    
    
        static void Main()
        {
            String projectFile = @"yourown.sln";
    
            try
            {
                String outProjectFile = Path.Combine(Path.GetDirectoryName(projectFile), Path.GetFileNameWithoutExtension(projectFile) + "_2.sln");
                Solution s = new Solution(projectFile);
                foreach( var proj in s.GetProjects() )
                {
                    Console.WriteLine( proj.RelativePath );
                }
    
                SolutionProject p = s.GetProjects().Where( x => x.ProjectName.Contains("Plugin") ).First();
                p.RelativePath = Path.Combine( Path.GetDirectoryName(p.RelativePath) , Path.GetFileNameWithoutExtension(p.RelativePath) + "_Variation" + ".csproj");
    
                s.SaveAs(outProjectFile);
    
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message);
            }
        }
    }
    

    【讨论】:

      【解决方案6】:

      @john-leidegren 的Answer 很棒。对于 VS2015 之前的版本,这很有用。但是有一个小错误,因为缺少检索配置的代码。所以想添加它,以防有人正在努力使用此代码。
      增强很简单:

          public class Solution
      {
          //internal class SolutionParser
          //Name: Microsoft.Build.Construction.SolutionParser
          //Assembly: Microsoft.Build, Version=4.0.0.0
      
          static readonly Type s_SolutionParser;
          static readonly PropertyInfo s_SolutionParser_solutionReader;
          static readonly MethodInfo s_SolutionParser_parseSolution;
          static readonly PropertyInfo s_SolutionParser_projects;
          static readonly PropertyInfo s_SolutionParser_configurations;//this was missing in john's answer
      
      
          static Solution()
          {
              s_SolutionParser = Type.GetType("Microsoft.Build.Construction.SolutionParser, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);
              if ( s_SolutionParser != null )
              {
                  s_SolutionParser_solutionReader = s_SolutionParser.GetProperty("SolutionReader", BindingFlags.NonPublic | BindingFlags.Instance);
                  s_SolutionParser_projects = s_SolutionParser.GetProperty("Projects", BindingFlags.NonPublic | BindingFlags.Instance);
                  s_SolutionParser_parseSolution = s_SolutionParser.GetMethod("ParseSolution", BindingFlags.NonPublic | BindingFlags.Instance);
                  s_SolutionParser_configurations = s_SolutionParser.GetProperty("SolutionConfigurations", BindingFlags.NonPublic | BindingFlags.Instance); //this was missing in john's answer
      
                  // additional info:
                  var PropNameLst = GenHlp_PropBrowser.PropNamesOfType(s_SolutionParser);
                  // the above call would yield something like this:
                  // [ 0] "SolutionParserWarnings"        string
                  // [ 1] "SolutionParserComments"        string
                  // [ 2] "SolutionParserErrorCodes"      string
                  // [ 3] "Version"                       string
                  // [ 4] "ContainsWebProjects"           string
                  // [ 5] "ContainsWebDeploymentProjects" string
                  // [ 6] "ProjectsInOrder"               string
                  // [ 7] "ProjectsByGuid"                string
                  // [ 8] "SolutionFile"                  string
                  // [ 9] "SolutionFileDirectory"         string
                  // [10] "SolutionReader"                string
                  // [11] "Projects"                      string
                  // [12] "SolutionConfigurations"        string
              }
          }
      
          public List<SolutionProject> Projects { get; private set; }
          public List<SolutionConfiguration> Configurations { get; private set; }
      
         //...
         //...
         //... no change in the rest of the code
      }
      

      作为附加帮助,提供简单的代码来浏览 @oasten 建议的 System.Type 的属性。

      public class GenHlp_PropBrowser
      {
          public static List<string> PropNamesOfClass(object anObj)
          {
              return anObj == null ? null : PropNamesOfType(anObj.GetType());
          }
          public static List<String> PropNamesOfType(System.Type aTyp)
          {
              List<string> retLst = new List<string>();
              foreach ( var p in aTyp.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance) )
              {
                  retLst.Add(p.Name);
              }
              return retLst;
          }
      }
      

      【讨论】:

        【解决方案7】:

        在 Visual Studio 2015 中,现在有一个可公开访问的 SolutionFile 类,可用于解析解决方案文件:

        using Microsoft.Build.Construction;
        var _solutionFile = SolutionFile.Parse(path);
        

        此类位于 Microsoft.Build.dll 14.0.0.0 程序集中。就我而言,它位于:

        C:\Program Files (x86)\Reference Assemblies\Microsoft\MSBuild\v14.0\Microsoft.Build.dll
        

        感谢Philpointing this out

        【讨论】:

        • 非常有用...这是我用于powershell消耗的...Add-Type -Path "C:\Program Files (x86)\Reference Assemblies\Microsoft\MSBuild\v14.0\Microsoft.Build.dll"$slnFile = [Microsoft.Build.Construction.SolutionFile]::Parse($slnPath);$slnFile.ProjectsInOrder
        • 我在 Visual Studio 2017 附带的 Microsoft.Build.dll v4.0.0.0 中找不到这样的类。
        • @JeffG 尝试单独安装 msbuild。
        • @JeffG 我也在使用 VS 2017。如果我从 Add Reference 的 Assemblies 选项卡中添加 Mircosoft.Build,那么我将无权访问 SolutionFile。但是,如果我浏览并引用位于上述文件夹中的 dll,那么它似乎确实可以工作。
        • @JeffG 这个包现在可以在 NuGet 上使用了 nuget.org/packages/Microsoft.Build
        【解决方案8】:

        JetBrains(Resharper 的创建者)在其程序集中具有 public sln 解析能力(无需反射)。它可能比此处建议的现有开源解决方案更强大(更不用说 ReGex hacks)。您需要做的就是:

        • 下载ReSharper Command Line Tools(免费)。
        • 添加以下内容作为对您项目的引用
          • JetBrains.Platform.ProjectModel
          • JetBrains.Platform.Util
          • JetBrains.Platform.Interop.WinApi

        该库没有文档记录,但 Reflector(或者实际上是 dotPeek)是您的朋友。例如:

        public static void PrintProjects(string solutionPath)
        {
            var slnFile = SolutionFileParser.ParseFile(FileSystemPath.Parse(solutionPath));
            foreach (var project in slnFile.Projects)
            {
                Console.WriteLine(project.ProjectName);
                Console.WriteLine(project.ProjectGuid);
                Console.WriteLine(project.ProjectTypeGuid);
                foreach (var kvp in project.ProjectSections)
                {
                    Console.WriteLine(kvp.Key);
                    foreach (var projectSection in kvp.Value) 
                    {
                        Console.WriteLine(projectSection.SectionName);
                        Console.WriteLine(projectSection.SectionValue);
                        foreach (var kvpp in projectSection.Properties)
                        {
                            Console.WriteLine(kvpp.Key); 
                            Console.WriteLine(string.Join(",", kvpp.Value));
                        }
                    }
                }
            }
        }
        

        【讨论】:

        • 注意:在这篇文章中,命名空间有点不同[也许 JB 重构了他们的东西 :)]:~JetBrains.Platform.ProjectModel ~JetBrains.Platform.Util ~JetBrains.Platform.Interop.WinApi
        【解决方案9】:

        我阐述,确定MSBuild类可以用来操作底层结构。稍后我将在我的网站上提供更多代码。

        // VSSolution
        
        using System;
        using System.Reflection;
        using System.Collections.Generic;
        using System.Linq;
        using System.Diagnostics;
        using System.IO;
        using AbstractX.Contracts;
        
        namespace VSProvider
        {
            public class VSSolution : IVSSolution
            {
                //internal class SolutionParser 
                //Name: Microsoft.Build.Construction.SolutionParser 
                //Assembly: Microsoft.Build, Version=4.0.0.0 
        
                static readonly Type s_SolutionParser;
                static readonly PropertyInfo s_SolutionParser_solutionReader;
                static readonly MethodInfo s_SolutionParser_parseSolution;
                static readonly PropertyInfo s_SolutionParser_projects;
                private string solutionFileName;
                private List<VSProject> projects;
        
                public string Name
                {
                    get
                    {
                        return Path.GetFileNameWithoutExtension(solutionFileName);
                    }
                }
        
                public IEnumerable<IVSProject> Projects
                {
                    get
                    {
                        return projects;
                    }
                }
        
                static VSSolution()
                {
                    s_SolutionParser = Type.GetType("Microsoft.Build.Construction.SolutionParser, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);
                    s_SolutionParser_solutionReader = s_SolutionParser.GetProperty("SolutionReader", BindingFlags.NonPublic | BindingFlags.Instance);
                    s_SolutionParser_projects = s_SolutionParser.GetProperty("Projects", BindingFlags.NonPublic | BindingFlags.Instance);
                    s_SolutionParser_parseSolution = s_SolutionParser.GetMethod("ParseSolution", BindingFlags.NonPublic | BindingFlags.Instance);
                }
        
                public string SolutionPath
                {
                    get
                    {
                        var file = new FileInfo(solutionFileName);
        
                        return file.DirectoryName;
                    }
                }
        
                public VSSolution(string solutionFileName)
                {
                    if (s_SolutionParser == null)
                    {
                        throw new InvalidOperationException("Can not find type 'Microsoft.Build.Construction.SolutionParser' are you missing a assembly reference to 'Microsoft.Build.dll'?");
                    }
        
                    var solutionParser = s_SolutionParser.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).First().Invoke(null);
        
                    using (var streamReader = new StreamReader(solutionFileName))
                    {
                        s_SolutionParser_solutionReader.SetValue(solutionParser, streamReader, null);
                        s_SolutionParser_parseSolution.Invoke(solutionParser, null);
                    }
        
                    this.solutionFileName = solutionFileName;
        
                    projects = new List<VSProject>();
                    var array = (Array)s_SolutionParser_projects.GetValue(solutionParser, null);
        
                    for (int i = 0; i < array.Length; i++)
                    {
                        projects.Add(new VSProject(this, array.GetValue(i)));
                    }
                }
        
                public void Dispose()
                {
                }
            }
        }
        
        // VSProject
        
        using System;
        using System.Reflection;
        using System.Collections.Generic;
        using System.Linq;
        using System.Diagnostics;
        using System.IO;
        using System.Xml;
        using AbstractX.Contracts;
        using System.Collections;
        
        namespace VSProvider
        {
            [DebuggerDisplay("{ProjectName}, {RelativePath}, {ProjectGuid}")]
            public class VSProject : IVSProject
            {
                static readonly Type s_ProjectInSolution;
                static readonly Type s_RootElement;
                static readonly Type s_ProjectRootElement;
                static readonly Type s_ProjectRootElementCache;
                static readonly PropertyInfo s_ProjectInSolution_ProjectName;
                static readonly PropertyInfo s_ProjectInSolution_ProjectType;
                static readonly PropertyInfo s_ProjectInSolution_RelativePath;
                static readonly PropertyInfo s_ProjectInSolution_ProjectGuid;
                static readonly PropertyInfo s_ProjectRootElement_Items;
        
                private VSSolution solution;
                private string projectFileName;
                private object internalSolutionProject;
                private List<VSProjectItem> items;
                public string Name { get; private set; }
                public string ProjectType { get; private set; }
                public string RelativePath { get; private set; }
                public string ProjectGuid { get; private set; }
        
                public string FileName
                {
                    get
                    {
                        return projectFileName;
                    }
                }
        
                static VSProject()
                {
                    s_ProjectInSolution = Type.GetType("Microsoft.Build.Construction.ProjectInSolution, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);
        
                    s_ProjectInSolution_ProjectName = s_ProjectInSolution.GetProperty("ProjectName", BindingFlags.NonPublic | BindingFlags.Instance);
                    s_ProjectInSolution_ProjectType = s_ProjectInSolution.GetProperty("ProjectType", BindingFlags.NonPublic | BindingFlags.Instance);
                    s_ProjectInSolution_RelativePath = s_ProjectInSolution.GetProperty("RelativePath", BindingFlags.NonPublic | BindingFlags.Instance);
                    s_ProjectInSolution_ProjectGuid = s_ProjectInSolution.GetProperty("ProjectGuid", BindingFlags.NonPublic | BindingFlags.Instance);
        
                    s_ProjectRootElement = Type.GetType("Microsoft.Build.Construction.ProjectRootElement, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);
                    s_ProjectRootElementCache = Type.GetType("Microsoft.Build.Evaluation.ProjectRootElementCache, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);
        
                    s_ProjectRootElement_Items = s_ProjectRootElement.GetProperty("Items", BindingFlags.Public | BindingFlags.Instance);
                }
        
                public IEnumerable<IVSProjectItem> Items
                {
                    get
                    {
                        return items;
                    }
                }
        
                public VSProject(VSSolution solution, object internalSolutionProject)
                {
                    this.Name = s_ProjectInSolution_ProjectName.GetValue(internalSolutionProject, null) as string;
                    this.ProjectType = s_ProjectInSolution_ProjectType.GetValue(internalSolutionProject, null).ToString();
                    this.RelativePath = s_ProjectInSolution_RelativePath.GetValue(internalSolutionProject, null) as string;
                    this.ProjectGuid = s_ProjectInSolution_ProjectGuid.GetValue(internalSolutionProject, null) as string;
        
                    this.solution = solution;
                    this.internalSolutionProject = internalSolutionProject;
        
                    this.projectFileName = Path.Combine(solution.SolutionPath, this.RelativePath);
        
                    items = new List<VSProjectItem>();
        
                    if (this.ProjectType == "KnownToBeMSBuildFormat")
                    {
                        this.Parse();
                    }
                }
        
                private void Parse()
                {
                    var stream = File.OpenRead(projectFileName);
                    var reader = XmlReader.Create(stream);
                    var cache = s_ProjectRootElementCache.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).First().Invoke(new object[] { true });
                    var rootElement = s_ProjectRootElement.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).First().Invoke(new object[] { reader, cache });
        
                    stream.Close();
        
                    var collection = (ICollection)s_ProjectRootElement_Items.GetValue(rootElement, null);
        
                    foreach (var item in collection)
                    {
                        items.Add(new VSProjectItem(this, item));
                    }
        
                }
        
                public IEnumerable<IVSProjectItem> EDMXModels
                {
                    get 
                    {
                        return this.items.Where(i => i.ItemType == "EntityDeploy");
                    }
                }
        
                public void Dispose()
                {
                }
            }
        }
        
        // VSProjectItem
        
        using System;
        using System.Reflection;
        using System.Collections.Generic;
        using System.Linq;
        using System.Diagnostics;
        using System.IO;
        using System.Xml;
        using AbstractX.Contracts;
        
        namespace VSProvider
        {
            [DebuggerDisplay("{ProjectName}, {RelativePath}, {ProjectGuid}")]
            public class VSProjectItem : IVSProjectItem
            {
                static readonly Type s_ProjectItemElement;
                static readonly PropertyInfo s_ProjectItemElement_ItemType;
                static readonly PropertyInfo s_ProjectItemElement_Include;
        
                private VSProject project;
                private object internalProjectItem;
                private string fileName;
        
                static VSProjectItem()
                {
                    s_ProjectItemElement = Type.GetType("Microsoft.Build.Construction.ProjectItemElement, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);
        
                    s_ProjectItemElement_ItemType = s_ProjectItemElement.GetProperty("ItemType", BindingFlags.Public | BindingFlags.Instance);
                    s_ProjectItemElement_Include = s_ProjectItemElement.GetProperty("Include", BindingFlags.Public | BindingFlags.Instance);
                }
        
                public string ItemType { get; private set; }
                public string Include { get; private set; }
        
                public VSProjectItem(VSProject project, object internalProjectItem)
                {
                    this.ItemType = s_ProjectItemElement_ItemType.GetValue(internalProjectItem, null) as string;
                    this.Include = s_ProjectItemElement_Include.GetValue(internalProjectItem, null) as string;
                    this.project = project;
                    this.internalProjectItem = internalProjectItem;
        
                    // todo - expand this
        
                    if (this.ItemType == "Compile" || this.ItemType == "EntityDeploy")
                    {
                        var file = new FileInfo(project.FileName);
        
                        fileName = Path.Combine(file.DirectoryName, this.Include);
                    }
                }
        
                public byte[] FileContents
                {
                    get 
                    {
                        return File.ReadAllBytes(fileName);
                    }
                }
        
                public string Name
                {
                    get 
                    {
                        if (fileName != null)
                        {
                            var file = new FileInfo(fileName);
        
                            return file.Name;
                        }
                        else
                        {
                            return this.Include;
                        }
                    }
                }
            }
        }
        

        【讨论】:

        • 知道 AbstractX 是从哪里来的吗?
        • 这似乎误解了 ASP.Net websites,其中 relativepath 成为该站点应在 IISExpress 等下运行的 URL。
        【解决方案10】:

        我们通过编写一个 Visual Studio 插件解决了自动合并解决方案的类似问题,该插件创建了一个新解决方案,然后搜索 *.sln 文件并将它们导入到新解决方案中:

        dte2.Solution.AddFromFile(solutionPath, false);
        

        我们的问题略有不同,因为我们希望 VS 为我们整理构建顺序,因此我们随后尽可能将任何 dll 引用转换为项目引用。

        然后,我们通过 COM 自动化运行 VS,将其自动化到构建过程中。

        这个解决方案有点像 Heath Robinson,但优点是 VS 正在编辑,所以我们的代码不依赖于 sln 文件的格式。当我们从 VS 2005 迁移到 2008 年并再次迁移到 2010 年时,这很有帮助。

        【讨论】:

        • 您在使用 DTE 时是否遇到过性能问题,如果有,您是如何解决的? stackoverflow.com/questions/1620199/…
        • @mouters 我们发现使用 DTE 和使用 gui 在性能方面大致相同。只安装最少的 VS 选项有一点帮助,但没有多大帮助。由于这是自动化的并在一夜之间运行,性能并不是我们一直关心的问题。
        • 如果sln有包含项目的文件夹,则无法获取文件夹中包含的项目。
        【解决方案11】:

        我不能真正为您提供图书馆,我猜那里根本不存在。但是我花了很多时间在批量编辑场景中处理 .sln 文件,我发现 Powershell 是完成这项任务的一个非常有用的工具。 .SLN 格式非常简单,几乎可以用一些快速而肮脏的表达式来完全解析。例如

        包含的项目文件。

        gc ConsoleApplication30.sln | 
          ? { $_ -match "^Project" } | 
          %{ $_ -match ".*=(.*)$" | out-null ; $matches[1] } | 
          %{ $_.Split(",")[1].Trim().Trim('"') }
        

        它并不总是很漂亮,但它是一种有效的批处理方式。

        【讨论】:

        • 是的,这就是我迄今为止一直在做的事情
        • 排除解决方案文件夹这对我有用:(Get-Content MySolution.sln) | Where-Object { $_ -match '(?=^Project(?!\("\{2150E333-8FDC-42A3-9474-1A3956D46DE8\}"\)))^(\w+)' } | ForEach-Object { $_ -match ".*=(.*)$" |出空; $matches[1] } | ForEach-Object { $_.Split(",")[1].Trim().Trim('"') }
        猜你喜欢
        • 2015-11-28
        • 1970-01-01
        • 1970-01-01
        • 2016-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-07
        • 1970-01-01
        相关资源
        最近更新 更多