Archives

Archives / 2007 / February
  • Enumerating projects in a Visual Studio solution

    People writing add-ins for Visual studio probably have come across the nightmare of the Visual studio object model to go through the projects within a solution. I had to get the list of projects in a solution, and for each solution its output path in the current configuration. Although I need the code in C# later on, the best way to check out how to program against the Visual Studio object model is within the Macro IDE of Visual Studio (Tools->Macros->Macros IDE).

    Although the question seems quite straightforward, the trouble begins when you have Solution Folders containing projects. The Solution object has a property called Projects, but Solution Folders are also seen as Projects. A project has project items. A project item can be a folder, a file, or... a project. You have to see it to believe the mess.

    After a lot of trial and error I got to a solution where I check if a project item has its ConfigurationManager property set, if this is the case, it must be a project. Not sure if this is the best approach, but it works for me. For example for the following project structure using Solution Folders:

    I get the following output:

    And the code to accomplish this (paste in  Macros IDE):

    Sub SolutionProjects()
            Dim project As EnvDTE.Project
            Write("--------- DUMP OF ALL PROJECTS IN CURRENT SOLUTION -------------")
            Try
                If Not DTE.Solution.IsOpen Then
                    Write("There is no solution open.")
                Else
                    For Each project In DTE.Solution.Projects
                        NavProj(project)
                    Next
                End If
            Catch ex As System.Exception
                Write(ex.ToString)
            End Try
        End Sub
        Sub NavProj(ByVal proj As Project)
            Dim outputPathProperty As EnvDTE.Property
            Dim outputPath As String
    
            If Not (proj.ConfigurationManager Is Nothing) Then
                ' It's a project!
                outputPathProperty = _
                proj.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath")
                If outputPathProperty Is Nothing Then
                    outputPath = "<no output path set>"
                Else
                    outputPath = outputPathProperty.Value
                End If
                Write("### Project: " + proj.Name + ", OutputPath: " + outputPath)
            Else
                NavProjItems(proj.ProjectItems)
            End If
        End Sub
        Sub NavProjItems(ByVal projItems As ProjectItems)
            Dim projectItem As EnvDTE.ProjectItem
    
            If Not (projItems Is Nothing) Then
                For Each projectItem In projItems
                    If Not (projectItem.SubProject Is Nothing) Then
                        ' Recurse, can be an Enterprise project in 
                        ' Visual Studio .NET 2002/2003 or a solution folder in VS 2005
                        NavProj(projectItem.SubProject)
                    End If
                Next
            End If
        End Sub
    
        Sub Write(ByVal s As String)
            Dim out As OutputWindowPane = GetOutputWindowPane("MACRO OUTPUT", True)
            out.OutputString(s)
            out.OutputString(vbCrLf)
        End Sub
    Function GetOutputWindowPane(ByVal Name As String, Optional ByVal show As Boolean = True) As OutputWindowPane Dim win As Window = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput) If show Then win.Visible = True Dim ow As OutputWindow = win.Object Dim owpane As OutputWindowPane Try owpane = ow.OutputWindowPanes.Item(Name) Catch e As System.Exception owpane = ow.OutputWindowPanes.Add(Name) End Try owpane.Activate() Return owpane End Function
  • VS 2005 image library - all the icons you need !

    I am busy writing a toolwindow extension for Visual Studio 2005, and I needed some icons for my toolbar in the same style as Visual Stuido 2005. In search of this I stumbled accross the VS2005ImageLibrary, a zip file containing all the icons you need!

    Path: X:\Program Files\Microsoft Visual Studio 8\Common7\VS2005ImageLibrary

  • PowerShell - Windows PowerShell In Action - Bruce Payette

    I have done a lot of coding in Windows PowerShell last months, and my biggest help next to some of the great weblogs on PowerShell like the weblog of The PowerShell Guy and the PowerShell team., was an early access version of the Windows PowerShell in Action e-book. Today I got the final e-book version, and it got even better! You can now preorder the book at Amazon, it will be available real soon. If you are serious about PowerShell: get this book!! It will get 5-* ratings on Amazon, I'm sure!

  • PowerShell - function return values implementing IEnumerable are decomposed into an array

    In this blog post I wrote about (by me) unexpected implicit type conversion on return values of functions which is actually really cool: all types returned that implement the IEnumerable interface are decomposed in an array of the elements that are enumerated in the IEnumerable type. This collection of elements is returned "on the pipeline". This decompositioning goes one level deep (a pipeline is linear). So if an array of values is returned, the values don't get decomposed. An array with a single element can be returned as follows: ,$element.

    An example follows below with two functions: foo where the return value gets decomposed into an array, and bar where we stoped the decompositioning by returning the element in an array:

    function foo
    {
        $arraylist = new-Object System.Collections.ArrayList
        [void]$arraylist.Add("one")
        [void]$arraylist.Add("two")
        [void]$arraylist.Add("three")
        return $arraylist # implicit conversion to array
    }

    function bar
    {
        $arraylist = new-Object System.Collections.ArrayList
        [void]$arraylist.Add("one")
        [void]$arraylist.Add("two")
        [void]$arraylist.Add("three")
        return ,$arraylist
    }

    foo | foreach ($_) {Write-Host "foo value (with type: $($_.GetType()) in pipe: $_" }
    bar | foreach ($_) {Write-Host "bar value (with type: $($_.GetType()) in pipe: $_" }

    This results in:

    foo value (with type: System.String in pipe: one
    foo value (with type: System.String in pipe: two
    foo value (with type: System.String in pipe: three
    bar value (with type: System.Collections.ArrayList in pipe: one two three

  • PowerShell - referencing files relative to the currently executing script

    I often have the situation that that I want to do a relative reference  to a script file from another script file. This works ok if we execute the referencing script file from its current location, but what if  that script is referenced as well. Ok, a bit vague, an example:

    Folder c:\test contains script X.ps1
    Folder c:\test\sub contains script Y.ps1
    Folder c:\test\sub contains script Z.ps1

    The initially executing script is: C:\test\X.ps1

    The scripts are defined as follows:

    C:\test\X.ps1:
    ./sub/Y.ps1
    Write-Host "X"

    C:\test\sub\Y.ps1:
    ./Z.ps1 # this one not found, because the current folder is c:\test, not c:\test\sub
    Write-Host "Y"

    C:\test\sub\Z.ps1:
    Write-Host "Z"

    I have this problem if Y.ps1 and Z.ps1 are part of a utility library, and X.ps1 is one of the calling scripts. A good example is the psexpect library from www.codeplex.com where I had exactly this problem.

    The PowerShell guy (/\/\o\/\/) came to the rescue with the following solution:

    Split-Path -Path $MyInvocation.MyCommand.Path -Parent

    We can rewrite our scripts as follows:


    C:\test\X.ps1:
    $executingScriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
    Write-Host "Script X: script folder=$executingScriptDirectory"
    & "$executingScriptDirectory/sub/Y.ps1"
    Write-Host "X"

    C:\test\sub\Y.ps1:
    $executingScriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
    Write-Host "Script Y: script folder=$executingScriptDirectory"
    & "$executingScriptDirectory/Z.ps1"
    Write-Host "Y"

    C:\test\sub\Z.ps1:
    Write-Host "Z"

    • The start script C:\test\X.ps1 can now be executed from any location.
    • The library script C:\test\sub\Y.ps1 can be utilized by any script.

    Note that this code must be executed in the body of the script, and not from within a function!!

    The one strange thing that puzzles me now is why PowerShell Analyzer gives the following exception:

    --------------------------------------------------------------------------------
    POWERSHELL EXCEPTION
    EXCEPTION TYPE:System.Management.Automation.CmdletInvocationException
    MESSAGE:Cannot find drive. A drive with name '$executingScriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
    Write-Host "Script X' does not exist.
    POSITION:
    At line:1 char:39
    + $executingScriptDirectory = Split-Path <<<< -Path $MyInvocation.MyCommand.Definition -Parent
    --------------------------------------------------------------------------------

    So many words to explain a problem and solve an issue that was so easy in cmd.exe, ~dp0 did the job!

  • PowerShell - Strange implicit type conversion

    PowerShell is great, but sometimes it has some strange quircks that can cost you a lot of time to solve. My latest "obstacle" was the following situation:

    function ArgsToArrayList {
      $al = new-object System.Collections.ArrayList
      $args | foreach { [void] $al.add($_) }
      $al
    }

    $x = ArgsToArrayList 1 2 3 4 5
    write-Host "5 entries, type: " $x.GetType()
    $x = ArgsToArrayList
    write-Host "0 entries, is null: " ($x -eq $null)

    I expect the function ArgsToArrayList to always return an object of type System.Collections.ArrayList, independent of the number of arguments passed. I was wrong.

    PowerShell converts the return value of the function ArgsToArrayList from System.Collections.ArrayList to System.Object[] when it contains entries, and to $null when the arraylist is empty.

    So the output of the above code is:

    5 entries, type: System.Object[]
    0 entries, is null: True

    Beats me...

  • VS.NET 2005 Tool Window for testing XPath expressions - XPathmania

    I had to define some XPath expressions to query information from an XML document. I couldn't find any standard support for this in Visual Studio 2005, but after some searching I stumbled accross XPathmania, a tool window developed by Don Xml. Works like a breeze! See below. See this blog entry for more information. Download it here. Source code is available as well.

  • Console2 - a great Windows console window enhancer for cmd.exe and PowerShell

    Through a discussion thread on the PowerShell Community Extensions forum at Codeplex I found Console2, a great tool that enhances the working experience with Windos console window that is normally used by Cmd.exe and PowerShell. You can resize the window dynamically, do normal text selections (use shift+mouse), have tabs, and do all kind of style configurations.See the discussion thread for information on configuration for PowerShell. Check it out!