Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Saturday, January 23, 2016

Parsing Visual Studio Solution files

Parsing Visual Studio Solution files


How can I parse Visual Studio solution (SLN) files in .NET? I would like to write an app that merges multiple solutions into one while saving the relative build order.

Answer by JaredPar for Parsing Visual Studio Solution files


I can't really offer you a library and my guess is there isn't one that exists out there. But I've spent a deal of time messing around with .sln files in batch editting scenarios and I've found Powershell to be a very useful tool for this task. The .SLN format is pretty simple and can be almost completely parsed with a few quick and dirty expressions. For Example

Included Project files.

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

It's not always pretty, but it is an effective way to do batch processing.

Answer by John Leidegren for Parsing Visual Studio Solution files


The .NET 4.0 version of the Microsoft.Build assembly contains a SolutionParser class in the Microsoft.Build.Construction namespace that parses Visual Studio solution files.

Unfortunately this class is internal, but I've wrapped some of that functionality in a class that uses reflection to get at some common properties you might find helpful.

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 Projects { get; private set; }      public List Configurations { 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();          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;            Object cfgArray = s_SolutionParser_configurations.GetValue(solutionParser, null);          PropertyInfo[] pInfos = null;          pInfos = cfgArray.GetType().GetProperties();          int count = (int)pInfos[1].GetValue(cfgArray, null);            var configs = new List();          for (int i = 0; i < count; i++)          {              configs.Add(new SolutionConfiguration(pInfos[2].GetValue(cfgArray, new object[] { i })));          }            this.Configurations = configs;      }  }    [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();      }  }    public class SolutionConfiguration  {      static readonly Type s_ConfigInSolution;      static readonly PropertyInfo configInSolution_configurationname;      static readonly PropertyInfo configInSolution_fullName;      static readonly PropertyInfo configInSolution_platformName;        static SolutionConfiguration()      {          s_ConfigInSolution = Type.GetType("Microsoft.Build.Construction.ConfigurationInSolution, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);          if (s_ConfigInSolution != null)          {              configInSolution_configurationname = s_ConfigInSolution.GetProperty("ConfigurationName", BindingFlags.NonPublic | BindingFlags.Instance);              configInSolution_fullName = s_ConfigInSolution.GetProperty("FullName", BindingFlags.NonPublic | BindingFlags.Instance);              configInSolution_platformName = s_ConfigInSolution.GetProperty("PlatformName", BindingFlags.NonPublic | BindingFlags.Instance);          }      }        public string configurationName { get; private set; }      public string fullName { get; private set; }      public string platformName { get; private set; }        public SolutionConfiguration(object solutionConfiguration)      {          this.configurationName = configInSolution_configurationname.GetValue(solutionConfiguration, null) as string;          this.fullName = configInSolution_fullName.GetValue(solutionConfiguration, null) as string;          this.platformName = configInSolution_platformName.GetValue(solutionConfiguration, null) as string;      }  }  

Note that you have to change your target framework to ".NET Framework 4" (not client profile) to be able to add the Microsoft.Build reference to your project.

Answer by Andy Lowry for Parsing Visual Studio Solution files


we solved a similar problem of merging solutions automatically by writing a Visual Studio plugin which created a new solution then searched for *.sln file and imported them into the new one using:

dte2.Solution.AddFromFile(solutionPath, false);  

Our problem was slightly different in that we wanted VS to sort out the build order for us, so we then converted any dll references to project references where possible.

We then automated this into a build process by running VS via COM automation.

This solution was a little Heath Robinson, but had the advantage that VS was doing the editing so our code was not dependant on format of the sln file. Which was helpful when we moved from VS 2005 to 2008 and again to 2010.

Answer by Ken for Parsing Visual Studio Solution files


I expounded, determined that the MSBuild classes can be used to manipulate the underlying structures. I will have further code on my web site later.

// 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 projects;            public string Name          {              get              {                  return Path.GetFileNameWithoutExtension(solutionFileName);              }          }            public IEnumerable 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();              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 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 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();                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 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;                  }              }          }      }  }  

Answer by WiredWiz for Parsing Visual Studio Solution files


I don't know if anyone is still looking for solutions to this problem, but I ran across a project that seems to do just what is needed. https://slntools.codeplex.com/ One of the functions of this tool is to merge multiple solutions together.

Answer by Ohad Schneider for Parsing Visual Studio Solution files


JetBrains (the creators of Resharper) have public sln parsing abilities in their assemblies (no reflection needed). It's probably more robust than the existing open source solutions suggested here (let alone the ReGex hacks). All you need to do is:

  • Download the ReSharper Command Line Tools (free).
  • Add the following as references to your project
    • JetBrains.Platform.ProjectModel
    • JetBrains.Platform.Util
    • JetBrains.Platform.Interop.WinApi

The library is not documented, but Reflector (or indeed, dotPeek) is your friend. For example:

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

Answer by TarmoPikaro for Parsing Visual Studio Solution files


Everything is great, but I wanted also to get sln generation capability - in code snapshot above you're only parsing .sln files - I wanted to make similar thing except to be able to re-generate sln with slight modifications back to .sln file. Such cases could be for example porting same project for different .NET platform. For now it's only sln re-generation, but later on I will expand it to projects as well.

I guess that I wanted also to demonstrate the power of regular expressions and native interfaces. (Smaller amount of code with more functionality)

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 + "\"";          }      }    ///   /// .sln loaded into class.  ///   public class Solution  {      public List slnLines;       // List of either String (line format is not intresting to us), or SolutionProject.        ///       /// Loads visual studio .sln solution      ///       ///       /// The file specified in path was not found.      public Solution( string solutionFileName )      {          slnLines = new List();          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\\(\"(?{[A-F0-9-]+})\"\\) = \"(?.*?)\", \"(?.*?)\", \"(?{[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          );      }        ///       /// Gets list of sub-projects in solution.      ///       /// true if get also sub-folders.      public List 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();      }        ///       /// Saves solution as file.      ///       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);          }      }  }    

Answer by Maciej Kucia for Parsing Visual Studio Solution files


With Visual Studio 2015 there is now a publicly accessible SolutionFile class which can be used to parse solution files:

using Microsoft.Build.Construction;  var _solutionFile = SolutionFile.Parse(path);  

This class is found in the Microsoft.Build.dll 14.0.0.0 assembly. In my case it was located at:

C:\Program Files (x86)\Reference Assemblies\Microsoft\MSBuild\v14.0\Microsoft.Build.dll  

Thanks to Phil for pointing this out!


Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72

0 comments:

Post a Comment

Popular Posts

Powered by Blogger.