C# 解析 Visual Studio 解决方案文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/707107/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Parsing Visual Studio Solution files
提问by Filip Fr?cz
How can I parse Visual Studio solution (SLN) files in .NET? I would like to write an app that merges multiple solutions into one while saving the relative build order.
如何解析 .NET 中的 Visual Studio 解决方案 (SLN) 文件?我想编写一个应用程序,将多个解决方案合并为一个,同时保存相关的构建顺序。
回答by JaredPar
I can't really offer you a library and my guess is there isn't one that exists out there. But I've spent a deal of time messing around with .sln files in batch editting scenarios and I've found Powershell to be a very useful tool for this task. The .SLN format is pretty simple and can be almost completely parsed with a few quick and dirty expressions. For Example
我真的不能为你提供一个图书馆,我猜那里没有一个。但是我花了很多时间在批量编辑场景中处理 .sln 文件,我发现 Powershell 是完成这项任务的非常有用的工具。.SLN 格式非常简单,几乎可以通过一些快速而肮脏的表达式完全解析。例如
Included Project files.
包括项目文件。
gc ConsoleApplication30.sln |
? { $_ -match "^Project" } |
%{ $_ -match ".*=(.*)$" | out-null ; $matches[1] } |
%{ $_.Split(",")[1].Trim().Trim('"') }
It's not always pretty, but it is an effective way to do batch processing.
它并不总是很漂亮,但它是一种进行批处理的有效方法。
回答by John Leidegren
The .NET 4.0 version of the Microsoft.Build assembly contains a SolutionParser class in the Microsoft.Build.Construction namespace that parses Visual Studio solution files.
.NET 4.0 版本的 Microsoft.Build 程序集在 Microsoft.Build.Construction 命名空间中包含一个 SolutionParser 类,用于解析 Visual Studio 解决方案文件。
Unfortunately this class is internal, but I've wrapped some of that functionality in a class that uses reflection to get at some common properties you might find helpful.
不幸的是,这个类是内部的,但我已经将其中的一些功能封装在一个类中,该类使用反射来获取一些您可能会觉得有用的常见属性。
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();
}
}
Note that you have to change your target framework to ".NET Framework 4" (not client profile) to be able to add the Microsoft.Build reference to your project.
请注意,您必须将目标框架更改为“.NET Framework 4”(不是客户端配置文件)才能将 Microsoft.Build 引用添加到您的项目中。
回答by Andy Lowry
we solved a similar problem of merging solutions automatically by writing a Visual Studio plugin which created a new solution then searched for *.sln file and imported them into the new one using:
我们通过编写一个 Visual Studio 插件来解决自动合并解决方案的类似问题,该插件创建了一个新的解决方案,然后搜索 *.sln 文件并使用以下命令将它们导入到新的文件中:
dte2.Solution.AddFromFile(solutionPath, false);
Our problem was slightly different in that we wanted VS to sort out the build order for us, so we then converted any dll references to project references where possible.
我们的问题略有不同,因为我们希望 VS 为我们整理构建顺序,因此我们然后在可能的情况下将任何 dll 引用转换为项目引用。
We then automated this into a build process by running VS via COM automation.
然后,我们通过 COM 自动化运行 VS,将其自动化为构建过程。
This solution was a little Heath Robinson, but had the advantage that VS was doing the editing so our code was not dependant on format of the sln file. Which was helpful when we moved from VS 2005 to 2008 and again to 2010.
这个解决方案有点像 Heath Robinson,但优点是 VS 正在编辑,所以我们的代码不依赖于 sln 文件的格式。当我们从 VS 2005 迁移到 2008 并再次迁移到 2010 时,这很有帮助。
回答by Ken
I expounded, determined that the MSBuild classes can be used to manipulate the underlying structures. I will have further code on my web site later.
我阐述了,确定 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;
}
}
}
}
}
回答by WiredWiz
I don't know if anyone is still looking for solutions to this problem, but I ran across a project that seems to do just what is needed. https://slntools.codeplex.com/One of the functions of this tool is to merge multiple solutions together.
我不知道是否有人还在寻找这个问题的解决方案,但我遇到了一个似乎只做需要的项目。 https://slntools.codeplex.com/该工具的功能之一是将多个解决方案合并在一起。
回答by Ohad Schneider
JetBrains(the creators of Resharper) have publicsln parsing abilities in their assemblies (no reflection needed). It's probably more robust than the existing open source solutions suggested here (let alone the ReGex hacks). All you need to do is:
JetBrains(Resharper 的创建者)在其程序集中具有公共sln 解析能力(无需反射)。它可能比这里建议的现有开源解决方案更强大(更不用说 ReGex 黑客了)。您需要做的就是:
- Download the ReSharper Command Line Tools(free).
- Add the following as references to your project
JetBrains.Platform.ProjectModel
JetBrains.Platform.Util
JetBrains.Platform.Interop.WinApi
- 下载ReSharper 命令行工具(免费)。
- 添加以下内容作为对您项目的引用
JetBrains.Platform.ProjectModel
JetBrains.Platform.Util
JetBrains.Platform.Interop.WinApi
The library is not documented, but Reflector (or indeed, dotPeek) is your friend. For example:
该库没有记录,但 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));
}
}
}
}
}
回答by TarmoPikaro
Everything is great, but I wanted also to get sln generation capability - in code snapshot above you're only parsing .sln files - I wanted to make similar thing except to be able to re-generate sln with slight modifications back to .sln file. Such cases could be for example porting same project for different .NET platform. For now it's only sln re-generation, but later on I will expand it to projects as well.
一切都很好,但我还想获得 sln 生成功能 - 在上面的代码快照中,您只解析 .sln 文件 - 我想做类似的事情,除了能够通过稍加修改重新生成 sln 回 .sln 文件. 例如,这种情况可能是为不同的 .NET 平台移植相同的项目。现在只是 sln 重新生成,但稍后我也会将其扩展到项目。
I guess that I wanted also to demonstrate the power of regular expressions and native interfaces. (Smaller amount of code with more functionality)
我想我还想展示正则表达式和本机接口的强大功能。(更少的代码,更多的功能)
Update 4.1.2017I've created separate svn repository for parsing .sln solution: https://sourceforge.net/p/syncproj/code/HEAD/tree/
2017 年1 月 4 日更新我创建了单独的 svn 存储库来解析 .sln 解决方案:https: //sourceforge.net/p/syncproj/code/HEAD/tree/
Below is my own code sample snippet (predecessor). You're free to use any of them.
下面是我自己的代码示例片段(前身)。您可以自由使用其中任何一个。
It's possible that in future svn based solution parsing code will be update with generation capabilities as well.
未来基于 svn 的解决方案解析代码也可能会更新生成功能。
Update 4.2.2017Source code in SVN is supporting .sln generation as well.
2017 年 2 月 4 日更新 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);
}
}
}
回答by Maciej Kucia
With Visual Studio 2015 there is now a publicly accessible SolutionFile
class which can be used to parse solution files:
Visual Studio 2015 现在有一个可公开访问的SolutionFile
类,可用于解析解决方案文件:
using Microsoft.Build.Construction;
var _solutionFile = SolutionFile.Parse(path);
This class is found in the Microsoft.Build.dll 14.0.0.0assembly. In my case it was located at:
此类位于Microsoft.Build.dll 14.0.0.0程序集中。就我而言,它位于:
C:\Program Files (x86)\Reference Assemblies\Microsoft\MSBuild\v14.0\Microsoft.Build.dll
Thanks to Philfor pointing this out!
回答by elimad
Answerby @john-leidegren is great. For pre-VS2015, this is of great use. But there was a minor mistake, as the code to retrieve configurations was missing. So wanted to add it, in case someone is struggling to use this code.
The enhancement is very simple:
@john-leidegren 的回答很棒。对于 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
}
As additional help, providing simple code to browse through the properties of a System.Type
as suggested by @oasten.
作为额外的帮助,提供简单的代码来浏览System.Type
@oasten 建议的 a 的属性。
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;
}
}
回答by lindexi
Thank @John Leidegren he offers an effective way.
I write a hlper class for I cant use his code that cant find the s_SolutionParser_configurations
and the projects without FullName.
感谢@John Leidegren,他提供了一种有效的方法。我写了一个 hlper 类,因为我不能使用他的代码,因为s_SolutionParser_configurations
没有全名就找不到项目和项目。
The code is in githubthat can get the projects with the FullName.
代码在github中,可以获取FullName的项目。
And the code cant get SolutionConfiguration.
并且代码无法获得解决方案配置。
But when you dev a vsx the vs will say cant find Microsoft.Build.dll
,so you may try use dte to get all the projects.
但是当你开发一个 vsx 时,vs 会说找不到Microsoft.Build.dll
,所以你可以尝试使用 dte 来获取所有项目。
The code that use dte to get all the projects is in github
使用dte获取所有项目的代码在github