ฉันจะแยกวิเคราะห์ไฟล์ Visual Studio solution (SLN) ใน. NET ได้อย่างไร ฉันต้องการเขียนแอปที่รวมโซลูชันหลายอย่างเข้าด้วยกันในขณะที่บันทึกลำดับการสร้างสัมพัทธ์
ฉันจะแยกวิเคราะห์ไฟล์ Visual Studio solution (SLN) ใน. NET ได้อย่างไร ฉันต้องการเขียนแอปที่รวมโซลูชันหลายอย่างเข้าด้วยกันในขณะที่บันทึกลำดับการสร้างสัมพัทธ์
คำตอบ:
แอสเซมบลี Microsoft.Build เวอร์ชัน. NET 4.0 มีคลาส SolutionParser ในเนมสเปซ Microsoft.Build.Construction ที่แยกวิเคราะห์ไฟล์โซลูชัน Visual Studio
น่าเสียดายที่คลาสนี้เป็นคลาสภายใน แต่ฉันได้รวมฟังก์ชันบางอย่างไว้ในคลาสที่ใช้การสะท้อนกลับเพื่อรับคุณสมบัติทั่วไปบางอย่างที่คุณอาจพบว่ามีประโยชน์
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 ไปยังโครงการของคุณได้
SolutionFile
เปิดตัวใน Microsoft.Build.dll ที่ติดตั้งด้วย Visual Studio 2015 (ดูmsdn.microsoft.com/en-us/library/… )
ด้วย 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
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
ฉันไม่รู้ว่ายังมีใครกำลังมองหาวิธีแก้ปัญหานี้อยู่หรือเปล่า แต่ฉันเจอโครงการที่ดูเหมือนจะทำในสิ่งที่จำเป็น https://slntools.codeplex.com/ หนึ่งในฟังก์ชันของเครื่องมือนี้คือการรวมโซลูชันหลายอย่างเข้าด้วยกัน
JetBrains (ผู้สร้าง Resharper) มีความสามารถในการแยกวิเคราะห์ sln สาธารณะในชุดประกอบ (ไม่จำเป็นต้องมีการสะท้อนกลับ) อาจมีประสิทธิภาพมากกว่าโซลูชันโอเพนซอร์สที่มีอยู่ที่แนะนำที่นี่ (นับประสาการแฮ็ก ReGex) สิ่งที่คุณต้องทำคือ:
JetBrains.Platform.ProjectModel
JetBrains.Platform.Util
JetBrains.Platform.Interop.WinApi
ไลบรารีไม่ได้รับการบันทึกไว้ แต่ตัวสะท้อนแสง (หรือ 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 ในสถานการณ์การแก้ไขแบทช์และฉันพบว่า Powershell เป็นเครื่องมือที่มีประโยชน์มากสำหรับงานนี้ รูปแบบ. SLN ค่อนข้างเรียบง่ายและสามารถแยกวิเคราะห์ได้เกือบทั้งหมดด้วยนิพจน์ที่รวดเร็วและสกปรก ตัวอย่างเช่น
รวมไฟล์โครงการ
gc ConsoleApplication30.sln |
? { $_ -match "^Project" } |
%{ $_ -match ".*=(.*)$" | out-null ; $matches[1] } |
%{ $_.Split(",")[1].Trim().Trim('"') }
มันไม่ได้สวยเสมอไป แต่มันเป็นวิธีที่มีประสิทธิภาพในการประมวลผลแบทช์
เราแก้ไขปัญหาที่คล้ายกันในการรวมโซลูชันโดยอัตโนมัติโดยการเขียนปลั๊กอิน Visual Studio ซึ่งสร้างโซลูชันใหม่จากนั้นค้นหาไฟล์ * .sln และนำเข้าสู่ไฟล์ใหม่โดยใช้:
dte2.Solution.AddFromFile(solutionPath, false);
ปัญหาของเราแตกต่างกันเล็กน้อยตรงที่เราต้องการให้ VS จัดเรียงลำดับการสร้างให้เราดังนั้นเราจึงแปลงการอ้างอิง dll ใด ๆ เป็นการอ้างอิงโครงการหากเป็นไปได้
จากนั้นเราทำให้สิ่งนี้เป็นอัตโนมัติในกระบวนการสร้างโดยเรียกใช้ VS ผ่าน COM อัตโนมัติ
โซลูชันนี้เป็น Heath Robinson เล็กน้อย แต่มีข้อได้เปรียบที่ VS กำลังทำการแก้ไขดังนั้นรหัสของเราจึงไม่ขึ้นอยู่กับรูปแบบของไฟล์ sln ซึ่งเป็นประโยชน์เมื่อเราย้ายจาก VS 2005 เป็น 2008 และอีกครั้งเป็น 2010
ทุกอย่างดีมาก แต่ฉันก็อยากได้ความสามารถในการสร้าง sln ด้วยเช่นกันในโค้ดสแนปชอตด้านบนคุณจะแยกวิเคราะห์ไฟล์. sln เท่านั้น - ฉันต้องการสร้างสิ่งที่คล้ายกันยกเว้นจะสามารถสร้าง sln ใหม่ได้โดยมีการปรับเปลี่ยนเล็กน้อยกลับไปที่ไฟล์. sln . กรณีดังกล่าวอาจเป็นตัวอย่างเช่นการย้ายโครงการเดียวกันสำหรับแพลตฟอร์ม. NET ที่แตกต่างกัน ตอนนี้เป็นเพียงการสร้างใหม่ sln แต่ในภายหลังฉันจะขยายไปยังโครงการด้วย
ฉันเดาว่าฉันต้องการแสดงให้เห็นถึงพลังของนิพจน์ทั่วไปและอินเทอร์เฟซดั้งเดิม (โค้ดจำนวนน้อยพร้อมฟังก์ชันการทำงานที่มากขึ้น)
อัปเดต 4.1.2017 ฉันได้สร้างที่เก็บ svn แยกต่างหากสำหรับการแยกวิเคราะห์โซลูชัน. sln: https://sourceforge.net/p/syncproj/code/HEAD/tree/
ด้านล่างนี้คือตัวอย่างโค้ดของฉันเอง (รุ่นก่อน) คุณสามารถใช้สิ่งเหล่านี้ได้ฟรี
เป็นไปได้ว่าในอนาคตรหัสการแยกวิเคราะห์โซลูชันที่ใช้ 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);
}
}
}
ฉันอธิบายแล้วพิจารณาว่าคลาส 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;
}
}
}
}
}
relativepath
จะกลายเป็น URL ที่ไซต์ควรทำงานใน IISExpress และอื่น ๆ ภายใต้
ตอบโดย @ 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
}
เพื่อเป็นความช่วยเหลือเพิ่มเติมโดยให้รหัสง่ายๆเพื่อเรียกดูคุณสมบัติของ a System.Type
ตามที่แนะนำโดย @oasten
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;
}
}
สำหรับสิ่งที่คุ้มค่าตอนนี้ฉันได้สร้างโครงการเล็ก ๆ เพื่ออ่านไฟล์ sln และ proj ที่มีอยู่ใน nuget:
ขอบคุณ @John Leidegren เขานำเสนอวิธีที่มีประสิทธิภาพ ฉันเขียนคลาส hlper เพราะฉันไม่สามารถใช้รหัสของเขาที่หาไฟล์s_SolutionParser_configurations
และโครงการที่ไม่มี FullName
รหัสอยู่ในgithubที่สามารถรับโปรเจ็กต์ด้วย FullName
และรหัสไม่สามารถรับ SolutionConfiguration
แต่เมื่อคุณ dev a vsx vs จะบอกว่าไม่พบ Microsoft.Build.dll
ดังนั้นคุณอาจลองใช้ dte เพื่อรับโปรเจ็กต์ทั้งหมด
รหัสที่ใช้ dte เพื่อรับโปรเจ็กต์ทั้งหมดอยู่ในgithub