Kiquenet
Kiquenet

Reputation: 15036

How do I programmatically list all projects in a solution?

How do I programmatically list all of the projects in a solution? I'll take a script, command-line, or API calls.

Upvotes: 55

Views: 42184

Answers (14)

Thomas Parikka
Thomas Parikka

Reputation: 802

Here is a modified approach I used to look at the problem from a Solution-first stance:

Function Get-ProjectReferences ($rootFolder) {
    $ns = @{ defaultNamespace = "http://schemas.microsoft.com/developer/msbuild/2003" }
    $solutionFilesWithContent = Get-ChildItem $rootFolder -Filter *.sln -Recurse |
    ForEach-Object {
        New-Object PSObject -Property @{
            SolutionFile    = $_;
            SolutionContent = Get-Content $_;
        }
    }
    $projectFilesWithContent = Get-ChildItem $rootFolder -Filter *.csproj -Recurse |
    ForEach-Object {
        New-Object PSObject -Property @{
            ProjectFile    = $_;
            ProjectContent = [xml](Get-Content $_);
        }  
    }

    $solutionFilesWithContent | ForEach-Object {
        $solutionFileWithContent = $_
        $projectsInSolutionStrings = $solutionFileWithContent.SolutionContent | Select-String 'Project\('
        $projectsInSolution = $projectsInSolutionStrings |
        ForEach-Object {
            $projectParts = $_ -Split '[,=]' | ForEach-Object { $_.Trim('[ "{}]') };
            if ($projectParts[2].Contains(".csproj")) {
                New-Object PSObject -Property @{
                    Name = $projectParts[1];
                    File = $projectParts[2];
                    Guid = $projectParts[3];
                }                    
            }
        }

        $projectsInSolution | ForEach-Object {
            $projectFileSearchName = $_.Name + ".csproj"
            $projectInSolutionFile = $projectFilesWithContent | Where-Object { $_.ProjectFile.Name -eq $projectFileSearchName } | Select-Object -First 1
            if (($null -eq $projectInSolutionFile) -and ($null -eq $projectInSolutionFile.ProjectContent)) {
                Write-Host "Project was null"
            }
            else {
                $projectInSolutionName = $projectInSolutionFile.ProjectFile | Select-Object -ExpandProperty BaseName
                $projectReferences = $projectInSolutionFile.ProjectContent | Select-Xml '//defaultNamespace:ProjectReference/defaultNamespace:Name' -Namespace $ns | Select-Object -ExpandProperty Node | Select-Object -ExpandProperty "#text"
                $projectTarget = $projectInSolutionFile.ProjectContent | Select-Xml "//defaultNamespace:TargetFrameworkVersion" -Namespace $ns | Select-Object -ExpandProperty Node | Select-Object -ExpandProperty "#text"
        
                $projectReferences | ForEach-Object {
                    $projectFileName = $_ + ".csproj"
                    $referenceProjectFile = $projectFilesWithContent | Where-Object { $_.ProjectFile.Name -eq $projectFileName } | Select-Object -First 1
                    if ($null -eq $referenceProjectFile) {
                        $referenceProjectTarget = "Unknown"
                    }
                    else {
                        $referenceProjectTarget = $referenceProjectFile.ProjectContent | Select-Xml "//defaultNamespace:TargetFrameworkVersion" -Namespace $ns | Select-Object -ExpandProperty Node | Select-Object -ExpandProperty "#text"                    
                    }
                    "[" + $solutionFileWithContent.SolutionFile.Name + "] -> [" + $projectInSolutionName + " " + $projectTarget + "] -> [" + $_ + " " + $referenceProjectTarget + "]"
                }
            }
        }
    }
}

Get-ProjectReferences "C:\src\repos\MyRepo" | Out-File "C:\src\repos\FrameworkDependencyAnalysis\FrameworkDependencyAnalysis.txt"

Upvotes: 0

Iakobos Karakizas
Iakobos Karakizas

