解析 VisualStudio 解决方案文件

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

51350 次浏览

我不能提供给你一个图书馆,而且我猜那里根本就没有图书馆。但我已经花了很多时间。批处理编辑场景中的 sln 文件,我发现 Powershell 对于这个任务是非常有用的工具。那个。SLN 格式非常简单,几乎可以通过一些快速且粗糙的表达式完成解析。例如

包括项目文件。

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

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

那个。NET 4.0版本的 Microsoft。生成程序集包含 Microsoft 中的 SolutionParser 类。建造。构造用于分析 VisualStudio 解决方案文件的命名空间。

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

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。生成对项目的引用。

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

dte2.Solution.AddFromFile(solutionPath, false);

我们的问题稍有不同,因为我们希望 VS 为我们排序构建顺序,所以我们随后在可能的情况下将任何 dll 引用转换为项目引用。

然后,我们通过 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.

我阐述并确定 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;
}
}
}
}
}

我不知道是否还有人在寻找这个问题的解决方案,但是我偶然发现了一个项目,它似乎正是我们所需要的。

https://slntools.codeplex.com/ 被迁移到了 < a href = “ https://github.com/mtherien/slntools”rel = “ nofollow norefrer”> https://github.com/mtherien/slntools

One of the functions of this tool is to merge multiple solutions together.

JetBrains (Resharper 的创建者)在他们的程序集中具有 公众人士 sln 解析能力(不需要反射)。它可能比这里建议的现有开源解决方案更加健壮(更不用说 ReGex 了)。你要做的就是:

  • 下载 ReSharper 命令行工具(免费)。
  • 添加以下内容作为对项目的引用
    • JetBrains.Platform.ProjectModel
    • JetBrains.Platform.Util
    • JetBrains.Platform.Interop.WinApi

这个库没有文档说明,但是 Refector (或者说 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));
}
}
}
}
}

一切都很好,但我还想获得 sln 生成能力-在代码快照上面你只是解析。Sln 文件-我想做类似的事情,除了能够重新生成 sln 并稍作修改返回。档案。这种情况可能是例如为不同的。NET 平台。现在它只是 sln 的重新生成,但是稍后我将把它扩展到项目中。

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

2017年4月1日更新 我已经为解析.sln 解决方案创建了单独的 svn 存储库: Https://sourceforge.net/p/syncproj/code/head/tree/

下面是我自己的代码示例片段(前身)。

It's possible that in future svn based solution parsing code will be update with generation capabilities as well.

更新日期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);
}
}
}

在 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

感谢 菲尔指出这一点

@ 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
}

作为额外的帮助,提供简单的代码来浏览@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;
}
}

谢谢@John Leidegren 他提供了一个有效的方法。 I write a hlper class for I cant use his code that cant find the s_SolutionParser_configurations and the projects without FullName.

代码位于 Github中,可以使用 FullName 获取项目。

代码不能得到解决方案配置。

但是,当你开发一个 vsx 的 vs 将说不能找到 Microsoft.Build.dll,所以你可以尝试使用 dte 得到所有的项目。

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

For what its worth I have now created a little project to read sln and proj files available on nuget:

Https://www.nuget.org/packages/bytedev /