Search code examples
powershellenvdte

How do you walk the solution hierarchy?


I've been working on this about 14 hours now. It is totally driving me crazy.

Without referencing any custom dlls I want to walk $dte.Solution.Projects for either projects or projectitems to check if

$SourceControl = get-interface $dte.SourceControl ([EnvDTE.SourceControl])
$SourceControl.IsItemUnderScc()

Things like solution folders get in the way. I've done this just fine in C# repeatedly (and in F#), I just can't seem to do it in powershell. I have done it both (C#) via Dte.Solution and UIHierarchy

My C# walking code is in a T4 Nuget Package as an example, but all my linqpad samples currently use UIHierarchy

Here's a sample from that c# that doesn't seem to work in powershell:

C# Projects projects = dte.Solution.Projects;

Powershell $Projects= [EnvDTE.Projects]$dte.Solution.Projects

which fails with Cannot convert the "System.__ComObject" value of type "System.__ComObject#{e3ec0add-31b3-461f-8303-8a5e6931257a}" to type "EnvDTE.Projects".


Solution

  • Using the code provided later in this answer calling it for just the first project in your solution would be GetUnversionedFiles(GetProjectFiles((GetSolutionProjects).get_Item(1)))

    to run this code GetUnversionedFiles(GetProjectFiles((GetSolutionProjects).get_Item(0)))


    Get the projects in the solution

    function GetSolutionProjects(){
        $projects = get-interface $dte.Solution.Projects ([EnvDTE.Projects])
        write-debug "projects=$projects"
        $result = new-object "System.Collections.Generic.List[EnvDTE.Project]"
        foreach($project in $projects.GetEnumerator()) {
                if($project -eq $null){
                    continue;
                }
    
                write-debug "yay project or solution folder! $project $project.Kind"
                if($project.Kind -eq [EnvDTE80.ProjectKinds]::vsProjectKindSolutionFolder){
                    write-debug ("Solution folder "+$project.Name)
    
                    foreach($solutionFolderProject in RecurseSolutionFolderProjects($project)){
                        $result+=$solutionFolderProject
                    }
    
                } else {
                    write-debug ("else " +$project.Name +  " " + $project.Kind
                    $result+=$project
                }
        }
        return $result
    }
    

    recurse down solution folders

    function RecurseSolutionFolderProjects(){
        param($solutionFolder = $(throw "Please specify a solutionFolder"))
        $projectList = @()
        for($i = 1; $i -le $solutionFolder.ProjectItems.Count; $i++){
            $subProject = $solutionFolder.ProjectItems.Item($i).subProject
            if($subProject -eq $null){
                continue;
            }
    
            if($subProject.Kind -eq [EnvDTE80.ProjectKinds]::vsProjectKindSolutionFolder)
            {
                $projectList += RecurseSolutionFolderProjects($subProject)
            } else {
                $projectList += $subProject
            }
        }
        return $projectList
    }
    

    walk down projectItems

    function RecurseDescendants(){
        param($source  = $(throw "Please specify a source"))
        write-debug "starting RecurseDescendants"
        $result = new-object "System.Collections.Generic.List[EnvDTE.ProjectItem]"
        foreach($s in $source){
            #write-host "working on " $s.Kind $s.Name $s.FileNames(0)
    
            $pi = $s.Object -as [EnvDTE.ProjectItem]
            $result+=$s
            $children=$s.ProjectItems
            foreach($child in RecurseDescendants($children)){
                $result+=$child
            }
            write-debug "inner for each stopped"
        }
        write-debug "outer for each finished"
        return $result
    }
    

    walk project for files within

    function GetProjectFiles(){
        param($project = $(throw "Please specify a project"))
    
        write-debug ("getting project files for " + $project.Name + " "+ $project.ProjectName)
    
        $projectItems = RecurseDescendants($project.ProjectItems)
            return $projectItems | Where-Object {$_.Kind -ne [EnvDTE.Constants]::vsProjectItemKindPhysicalFolder}
        }
    

    Check found files for being in version control

        function GetUnversionedFiles(){
            param($items = $(throw "Please specify items"))
            write-host "checking for unversioned files"
    
            $SourceControl = get-interface $dte.SourceControl ([EnvDTE.SourceControl])
            return $items | Where-Object {$SourceControl.IsItemUnderSCC($_.FileNames(0)) -eq $FALSE }
    
        }
    

    walk project for items within

    function GetProjectItems(){ 
        param($project = $(throw "Please specify a project"))
        if($project.ProjectItems.count -gt 0){
            write-debug "getting project items for '$project.Name' '$project.ProjectName'"
        }
        #example: GetProjectItems((GetSolutionProjects).get_Item(1))
        $result =RecurseDescendants($project.ProjectItems)
        return $result
    }
    

    the most current version is at https://github.com/ImaginaryDevelopment/config/blob/master/My%20Documents/WindowsPowerShell/NuGet_profile.ps1