Reputation: 111

from powershelll and in the solution's folder write

dotnet sln list

Upvotes: 8

MattMS
MattMS

Reputation: 1156

To expand on the answer by @brianpeiris:

Function Global:Get-ProjectInSolution {
    [CmdletBinding()] param (
        [Parameter()][string]$Solution
    )
    $SolutionPath = Join-Path (Get-Location) $Solution
    $SolutionFile = Get-Item $SolutionPath
    $SolutionFolder = $SolutionFile.Directory.FullName

    Get-Content $Solution |
        Select-String 'Project\(' |
        ForEach-Object {
            $projectParts = $_ -Split '[,=]' | ForEach-Object { $_.Trim('[ "{}]') }
            [PSCustomObject]@{
                File = $projectParts[2]
                Guid = $projectParts[3]
                Name = $projectParts[1]
            }
        } |
        Where-Object File -match "csproj$" |
        ForEach-Object {
            Add-Member -InputObject $_ -NotePropertyName FullName -NotePropertyValue (Join-Path $SolutionFolder $_.File) -PassThru
        }
}

This filters to only .csproj files and adds the full path of each based on the File field and the path containing the sln file.

Use Get-ProjectInSolution MySolution.sln | Select-Object FullName to get each of the full file paths.

The reason I wanted the full path was to be able to access the packages.config files beside each project file and then get the packages from all of them:

Get-ProjectInSolution MySolution.sln |
    %{Join-Path ($_.FullName | Split-Path) packages.config} |
    %{select-xml "//package[@id]" $_ | %{$_.Node.GetAttribute("id")}} |
    select -unique

Upvotes: 0

Jevgenij Kononov
Jevgenij Kononov

Reputation: 1237

I know that this is maybe already answered question, but I would like to share my approach of reading sln file. Also during run time I am determining if project is Test project or not

function ReadSolutionFile($solutionName)
{
    $startTime = (Get-Date).Millisecond
    Write-Host "---------------Read Start---------------" 
    $solutionProjects = @()

    dotnet  sln "$solutionName.sln" list | ForEach-Object{     
        if($_  -Match ".csproj" )
        {
            #$projData = ($projectString -split '\\')

            $proj = New-Object PSObject -Property @{

                Project = [string]$_;
                IsTestProject =   If ([string]$_ -Match "test") {$True} Else {$False}  
            }

            $solutionProjects += $proj

        }
    }

    Write-Host "---------------Read finish---------------" 
    $solutionProjects

    $finishTime = (Get-Date).Millisecond
    Write-Host "Script run time: $($finishTime-$startTime) mil" 
}

Hope this will be helpfull.

Upvotes: 2

Scott Mackay
Scott Mackay

Reputation: 1214

You can use the EnvDTE.Solution.Projects object to programmatically get access to the projects in a solution.

One gotcha though is that if you have any SolutionFolders in your solution, any projects in these folders are not shown in the above collection.

I've written an article including a code sample on how to get all projects regardless of any solutionfolders

Upvotes: 12

cezarypiatek
cezarypiatek

Reputation: 1124

Currently you can use Package Manager Console in VS to obtain that info. Use powershell Get-Project command

Get-Project -All

Upvotes: 11

Roland Roos
Roland Roos

Reputation: 1083

The trick is to choose the right MsBuild.dll. Under VS2017 it is indeed "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\amd64\Microsoft.Build.dll" (Dont use the standard Msbuild ddl in references. Browse to this path)

c#:

var solutionFile =    
SolutionFile.Parse(@"c:\NuGetApp1\NuGetApp1.sln");//your solution full path name
var projectsInSolution = solutionFile.ProjectsInOrder;
foreach(var project in projectsInSolution)
{
   switch (project.ProjectType)
   {
      case SolutionProjectType.KnownToBeMSBuildFormat:
     {
         break;
     }
     case SolutionProjectType.SolutionFolder:
     {
         break;
     }
  }
}

