Visual Studio 솔루션 파일 구문 분석


109

.NET에서 SLN (Visual Studio 솔루션) 파일을 어떻게 구문 분석 할 수 있습니까? 상대적 빌드 순서를 저장하면서 여러 솔루션을 하나로 병합하는 앱을 작성하고 싶습니다.

답변:


113

Microsoft.Build 어셈블리의 .NET 4.0 버전에는 Visual Studio 솔루션 파일을 구문 분석하는 Microsoft.Build.Construction 네임 스페이스에 SolutionParser 클래스가 포함되어 있습니다.

안타깝게도이 클래스는 내부 클래스이지만 유용 할 수있는 몇 가지 공통 속성을 얻기 위해 리플렉션을 사용하는 클래스에 해당 기능 중 일부를 래핑했습니다.

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();
    }
}

Microsoft.Build 참조를 프로젝트에 추가하려면 대상 프레임 워크를 ".NET Framework 4"(클라이언트 프로필 아님)로 변경해야합니다.


3
괜찮지 만 "솔루션 항목"그룹이 "프로젝트"로 표시되며 이는 올바르지 않습니다.
Doug

3
다음은 추가 할 "using"문입니다. using System; System.Reflection 사용; System.Collections.Generic 사용; System.Diagnostics 사용; System.IO 사용; System.Linq 사용;
NealWalters

1
@Kiquenet-노출 된 다른 속성과 동일한 방식으로 s_ProjectInSolution 개체의 "ProjectType"속성을 검사 할 수 있습니다. 이것은 열거 형을 반환하지만 ToString () 만 반환합니다. 나는 이것을 두 번 포함하도록 게시물을 편집하려고 시도했지만 매번 화염에 맞았습니다.
oasten

3
@oasten 좋은 의도를 가진 SO 커뮤니티는 이러한 편집에 대해 눈살을 찌푸 리지만 이에 대해 자세히 알아 보려면 메타 토론에 참여해야합니다. 나는 개인적으로 때때로 그것이 약간 미친다는 것을 안다. 편집이 종료되는 것과는 전혀 관련이 없음을 유의하십시오. 편집 내용을 추가하려는 적절한 방법은 실제로 모든 것을 다른 답변으로 다시 게시하는 것입니다. 이렇게하면 내가 원래 제공 한 것 외에 귀하가 기여자임을 분명히 알 수 있습니다. 말이되지만 중재 도구는 좋은 피드백 메커니즘을 제공하지 않습니다.
John Leidegren 2014

