Wie kann ich SLN-Dateien (Visual Studio Solution) in .NET analysieren? Ich möchte eine App schreiben, die mehrere Lösungen zu einer zusammenführt und gleichzeitig die relative Erstellungsreihenfolge speichert.
Wie kann ich SLN-Dateien (Visual Studio Solution) in .NET analysieren? Ich möchte eine App schreiben, die mehrere Lösungen zu einer zusammenführt und gleichzeitig die relative Erstellungsreihenfolge speichert.
Antworten:
Die .NET 4.0-Version der Microsoft.Build-Assembly enthält eine SolutionParser-Klasse im Microsoft.Build.Construction-Namespace, die Visual Studio-Lösungsdateien analysiert.
Leider ist diese Klasse intern, aber ich habe einige dieser Funktionen in eine Klasse eingeschlossen, die Reflektion verwendet, um zu einigen allgemeinen Eigenschaften zu gelangen, die Sie möglicherweise hilfreich finden.
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();
}
}
Beachten Sie, dass Sie Ihr Zielframework in ".NET Framework 4" (kein Clientprofil) ändern müssen, um die Microsoft.Build-Referenz zu Ihrem Projekt hinzufügen zu können.
SolutionFile
In Microsoft.Build.dll wurde eine neue öffentliche Klasse eingeführt, die mit Visual Studio 2015 installiert wurde (siehe msdn.microsoft.com/en-us/library/… )
Mit Visual Studio 2015 gibt es jetzt eine öffentlich zugängliche SolutionFile
Klasse, mit der Lösungsdateien analysiert werden können:
using Microsoft.Build.Construction;
var _solutionFile = SolutionFile.Parse(path);
Diese Klasse befindet sich in der Assembly Microsoft.Build.dll 14.0.0.0 . In meinem Fall befand es sich bei:
C:\Program Files (x86)\Reference Assemblies\Microsoft\MSBuild\v14.0\Microsoft.Build.dll
Vielen Dank an Phil für diesen Hinweis !
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
Ich weiß nicht, ob noch jemand nach Lösungen für dieses Problem sucht, aber ich bin auf ein Projekt gestoßen, das genau das zu tun scheint, was benötigt wird. https://slntools.codeplex.com/ Eine der Funktionen dieses Tools besteht darin, mehrere Lösungen zusammenzuführen.
JetBrains (die Entwickler von Resharper) verfügen in ihren Assemblys über öffentliche SLN-Analysefunktionen (keine Reflexion erforderlich). Es ist wahrscheinlich robuster als die hier vorgeschlagenen Open Source-Lösungen (geschweige denn die ReGex-Hacks). Alles was Sie tun müssen ist:
JetBrains.Platform.ProjectModel
JetBrains.Platform.Util
JetBrains.Platform.Interop.WinApi
Die Bibliothek ist nicht dokumentiert, aber Reflector (oder dotPeek) ist Ihr Freund. Beispielsweise:
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));
}
}
}
}
}
Ich kann Ihnen keine Bibliothek anbieten und ich vermute, dass es da draußen keine gibt. Aber ich habe viel Zeit damit verbracht, mit .sln-Dateien in Batch-Bearbeitungsszenarien herumzuspielen, und ich habe festgestellt, dass Powershell ein sehr nützliches Werkzeug für diese Aufgabe ist. Das .SLN-Format ist ziemlich einfach und kann mit ein paar schnellen und schmutzigen Ausdrücken fast vollständig analysiert werden. Beispielsweise
Enthaltene Projektdateien.
gc ConsoleApplication30.sln |
? { $_ -match "^Project" } |
%{ $_ -match ".*=(.*)$" | out-null ; $matches[1] } |
%{ $_.Split(",")[1].Trim().Trim('"') }
Es ist nicht immer schön, aber es ist eine effektive Möglichkeit, Stapelverarbeitung durchzuführen.
Wir haben ein ähnliches Problem beim automatischen Zusammenführen von Lösungen gelöst, indem wir ein Visual Studio-Plugin geschrieben haben, das eine neue Lösung erstellt und dann nach der * .sln-Datei gesucht und diese in die neue importiert hat:
dte2.Solution.AddFromFile(solutionPath, false);
Unser Problem war insofern etwas anders, als wir wollten, dass VS die Erstellungsreihenfolge für uns sortiert. Deshalb haben wir alle DLL-Referenzen nach Möglichkeit in Projektreferenzen konvertiert.
Wir haben dies dann zu einem Build-Prozess automatisiert, indem wir VS über COM-Automatisierung ausgeführt haben.
Diese Lösung war ein wenig Heath Robinson, hatte aber den Vorteil, dass VS die Bearbeitung durchführte, sodass unser Code nicht vom Format der SLN-Datei abhängig war. Das war hilfreich, als wir von VS 2005 nach 2008 und wieder nach 2010 wechselten.
Alles ist großartig, aber ich wollte auch die Fähigkeit zur SLN-Generierung erhalten - im obigen Code-Snapshot analysieren Sie nur SLN-Dateien - ich wollte etwas Ähnliches machen, außer um SLN mit geringfügigen Änderungen wieder in SLN-Datei generieren zu können . In solchen Fällen kann beispielsweise dasselbe Projekt für eine andere .NET-Plattform portiert werden. Im Moment ist es nur eine Neugenerierung, aber später werde ich es auch auf Projekte ausweiten.
Ich denke, ich wollte auch die Leistungsfähigkeit regulärer Ausdrücke und nativer Schnittstellen demonstrieren. (Kleinere Codemenge mit mehr Funktionalität)
Update 4.1.2017 Ich habe ein separates SVN-Repository zum Parsen der SLN-Lösung erstellt: https://sourceforge.net/p/syncproj/code/HEAD/tree/
Unten ist mein eigenes Codebeispiel-Snippet (Vorgänger). Sie können jeden von ihnen verwenden.
Es ist möglich, dass der Parsing-Code für SVN-basierte Lösungen in Zukunft auch mit Generierungsfunktionen aktualisiert wird.
Update 4.2.2017 Der Quellcode in SVN unterstützt auch die SLN-Generierung.
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);
}
}
}
Ich erklärte, festgestellt, dass die MSBuild-Klassen verwendet werden können, um die zugrunde liegenden Strukturen zu manipulieren. Ich werde später weiteren Code auf meiner Website haben.
// 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;
}
}
}
}
}
relativepath
URL wird, unter der die Site in IISExpress usw. ausgeführt werden soll.
Antwort von @ John-Leidegren ist großartig. Für die Zeit vor VS2015 ist dies von großem Nutzen. Es gab jedoch einen kleinen Fehler, da der Code zum Abrufen von Konfigurationen fehlte. Ich wollte es also hinzufügen, falls jemand Schwierigkeiten hat, diesen Code zu verwenden.
Die Verbesserung ist sehr einfach:
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
}
Als zusätzliche Hilfe bieten Sie einfachen Code zum Durchsuchen der Eigenschaften von a, System.Type
wie von @oasten vorgeschlagen.
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;
}
}
Für das, was es wert ist, habe ich jetzt ein kleines Projekt erstellt, um auf nuget verfügbare sln- und proj-Dateien zu lesen:
Vielen Dank an John Leidegren, er bietet einen effektiven Weg. Ich schreibe eine hlper-Klasse, weil ich seinen Code nicht verwenden kann, der die s_SolutionParser_configurations
und die Projekte ohne FullName nicht finden kann.
Der Code befindet sich in Github , der die Projekte mit dem vollständigen Namen abrufen kann.
Und der Code kann SolutionConfiguration nicht bekommen.
Aber wenn Sie ein vsx entwickeln, sagt das vs nicht finden Microsoft.Build.dll
, also können Sie versuchen, dte zu verwenden, um alle Projekte zu erhalten.
Der Code, der dte verwendet, um alle Projekte abzurufen, befindet sich in github