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.
quelle
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.
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:Diese Klasse befindet sich in der Assembly Microsoft.Build.dll 14.0.0.0 . In meinem Fall befand es sich bei:
Vielen Dank an Phil für diesen Hinweis !
quelle
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.
quelle
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:
quelle
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.
Es ist nicht immer schön, aber es ist eine effektive Möglichkeit, Stapelverarbeitung durchzuführen.
quelle
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:
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.
quelle
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.
quelle
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.
quelle
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:
Als zusätzliche Hilfe bieten Sie einfachen Code zum Durchsuchen der Eigenschaften von a,
System.Type
wie von @oasten vorgeschlagen.quelle
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:
https://www.nuget.org/packages/ByteDev.DotNet/
quelle
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
quelle