18
SolutionFileVisual Studio 2015와 함께 설치된 Microsoft.Build.dll에 도입 된 새로운 공용 클래스가 있습니다 ( 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

이것을 지적한 Phil 에게 감사합니다 !


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
Visual Studio 2017과 함께 제공된 Microsoft.Build.dll v4.0.0.0에서 이러한 클래스를 찾을 수 없습니다.
Jeff G

@JeffG msbuild 만 설치하십시오.
Maciej Kucia

1
@JeffG 또한 VS 2017을 사용하고 있습니다. 참조 추가의 어셈블리 탭에서 Mircosoft.Build를 추가하면 SolutionFile에 액세스 할 수 없습니다. 그러나 위의 폴더에있는 dll을 찾아보고 참조하면 작동하는 것 같습니다.
Inrego

3
@JeffG이 패키지는 이제 NuGet nuget.org/packages/Microsoft.Build에서
Robert Hardy

16

아직이 문제에 대한 해결책을 찾고있는 사람이 있는지는 모르겠지만 필요한 작업을 수행하는 것으로 보이는 프로젝트를 실행했습니다. https://slntools.codeplex.com/ 이 도구의 기능 중 하나는 여러 솔루션을 함께 병합하는 것입니다.


테스트 한 솔루션 파일에서이 slntools는 실제로 ReSharper 라이브러리보다 더 많은 세부 정보를 제공했습니다.
Răzvan Flavius ​​Panda 2014

14

JetBrains (Resharper의 제작자)는 어셈블리에서 공개 sln 구문 분석 기능을 가지고 있습니다 (반영이 필요하지 않음). 아마도 여기에서 제안 된 기존 오픈 소스 솔루션 (ReGex 해킹은 제외)보다 더 강력 할 것입니다. 당신이해야 할 일은 :

  • 다운로드 ReSharper에서 명령 행 도구를 (무료).
  • 프로젝트에 대한 참조로 다음을 추가하십시오.
    • JetBrains.Platform.ProjectModel
    • JetBrains.Platform.Util
    • JetBrains.Platform.Interop.WinApi

라이브러리는 문서화되어 있지 않지만 Reflector (또는 실제로 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
참고 :이 게시물에서 네임 스페이스는 약간 다릅니다 [아마 JB가 항목을 리팩터링했습니다. :)] : ~ 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 2009

솔루션 폴더를 제외하려면 다음과 같이 작동합니다. (Get-Content MySolution.sln) | Where-Object {$ _ -match '(? = ^ Project (?! \ ( "\ {2150E333-8FDC-42A3-9474-1A3956D46DE8 \}"\))) ^ (\ w +)'} | ForEach-Object {$ _ -match ". * = (. *) $"| out-null; $ matches [1]} | ForEach-Object {$ _. Split ( ",") [1] .Trim (). Trim ( ' "')}
David Gardiner

6

새 솔루션을 만든 다음 * .sln 파일을 검색하고 다음을 사용하여 새 솔루션으로 가져 오는 Visual Studio 플러그인을 작성하여 솔루션을 자동으로 병합하는 유사한 문제를 해결했습니다.

dte2.Solution.AddFromFile(solutionPath, false);

우리의 문제는 VS가 빌드 순서를 정렬하기를 원한다는 점에서 약간 달랐으므로 가능한 경우 모든 dll 참조를 프로젝트 참조로 변환했습니다.

그런 다음 COM 자동화를 통해 VS를 실행하여이를 빌드 프로세스로 자동화했습니다.

이 솔루션은 약간의 Heath Robinson 이었지만 VS가 편집을 수행한다는 이점이 있었으므로 코드가 sln 파일의 형식에 의존하지 않았습니다. VS 2005에서 2008로, 그리고 다시 2010으로 이동할 때 도움이되었습니다.


DTE를 사용하여 성능 문제가 있었습니까? 그렇다면 어떻게 해결 했습니까? stackoverflow.com/questions/1620199/…
Chris Moutray

@mouters DTE를 사용하는 것과 GUI를 사용하는 것이 성능면에서 거의 비슷하다는 것을 알았습니다. 최소한의 VS 옵션 만 설치하면 도움이되었지만 많지는 않았습니다. 이것은 자동화되고 밤새 실행되기 때문에 성능은 우리가 관심을 두지 않았습니다.
Andy Lowry

sln에 project 포함 된 폴더가 있으면 폴더에 포함 된 프로젝트를 가져올 수 없습니다.
lindexi

5

모든 것이 훌륭하지만 sln 생성 기능도 얻고 싶었습니다. 위의 코드 스냅 샷에서는 .sln 파일 만 구문 분석하고 있습니다. .sln 파일을 약간 수정하여 sln을 다시 생성 할 수 있다는 점을 제외하면 비슷한 것을 만들고 싶었습니다. . 이러한 경우는 예를 들어 다른 .NET 플랫폼에 대해 동일한 프로젝트를 포팅 할 수 있습니다. 지금은 sln 재생 일 뿐이지 만 나중에 프로젝트로 확장 할 것입니다.

정규 표현식과 네이티브 인터페이스의 힘을 보여주고 싶었습니다. (더 많은 기능을 가진 적은 양의 코드)

업데이트 4.1.2017 .sln 솔루션을 구문 분석하기 위해 별도의 svn 저장소를 만들었습니다 : 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 2012

이것은 사이트가 IISExpress 등에서 실행되어야하는 URL이 되는 ASP.Net 웹 사이트 를 잘못 해석하는 것 같습니다 relativepath.
Doug

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
}

추가 도움말 System.Type로 @oasten이 제안한대로 a의 속성을 탐색 할 수있는 간단한 코드를 제공합니다 .

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에게 감사드립니다. 나는 s_SolutionParser_configurationsFullName없이 프로젝트와 프로젝트를 찾을 수없는 그의 코드를 사용할 수 없기 때문에 hlper 클래스를 작성합니다 .

코드는 FullName으로 프로젝트를 가져올 수 있는 github 에 있습니다.

그리고 코드는 SolutionConfiguration을 가져올 수 없습니다.

그러나 vsx를 개발할 때 vs는 찾을 수 없다고 말할 것이므로 dte를 Microsoft.Build.dll사용하여 모든 프로젝트를 가져올 수 있습니다.

dte를 사용하여 모든 프로젝트를 가져 오는 코드는 github에 있습니다.


@ NP83 감사합니다. 링크를 삭제했습니다
lindexi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.