Làm cách nào để phân tích cú pháp tệp giải pháp Visual Studio (SLN) trong .NET? Tôi muốn viết một ứng dụng kết hợp nhiều giải pháp thành một trong khi lưu thứ tự xây dựng tương đối.
Làm cách nào để phân tích cú pháp tệp giải pháp Visual Studio (SLN) trong .NET? Tôi muốn viết một ứng dụng kết hợp nhiều giải pháp thành một trong khi lưu thứ tự xây dựng tương đối.
Câu trả lời:
Phiên bản .NET 4.0 của hợp ngữ Microsoft.Build chứa lớp SolutionParser trong không gian tên Microsoft.Build.Construction phân tích cú pháp tệp giải pháp Visual Studio.
Thật không may, lớp này là nội bộ, nhưng tôi đã gói một số chức năng đó trong một lớp sử dụng phản xạ để có được một số thuộc tính chung mà bạn có thể thấy hữu ích.
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();
}
}
Lưu ý rằng bạn phải thay đổi khung mục tiêu của mình thành ".NET Framework 4" (không phải hồ sơ máy khách) để có thể thêm tham chiếu Microsoft.Build vào dự án của bạn.
SolutionFile
giới thiệu trong Microsoft.Build.dll được cài đặt với Visual Studio 2015 (xem msdn.microsoft.com/en-us/library/… )
Với Visual Studio 2015 hiện có một SolutionFile
lớp có thể truy cập công khai có thể được sử dụng để phân tích cú pháp các tệp giải pháp:
using Microsoft.Build.Construction;
var _solutionFile = SolutionFile.Parse(path);
Lớp này được tìm thấy trong hội đồng Microsoft.Build.dll 14.0.0.0 . Trong trường hợp của tôi, nó được đặt tại:
C:\Program Files (x86)\Reference Assemblies\Microsoft\MSBuild\v14.0\Microsoft.Build.dll
Nhờ Phil cho chỉ ra này !
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
Tôi không biết liệu có ai vẫn đang tìm kiếm giải pháp cho vấn đề này không, nhưng tôi đã xem qua một dự án dường như chỉ làm những gì cần thiết. https://slntools.codeplex.com/ Một trong những chức năng của công cụ này là hợp nhất nhiều giải pháp với nhau.
JetBrains (những người tạo ra Resharper) có khả năng phân tích cú pháp sln công khai trong các tập hợp của họ (không cần phản ánh). Nó có lẽ mạnh mẽ hơn các giải pháp mã nguồn mở hiện tại được đề xuất ở đây (hãy để một mình các bản hack ReGex). Tất cả những gì bạn cần làm là:
JetBrains.Platform.ProjectModel
JetBrains.Platform.Util
JetBrains.Platform.Interop.WinApi
Thư viện không có tài liệu, nhưng Reflector (hoặc thực sự, dotPeek) là bạn của bạn. Ví dụ:
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));
}
}
}
}
}
Tôi thực sự không thể cung cấp cho bạn một thư viện và tôi đoán là không có cái nào tồn tại ở đó. Nhưng tôi đã dành rất nhiều thời gian với các tệp .sln trong các kịch bản chỉnh sửa hàng loạt và tôi thấy Powershell là một công cụ rất hữu ích cho tác vụ này. Định dạng .SLN khá đơn giản và có thể được phân tích cú pháp gần như hoàn toàn với một vài biểu thức nhanh và khó hiểu. Ví dụ
Các tệp Dự án bao gồm.
gc ConsoleApplication30.sln |
? { $_ -match "^Project" } |
%{ $_ -match ".*=(.*)$" | out-null ; $matches[1] } |
%{ $_.Split(",")[1].Trim().Trim('"') }
Nó không phải lúc nào cũng đẹp, nhưng nó là một cách hiệu quả để xử lý hàng loạt.
chúng tôi đã giải quyết một vấn đề tương tự về việc hợp nhất các giải pháp tự động bằng cách viết một plugin Visual Studio tạo giải pháp mới, sau đó tìm kiếm tệp * .sln và nhập chúng vào tệp mới bằng cách sử dụng:
dte2.Solution.AddFromFile(solutionPath, false);
Vấn đề của chúng tôi hơi khác ở chỗ chúng tôi muốn VS sắp xếp thứ tự xây dựng cho chúng tôi, vì vậy chúng tôi sau đó chuyển đổi bất kỳ tham chiếu dll nào thành tham chiếu dự án nếu có thể.
Sau đó, chúng tôi tự động hóa điều này thành một quy trình xây dựng bằng cách chạy VS thông qua tự động hóa COM.
Giải pháp này hơi giống Heath Robinson, nhưng có lợi thế là VS đang thực hiện việc chỉnh sửa nên mã của chúng tôi không phụ thuộc vào định dạng của tệp sln. Điều này rất hữu ích khi chúng tôi chuyển từ VS 2005 sang 2008 và một lần nữa sang 2010.
Mọi thứ đều tuyệt vời, nhưng tôi cũng muốn có được khả năng tạo sln - trong ảnh chụp nhanh mã ở trên, bạn chỉ phân tích cú pháp các tệp .sln - tôi muốn làm điều tương tự ngoại trừ việc có thể tạo lại sln với các sửa đổi nhỏ trở lại tệp .sln . Những trường hợp như vậy có thể là ví dụ chuyển cùng một dự án cho nền tảng .NET khác nhau. Hiện tại, nó chỉ là sự tái tạo lại sln, nhưng sau này tôi sẽ mở rộng nó sang các dự án.
Tôi đoán rằng tôi cũng muốn chứng minh sức mạnh của biểu thức chính quy và giao diện gốc. (Số lượng mã nhỏ hơn với nhiều chức năng hơn)
Cập nhật 4.1.2017 Tôi đã tạo kho lưu trữ svn riêng biệt để phân tích cú pháp .sln: https://sourceforge.net/p/syncproj/code/HEAD/tree/
Dưới đây là đoạn mã mẫu của riêng tôi (tiền thân). Bạn có thể tự do sử dụng bất kỳ ứng dụng nào trong số chúng.
Có thể trong tương lai mã phân tích cú pháp dựa trên giải pháp svn cũng sẽ được cập nhật với các khả năng tạo.
Cập nhật 4.2.2017 Mã nguồn trong SVN cũng đang hỗ trợ tạo .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);
}
}
}
Tôi giải thích, xác định rằng các lớp MSBuild có thể được sử dụng để thao tác các cấu trúc bên dưới. Tôi sẽ có thêm mã trên trang web của tôi sau.
// 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
trở thành URL mà trang web sẽ chạy trong IISExpress, v.v.
Câu trả lời của @ john-leidegren thật tuyệt. Đối với trước VS2015, điều này rất hữu ích. Nhưng có một lỗi nhỏ, vì mã để truy xuất cấu hình bị thiếu. Vì vậy, muốn thêm nó, trong trường hợp ai đó đang gặp khó khăn để sử dụng mã này.
Cải tiến rất đơn giản:
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
}
Như trợ giúp bổ sung, cung cấp mã đơn giản để duyệt qua các thuộc tính của a System.Type
như được đề xuất bởi @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;
}
}
Vì những gì xứng đáng của nó, bây giờ tôi đã tạo một dự án nhỏ để đọc các tệp sln và proj có sẵn trên nuget:
Cảm ơn @John Leidegren, anh ấy đã đưa ra một cách hiệu quả. Tôi viết một lớp hlper vì tôi không thể sử dụng mã của anh ấy mà không thể tìm thấy s_SolutionParser_configurations
và các dự án không có FullName.
Mã nằm trong github có thể tải các dự án với FullName.
Và mã không thể nhận được SolutionConfiguration.
Nhưng khi bạn phát triển một vsx, vs sẽ thông báo không thể tìm thấy Microsoft.Build.dll
, vì vậy bạn có thể thử sử dụng dte để nhận tất cả các dự án.
Mã sử dụng dte để nhận tất cả các dự án có trong github