powershell:

Add-Type -Path (${env:ProgramFiles(x86)} + '\Microsoft Visual 
Studio\2017\Professional\MSBuild\15.0\Bin\amd64\Microsoft.Build.dll')

$slnPath = 'c:\NuGetApp1\NuGetApp1.sln'
$slnFile = [Microsoft.Build.Construction.SolutionFile]::Parse($slnPath)
$pjcts = $slnFile.ProjectsInOrder

foreach ($item in $pjcts)
{

    switch($item.ProjectType)
    {
        'KnownToBeMSBuildFormat'{Write-Host Project  : $item.ProjectName}
        'SolutionFolder'{Write-Host Solution Folder : $item.ProjectName}
    }
}  

Upvotes: 14

Dylan Berry
Dylan Berry

Reputation: 351

There's a really elegant solution here: Parsing Visual Studio Solution files

The answer by John Leidegren involves wrapping the internal Microsoft.Build.Construction.SolutionParser class.

Upvotes: 3

Adam Hardy
Adam Hardy

Reputation: 426

Since Visual Studio 2013 the Microsoft.Build.dll provides a SolutionFile object with some very handy functions.

Here's an example of using the v14.0 version to list the relative path of all the projects in the order they appear in the solution.

Add-Type -Path (${env:ProgramFiles(x86)} + '\Reference Assemblies\Microsoft\MSBuild\v14.0\Microsoft.Build.dll')
$solutionFile = '<FULL PATH TO SOLUTION FILE>'
$solution = [Microsoft.Build.Construction.SolutionFile] $solutionFile
($solution.ProjectsInOrder | Where-Object {$_.ProjectType -eq 'KnownToBeMSBuildFormat'}).RelativePath

There are plenty of other properties on the project object (ProjectName, AbsolutePath, configurations etc) that may be of use. In the above example I used the ProjectType to filter out Solution Folders.

Upvotes: 4

user147373
user147373

Reputation:

If you need to do this on a non Windows machine, you can use the following Bash command:

grep "Project(" NameOfYourSolution.sln | cut -d'"' -f4

Upvotes: 1

watbywbarif
watbywbarif

Reputation: 7017

    var Content = File.ReadAllText(SlnPath);
    Regex projReg = new Regex(
        "Project\\(\"\\{[\\w-]*\\}\"\\) = \"([\\w _]*.*)\", \"(.*\\.(cs|vcx|vb)proj)\""
        , RegexOptions.Compiled);
    var matches = projReg.Matches(Content).Cast<Match>();
    var Projects = matches.Select(x => x.Groups[2].Value).ToList();
    for (int i = 0; i < Projects.Count; ++i)
    {
        if (!Path.IsPathRooted(Projects[i]))
            Projects[i] = Path.Combine(Path.GetDirectoryName(SlnPath),
                Projects[i]);
        Projects[i] = Path.GetFullPath(Projects[i]);
    }

Edit: Amended the regex to include the ".*" as per the comment by Kumar Vaibhav

Upvotes: 21

brianpeiris
brianpeiris

Reputation: 10795

Here's a PowerShell script that retrieves project details from a .sln file:

Get-Content 'Foo.sln' |
  Select-String 'Project\(' |
    ForEach-Object {
      $projectParts = $_ -Split '[,=]' | ForEach-Object { $_.Trim('[ "{}]') };
      New-Object PSObject -Property @{
        Name = $projectParts[1];
        File = $projectParts[2];
        Guid = $projectParts[3]
      }
    }

Upvotes: 76

default locale
default locale

Reputation: 13456

just read the list from *.sln file. There are "Project"-"EndProject" sections.
Here is an article from MSDN.

Upvotes: 5

Oliver
Oliver

Reputation: 45119

If you write your program as Visual Studio Add-in you can access the EnvDTE to find out all the projects within the currently opened solution.

Upvotes: 3

Related Questions