Get path to .NET Framework directory in Powershell

2k Views Asked by At

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?

1

There are 1 best solutions below

2
Lance U. Matthews On BEST ANSWER

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.