การแยกวิเคราะห์ไฟล์ Visual Studio Solution


109

ฉันจะแยกวิเคราะห์ไฟล์ Visual Studio solution (SLN) ใน. NET ได้อย่างไร ฉันต้องการเขียนแอปที่รวมโซลูชันหลายอย่างเข้าด้วยกันในขณะที่บันทึกลำดับการสร้างสัมพัทธ์

คำตอบ:


113

แอสเซมบลี 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 ไปยังโครงการของคุณได้


3
ไม่เป็นไร แต่จะแสดงกลุ่ม "รายการโซลูชัน" เป็น "โครงการ" ซึ่งไม่ถูกต้อง
Doug

3
นี่คือคำสั่ง "ใช้" เพื่อเพิ่ม: using System; ใช้ System.Reflection; ใช้ System.Collections.Generic; ใช้ System.Diagnostics; ใช้ System.IO; ใช้ System.Linq;
NealWalters

1
@Kiquenet - คุณสามารถตรวจสอบคุณสมบัติ "ProjectType" ของอ็อบเจ็กต์ s_ProjectInSolution ในลักษณะเดียวกับคุณสมบัติอื่น ๆ ที่เปิดเผย สิ่งนี้ส่งคืน enum แต่เป็นเพียง ToString () เท่านั้น ฉันพยายามแก้ไขโพสต์เพื่อรวมโพสต์นี้สองครั้ง แต่ก็ถูกไฟไหม้ทุกครั้ง
oasten

3
@oasten ในขณะที่ความตั้งใจดีชุมชน SO ขมวดคิ้วในการแก้ไขดังกล่าวคุณควรมีส่วนร่วมในการอภิปรายเมตาหากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับเรื่องนี้ ฉันเองพบว่ามันค่อนข้างบ้าในบางครั้ง โปรดทราบว่าฉันไม่มีส่วนเกี่ยวข้องกับการแก้ไขของคุณที่ถูกปิดลง วิธีที่เหมาะสมแม้ว่าฉันคิดว่าจะเพิ่มการแก้ไขให้คุณก็คือการโพสต์ทุกอย่างใหม่เป็นคำตอบอื่น ด้วยวิธีนี้จะเห็นได้ชัดว่าคุณเป็นผู้ให้ข้อมูลนอกเหนือจากที่ฉันให้ไว้ในตอนแรก มันสมเหตุสมผล แต่เครื่องมือการกลั่นกรองไม่ได้ให้กลไกการตอบรับที่ดี
John Leidegren

18
มีคลาสสาธารณะใหม่ที่SolutionFileเปิดตัวใน Microsoft.Build.dll ที่ติดตั้งด้วย Visual Studio 2015 (ดูmsdn.microsoft.com/en-us/library/… )
Phil

70

ด้วย 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

ขอบคุณฟิลที่ชี้ให้เห็น !


1
มีประโยชน์มาก ... นี่คือสิ่งที่ฉันใช้สำหรับการบริโภค powershell ...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
SliverNinja - MSFT

2
ฉันไม่พบคลาสดังกล่าวใน Microsoft.Build.dll v4.0.0.0 ที่มาพร้อมกับ Visual Studio 2017
Jeff G

@JeffG ลองติดตั้ง msbuild เพียงอย่างเดียว
Matt Kucia

1
@JeffG ฉันใช้ VS 2017 ด้วยถ้าฉันเพิ่ม Mircosoft สร้างจากแท็บ Assemblies ใน Add Reference แสดงว่าฉันไม่สามารถเข้าถึง SolutionFile ได้ อย่างไรก็ตามหากฉันเรียกดูและอ้างอิง dll ที่อยู่ในโฟลเดอร์ด้านบนดูเหมือนว่าจะใช้งานได้
Inrego

3
@JeffG แพ็คเกจนี้พร้อมใช้งานแล้วบน NuGet nuget.org/packages/Microsoft.Build
Robert Hardy

16

ฉันไม่รู้ว่ายังมีใครกำลังมองหาวิธีแก้ปัญหานี้อยู่หรือเปล่า แต่ฉันเจอโครงการที่ดูเหมือนจะทำในสิ่งที่จำเป็น https://slntools.codeplex.com/ หนึ่งในฟังก์ชันของเครื่องมือนี้คือการรวมโซลูชันหลายอย่างเข้าด้วยกัน


ในไฟล์โซลูชันที่ฉันทดสอบ slntools นี้ให้รายละเอียดมากกว่า ReSharper libs
Răzvan Flavius ​​Panda

14

JetBrains (ผู้สร้าง Resharper) มีความสามารถในการแยกวิเคราะห์ sln สาธารณะในชุดประกอบ (ไม่จำเป็นต้องมีการสะท้อนกลับ) อาจมีประสิทธิภาพมากกว่าโซลูชันโอเพนซอร์สที่มีอยู่ที่แนะนำที่นี่ (นับประสาการแฮ็ก ReGex) สิ่งที่คุณต้องทำคือ:

  • ดาวน์โหลดReSharper Command Line Tools (ฟรี)
  • เพิ่มสิ่งต่อไปนี้เป็นข้อมูลอ้างอิงในโครงการของคุณ
    • 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));
                }
            }
        }
    }
}

4
หมายเหตุ: ในโพสต์นี้เนมสเปซจะแตกต่างกันเล็กน้อย [บางทีเจบีอาจปรับโครงสร้างเนื้อหาของพวกเขา :)]: ~ JetBrains.Platform.ProjectModel ~ JetBrains.Platform.Util ~ JetBrains.Platform.Interop.WinApi
lewiSnort

