Search code examples
.netpowershellmsbuild.net-framework-versionmicrosoft.build

Get path to .NET Framework directory in Powershell


I am trying to use this method to get the .NET Framework directory (e.g. C:\Windows\Microsoft.NET\v4.0.30319)

Add-Type -AssemblyName Microsoft.Build.Utilities

$dotNetDir = [Microsoft.Build.Utilities.ToolLocationHelper]::GetPathToDotNetFramework([Microsoft.Build.Utilities.TargetDotNetFrameworkVersion]::VersionLatest)

However, it is returning nothing. No error, just blank.

I know there are other ways to get the .NET directory but I am wondering why this method is not working?


Solution

  • TL;DR The Microsoft.Build.Utilities assembly doesn't support framework versions newer than 2.0. Use a newer assembly to get support for newer framework versions.


    On 64-bit Windows 10 Professional I get the following output...

    PS> $PSVersionTable
    Name                           Value
    ----                           -----
    PSVersion                      5.1.18362.145
    PSEdition                      Desktop
    PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
    BuildVersion                   10.0.18362.145
    CLRVersion                     4.0.30319.42000
    WSManStackVersion              3.0
    PSRemotingProtocolVersion      2.3
    SerializationVersion           1.1.0.1
    
    
    PS> [Microsoft.Build.Utilities.ToolLocationHelper]::GetPathToDotNetFramework([Microsoft.Build.Utilities.TargetDotNetFrameworkVersion]::VersionLatest)
    C:\Windows\Microsoft.NET\Framework\v2.0.50727
    

    I also notice that the VersionLatest enumeration value is an alias for Version20...

    PS> [Microsoft.Build.Utilities.TargetDotNetFrameworkVersion]::VersionLatest
    Version20
    

    That is, VersionLatest evidently doesn't mean "find the latest installed framework version at runtime", it's just a flexible way to specify the latest available version known at compile-time (of that Microsoft.Build.Utilities assembly), of which there are not many...

    PS> $enumType = [Microsoft.Build.Utilities.TargetDotNetFrameworkVersion]
    PS> [Enum]::GetNames($enumType) `
        | ForEach-Object -Process {
            $value = $enumType::$_;
            New-Object -TypeName 'PSCustomObject' -Property (
                [Ordered] @{
                    Name = $_;
                    ValueText = $value;
                    ValueInt32 = [Int32] $value
                }
            )
        }
    
    Name          ValueText ValueInt32
    ----          --------- ----------
    Version11     Version11          0
    Version20     Version20          1
    VersionLatest Version20          1
    

    Regarding the results you see on different operating systems, Windows 7 has .NET 2.0 installed by default and, if I recall, Windows 10 has .NET 4.0 but not 2.0 installed by default, so if you didn't change anything that would explain why you get outdated results on Windows 7 and no results on Windows 10. I do have .NET 2.0 as an installed feature on Windows 10, which is why the method is able to find that framework directory.

    To fix this, you need to use a newer Microsoft.Build.Utilities* assembly, which uses a new name for each version. On my system I have Microsoft.Build.Utilities and Microsoft.Build.Utilities.v3.5 in the GAC, but the latter only supports up to .NET 3.5. Instead, I installed Microsoft.Build.Utilities.Core from NuGet...

    PS> Install-Package -Name 'Microsoft.Build.Utilities.Core' -ProviderName 'NuGet'
    

    After passing Microsoft.Build.Utilities.Core.dll and its dependencies to Add-Type, I get a much longer list of Microsoft.Build.Utilities.TargetDotNetFrameworkVersion values using the snippet above...

    Name              ValueText ValueInt32
    ----              --------- ----------
    Version11         Version11          0
    Version20         Version20          1
    Version30         Version30          2
    Version35         Version35          3
    Version40         Version40          4
    Version45         Version45          5
    Version451       Version451          6
    Version46         Version46          7
    Version461       Version461          8
    Version452       Version452          9
    Version462    VersionLatest         10
    VersionLatest VersionLatest         10
    Version47         Version47         11
    Version471       Version471         12
    Version472       Version472         13
    Latest               Latest       9999
    

    Now your original code finally returns the directory for .NET 4.0...

    PS> [Microsoft.Build.Utilities.ToolLocationHelper]::GetPathToDotNetFramework([Microsoft.Build.Utilities.TargetDotNetFrameworkVersion]::VersionLatest)
    C:\Windows\Microsoft.NET\Framework64\v4.0.30319
    

    I notice there is a new Latest enumeration value as well which looks like it really does mean "the version of the latest installed framework", though it (currently) returns the same path as passing VersionLatest.