¿Cómo puedo analizar los archivos de la solución de Visual Studio (SLN) en .NET? Me gustaría escribir una aplicación que combine varias soluciones en una mientras se guarda el orden de compilación relativo.
fuente
¿Cómo puedo analizar los archivos de la solución de Visual Studio (SLN) en .NET? Me gustaría escribir una aplicación que combine varias soluciones en una mientras se guarda el orden de compilación relativo.
La versión .NET 4.0 del ensamblado Microsoft.Build contiene una clase SolutionParser en el espacio de nombres Microsoft.Build.Construction que analiza los archivos de solución de Visual Studio.
Desafortunadamente, esta clase es interna, pero he incluido parte de esa funcionalidad en una clase que usa la reflexión para obtener algunas propiedades comunes que pueden resultarle útiles.
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();
}
}
Tenga en cuenta que debe cambiar su marco de destino a ".NET Framework 4" (no perfil de cliente) para poder agregar la referencia Microsoft.Build a su proyecto.
SolutionFile
introducida en Microsoft.Build.dll que se instala con Visual Studio 2015 (consulte msdn.microsoft.com/en-us/library/… )Con Visual Studio 2015, ahora hay una
SolutionFile
clase de acceso público que se puede usar para analizar archivos de solución:Esta clase se encuentra en el ensamblado Microsoft.Build.dll 14.0.0.0 . En mi caso estaba ubicado en:
¡Gracias a Phil por señalar esto !
fuente
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
No sé si alguien todavía está buscando soluciones a este problema, pero me encontré con un proyecto que parece hacer exactamente lo que se necesita. https://slntools.codeplex.com/ Una de las funciones de esta herramienta es fusionar múltiples soluciones.
fuente
JetBrains (los creadores de Resharper) tienen habilidades públicas de análisis de sln en sus ensamblajes (no se necesita reflexión). Probablemente sea más robusto que las soluciones de código abierto existentes sugeridas aquí (y mucho menos los hacks de ReGex). Todo lo que necesitas hacer es:
JetBrains.Platform.ProjectModel
JetBrains.Platform.Util
JetBrains.Platform.Interop.WinApi
La biblioteca no está documentada, pero Reflector (o de hecho, dotPeek) es tu amigo. Por ejemplo:
fuente
Realmente no puedo ofrecerte una biblioteca y supongo que no existe ninguna. Pero he pasado mucho tiempo jugando con archivos .sln en escenarios de edición por lotes y descubrí que Powershell es una herramienta muy útil para esta tarea. El formato .SLN es bastante simple y se puede analizar casi por completo con algunas expresiones rápidas y sucias. Por ejemplo
Archivos de proyecto incluidos.
No siempre es bonito, pero es una forma eficaz de procesar por lotes.
fuente
Resolvimos un problema similar de combinar soluciones automáticamente escribiendo un complemento de Visual Studio que creó una nueva solución, luego buscó el archivo * .sln y lo importó al nuevo usando:
Nuestro problema era ligeramente diferente en el sentido de que queríamos que VS resolviera el orden de compilación por nosotros, por lo que luego convertimos cualquier referencia dll a referencias de proyecto cuando fuera posible.
Luego automatizamos esto en un proceso de compilación ejecutando VS a través de la automatización COM.
Esta solución era un poco Heath Robinson, pero tenía la ventaja de que VS estaba haciendo la edición, por lo que nuestro código no dependía del formato del archivo sln. Lo cual fue útil cuando nos mudamos de VS 2005 a 2008 y nuevamente a 2010.
fuente
Todo es genial, pero también quería obtener la capacidad de generación de sln; en la instantánea de código anterior, solo está analizando archivos .sln; quería hacer algo similar, excepto para poder volver a generar sln con ligeras modificaciones al archivo .sln . Tales casos podrían ser, por ejemplo, portar el mismo proyecto para una plataforma .NET diferente. Por ahora solo es una regeneración de sln, pero más adelante también lo expandiré a proyectos.
Supongo que también quería demostrar el poder de las expresiones regulares y las interfaces nativas. (Menor cantidad de código con más funcionalidad)
Actualización 4.1.2017 He creado un repositorio svn separado para analizar la solución .sln: https://sourceforge.net/p/syncproj/code/HEAD/tree/
A continuación se muestra mi propio fragmento de código de muestra (predecesor). Eres libre de usar cualquiera de ellos.
Es posible que en el futuro el código de análisis de la solución basada en svn también se actualice con capacidades de generación.Actualización 4.2.2017 El código fuente en SVN también admite la generación .sln.
fuente
Expuse, determiné que las clases de MSBuild se pueden usar para manipular las estructuras subyacentes. Tendré más código en mi sitio web más adelante.
fuente
relativepath
convierte en la URL en la que el sitio debería ejecutarse en IISExpress, etc.La respuesta de @ john-leidegren es genial. Para versiones anteriores a VS2015, esto es de gran utilidad. Pero hubo un pequeño error, ya que faltaba el código para recuperar las configuraciones. Así que quería agregarlo, en caso de que alguien tenga dificultades para usar este código.
La mejora es muy simple:
Como ayuda adicional, proporciona un código simple para navegar a través de las propiedades de a
System.Type
como lo sugiere @oasten.fuente
Por lo que vale, ahora he creado un pequeño proyecto para leer archivos sln y proj disponibles en nuget:
https://www.nuget.org/packages/ByteDev.DotNet/
fuente
Gracias a @John Leidegren, ofrece una forma eficaz. Escribo una clase hlper porque no puedo usar su código que no puede encontrar el
s_SolutionParser_configurations
y los proyectos sin FullName.El código está en github que puede obtener los proyectos con FullName.
Y el código no puede obtener SolutionConfiguration.
Pero cuando desarrolle un vsx, el vs dirá que no se puede encontrar
Microsoft.Build.dll
, por lo que puede intentar usar dte para obtener todos los proyectos.El código que usa dte para obtener todos los proyectos está en github
fuente