9

ฉันเสนอห้องสมุดให้คุณไม่ได้จริงๆและฉันเดาว่าไม่มีห้องสมุดอยู่ที่นั่น แต่ฉันใช้เวลาไปกับการยุ่งกับไฟล์. sln ในสถานการณ์การแก้ไขแบทช์และฉันพบว่า Powershell เป็นเครื่องมือที่มีประโยชน์มากสำหรับงานนี้ รูปแบบ. SLN ค่อนข้างเรียบง่ายและสามารถแยกวิเคราะห์ได้เกือบทั้งหมดด้วยนิพจน์ที่รวดเร็วและสกปรก ตัวอย่างเช่น

รวมไฟล์โครงการ

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

มันไม่ได้สวยเสมอไป แต่มันเป็นวิธีที่มีประสิทธิภาพในการประมวลผลแบทช์


ใช่นั่นคือสิ่งที่ฉันทำจนถึงตอนนี้
Filip Frącz

หากต้องการยกเว้นโฟลเดอร์โซลูชันสิ่งนี้ใช้ได้กับฉัน: (Get-Content MySolution.sln) | ที่ไหน - วัตถุ {$ _ -match '(? = ^ โครงการ (?! \ ("\ {2150E333-8FDC-42A3-9474-1A3956D46DE8 \}" \))) ^ (\ w +)'} | ForEach-Object {$ _ -match ". * = (. *) $" | นอกศูนย์; $ ตรงกับ [1]} | ForEach-Object {$ _. Split (",") [1] .Trim (). trim ('"')}
David Gardiner

6

เราแก้ไขปัญหาที่คล้ายกันในการรวมโซลูชันโดยอัตโนมัติโดยการเขียนปลั๊กอิน Visual Studio ซึ่งสร้างโซลูชันใหม่จากนั้นค้นหาไฟล์ * .sln และนำเข้าสู่ไฟล์ใหม่โดยใช้:

dte2.Solution.AddFromFile(solutionPath, false);

ปัญหาของเราแตกต่างกันเล็กน้อยตรงที่เราต้องการให้ VS จัดเรียงลำดับการสร้างให้เราดังนั้นเราจึงแปลงการอ้างอิง dll ใด ๆ เป็นการอ้างอิงโครงการหากเป็นไปได้

จากนั้นเราทำให้สิ่งนี้เป็นอัตโนมัติในกระบวนการสร้างโดยเรียกใช้ VS ผ่าน COM อัตโนมัติ

โซลูชันนี้เป็น Heath Robinson เล็กน้อย แต่มีข้อได้เปรียบที่ VS กำลังทำการแก้ไขดังนั้นรหัสของเราจึงไม่ขึ้นอยู่กับรูปแบบของไฟล์ sln ซึ่งเป็นประโยชน์เมื่อเราย้ายจาก VS 2005 เป็น 2008 และอีกครั้งเป็น 2010


คุณมีปัญหาด้านประสิทธิภาพการใช้งาน DTE หรือไม่และหากเป็นเช่นนั้นคุณจะแก้ปัญหาได้อย่างไร stackoverflow.com/questions/1620199/…
Chris Moutray

@mouters เราพบว่าการใช้ DTE และการใช้ gui นั้นมีประสิทธิภาพเหมือนกัน การติดตั้งตัวเลือก VS เพียงเล็กน้อยช่วยได้เล็กน้อย แต่ไม่มากนัก เนื่องจากเป็นระบบอัตโนมัติและทำงานข้ามคืนประสิทธิภาพจึงไม่ใช่สิ่งที่เรากังวล
Andy Lowry

หาก sln มีโฟลเดอร์ที่มีโปรเจ็กต์คุณจะไม่สามารถรับโปรเจ็กต์ที่รวมอยู่ในโฟลเดอร์ได้
lindexi

5

ทุกอย่างดีมาก แต่ฉันก็อยากได้ความสามารถในการสร้าง 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);
        }
    }
}

3

ฉันอธิบายแล้วพิจารณาว่าคลาส 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;
                }
            }
        }
    }
}

ความคิดใดที่ AbstractX มาจาก?
gunr2171

ดูเหมือนว่าจะตีความเว็บไซต์ ASP.Net ผิดซึ่งrelativepathจะกลายเป็น URL ที่ไซต์ควรทำงานใน IISExpress และอื่น ๆ ภายใต้
ดั๊ก

1

ตอบโดย @ 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;
    }
}


0

ขอบคุณ @John Leidegren เขานำเสนอวิธีที่มีประสิทธิภาพ ฉันเขียนคลาส hlper เพราะฉันไม่สามารถใช้รหัสของเขาที่หาไฟล์s_SolutionParser_configurationsและโครงการที่ไม่มี FullName

รหัสอยู่ในgithubที่สามารถรับโปรเจ็กต์ด้วย FullName

และรหัสไม่สามารถรับ SolutionConfiguration

แต่เมื่อคุณ dev a vsx vs จะบอกว่าไม่พบ Microsoft.Build.dllดังนั้นคุณอาจลองใช้ dte เพื่อรับโปรเจ็กต์ทั้งหมด

รหัสที่ใช้ dte เพื่อรับโปรเจ็กต์ทั้งหมดอยู่ในgithub


@ NP83 ขอบคุณและฉันลบลิงค์
lindexi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.