A versão .NET 4.0 do assembly Microsoft.Build contém uma classe SolutionParser no namespace Microsoft.Build.Construction que analisa os arquivos de solução do Visual Studio.
Infelizmente, essa classe é interna, mas envolvi parte dessa funcionalidade em uma classe que usa reflexão para obter algumas propriedades comuns que podem ser úteis.
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();
}
}
Observe que você precisa alterar sua estrutura de destino para ".NET Framework 4" (não o perfil do cliente) para poder adicionar a referência Microsoft.Build ao seu projeto.
SolutionFile
introduzida no Microsoft.Build.dll que é instalado com o Visual Studio 2015 (consulte msdn.microsoft.com/en-us/library/… )Com o Visual Studio 2015, agora há uma
SolutionFile
classe acessível publicamente que pode ser usada para analisar arquivos de solução:Essa classe é encontrada no assembly Microsoft.Build.dll 14.0.0.0 . No meu caso, estava localizado em:
Obrigado a Phil por apontar isso !
fonte
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
Não sei se alguém ainda está procurando soluções para esse problema, mas me deparei com um projeto que parece fazer exatamente o que é necessário. https://slntools.codeplex.com/ Uma das funções dessa ferramenta é mesclar várias soluções.
fonte
JetBrains (os criadores do Resharper) têm habilidades de análise sintática pública em seus conjuntos (sem necessidade de reflexão). É provavelmente mais robusto do que as soluções de código aberto existentes sugeridas aqui (sem falar dos hacks ReGex). Tudo que você precisa fazer é:
JetBrains.Platform.ProjectModel
JetBrains.Platform.Util
JetBrains.Platform.Interop.WinApi
A biblioteca não está documentada, mas o Reflector (ou, na verdade, dotPeek) é seu amigo. Por exemplo:
fonte
Eu realmente não posso oferecer uma biblioteca e meu palpite é que não existe nenhuma lá fora. Mas eu passei muito tempo mexendo em arquivos .sln em cenários de edição em lote e descobri que o Powershell é uma ferramenta muito útil para essa tarefa. O formato .SLN é bastante simples e pode ser quase completamente analisado com algumas expressões rápidas e sujas. Por exemplo
Arquivos de projeto incluídos.
Nem sempre é bonito, mas é uma maneira eficaz de fazer processamento em lote.
fonte
resolvemos um problema semelhante de mesclar soluções automaticamente escrevendo um plug-in do Visual Studio que criou uma nova solução, em seguida, procurou o arquivo * .sln e os importou para o novo usando:
Nosso problema era um pouco diferente, pois queríamos que o VS classificasse a ordem de construção para nós, então convertemos todas as referências dll em referências de projeto sempre que possível.
Em seguida, automatizamos isso em um processo de construção executando o VS via automação COM.
Essa solução era um pouco Heath Robinson, mas tinha a vantagem de que o VS estava fazendo a edição, então nosso código não dependia do formato do arquivo sln. O que foi útil quando mudamos do VS 2005 para 2008 e novamente para 2010.
fonte
Tudo está ótimo, mas eu queria também obter capacidade de geração de sln - no instantâneo do código acima, você está apenas analisando arquivos .sln - eu queria fazer algo semelhante, exceto para poder regenerar o sln com pequenas modificações no arquivo .sln . Esses casos podem ser, por exemplo, portar o mesmo projeto para uma plataforma .NET diferente. Por enquanto, é apenas a regeneração do sln, mas mais tarde irei expandi-lo para projetos também.
Acho que também queria demonstrar o poder das expressões regulares e das interfaces nativas. (Menor quantidade de código com mais funcionalidade)
Atualização 4.1.2017 Criei um repositório svn separado para analisar a solução .sln: https://sourceforge.net/p/syncproj/code/HEAD/tree/
Abaixo está meu próprio trecho de amostra de código (predecessor). Você está livre para usar qualquer um deles.
É possível que, no futuro, o código de análise de solução baseada em svn seja atualizado com recursos de geração também.Atualização 4.2.2017 O código-fonte no SVN também oferece suporte à geração de .sln.
fonte
Expus, determinei que as classes MSBuild podem ser usadas para manipular as estruturas subjacentes. Terei mais código em meu site mais tarde.
fonte
relativepath
se torna a URL que o site deve executar no IISExpress etc.A resposta de @ john-leidegren é ótima. Para pré-VS2015, isso é de grande utilidade. Mas houve um pequeno erro, pois o código para recuperar as configurações estava faltando. Então, queria adicioná-lo, caso alguém esteja com dificuldades para usar esse código.
O aprimoramento é muito simples:
Como ajuda adicional, fornecer código simples para navegar pelas propriedades de a
System.Type
conforme sugerido por @oasten.fonte
Pelo que vale a pena, agora criei um pequeno projeto para ler os arquivos sln e proj disponíveis no nuget:
https://www.nuget.org/packages/ByteDev.DotNet/
fonte
Obrigado @John Leidegren, ele oferece uma maneira eficaz. Eu escrevo uma classe de hlper porque não posso usar o código dele que não consigo encontrar o
s_SolutionParser_configurations
os projetos sem FullName.O código está no github que pode obter os projetos com o FullName.
E o código não pode obter SolutionConfiguration.
Mas quando você desenvolve um vsx o vs dirá não consigo encontrar
Microsoft.Build.dll
, então você pode tentar usar dte para obter todos os projetos.O código que usa dte para obter todos os projetos está no github
fonte