How to load assemblies in PowerShell?

LoadWithPartialName has been deprecated. The recommended solution for PowerShell V3 is to use the Add-Type cmdlet e.g.:

Add-Type -Path 'C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll'

There are multiple different versions and you may want to pick a particular version. :-)


[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")

Most people know by now that System.Reflection.Assembly.LoadWithPartialName is deprecated, but it turns out that Add-Type -AssemblyName Microsoft.VisualBasic does not behave much better than LoadWithPartialName:

Rather than make any attempt to parse your request in the context of your system, [Add-Type] looks at a static, internal table to translate the "partial name" to a "full name".

If your "partial name" doesn't appear in their table, your script will fail.

If you have multiple versions of the assembly installed on your computer, there is no intelligent algorithm to choose between them. You are going to get whichever one appears in their table, probably the older, outdated one.

If the versions you have installed are all newer than the obsolete one in the table, your script will fail.

Add-Type has no intelligent parser of "partial names" like .LoadWithPartialNames.

What Microsoft's .Net teams says you're actually supposed to do is something like this:

Add-Type -AssemblyName 'Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

Or, if you know the path, something like this:

Add-Type -Path 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Microsoft.VisualBasic\v4.0_10.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualBasic.dll'

That long name given for the assembly is known as the strong name, which is both unique to the version and the assembly, and is also sometimes known as the full name.

But this leaves a couple questions unanswered:

  1. How do I determine the strong name of what's actually being loaded on my system with a given partial name?

    [System.Reflection.Assembly]::LoadWithPartialName($TypeName).Location; [System.Reflection.Assembly]::LoadWithPartialName($TypeName).FullName;

These should also work:

Add-Type -AssemblyName $TypeName -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. If I want my script to always use a specific version of a .dll but I can't be certain of where it's installed, how do I determine what the strong name is from the .dll?

    [System.Reflection.AssemblyName]::GetAssemblyName($Path).FullName;

Or:

Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. If I know the strong name, how do I determine the .dll path?

    [Reflection.Assembly]::Load('Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a').Location;

  2. And, on a similar vein, if I know the type name of what I'm using, how do I know what assembly it's coming from?

    [Reflection.Assembly]::GetAssembly([Type]).Location [Reflection.Assembly]::GetAssembly([Type]).FullName

  3. How do I see what assemblies are available?

I suggest the GAC PowerShell module. Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullName works pretty well.

  1. How can I see the list that Add-Type uses?

This is a bit more complex. I can describe how to access it for any version of PowerShell with a .Net reflector (see the update below for PowerShell Core 6.0).

First, figure out which library Add-Type comes from:

Get-Command -Name Add-Type | Select-Object -Property DLL

Open the resulting DLL with your reflector. I've used ILSpy for this because it's FLOSS, but any C# reflector should work. Open that library, and look in Microsoft.Powershell.Commands.Utility. Under Microsoft.Powershell.Commands, there should be AddTypeCommand.

In the code listing for that, there is a private class, InitializeStrongNameDictionary(). That lists the dictionary that maps the short names to the strong names. There's almost 750 entries in the library I've looked at.

Update: Now that PowerShell Core 6.0 is open source. For that version, you can skip the above steps and see the code directly online in their GitHub repository. I can't guarantee that that code matches any other version of PowerShell, however.

Update 2: Powershell 7+ does not appear to have the hash table lookup any longer. Instead they use a LoadAssemblyHelper() method which the comments call "the closest approximation possible" to LoadWithPartialName. Basically, they do this:

loadedAssembly = Assembly.Load(new AssemblyName(assemblyName));

Now, the comments also say "users can just say Add-Type -AssemblyName Forms (instead of System.Windows.Forms)". However, that's not what I see in Powershell v7.0.3 on Windows 10 2004.

# Returns an error
Add-Type -AssemblyName Forms

# Returns an error
[System.Reflection.Assembly]::Load([System.Reflection.AssemblyName]::new('Forms'))

# Works fine
Add-Type -AssemblyName System.Windows.Forms

# Works fine
[System.Reflection.Assembly]::Load([System.Reflection.AssemblyName]::new('System.Windows.Forms'))

So the comments appear to be a bit of a mystery.

I don't know exactly what the logic is in Assembly.Load(AssemblyName) when there is no version or public key token specified. I would expect that this has many of the same problems that LoadWithPartialName does like potentially loading the wrong version of the assembly if you have multiple installed.


If you want to load an assembly without locking it during the duration of the PowerShell session, use this:

$bytes = [System.IO.File]::ReadAllBytes($storageAssemblyPath)
[System.Reflection.Assembly]::Load($bytes)

Where $storageAssemblyPath is the file path of your assembly.

This is especially useful if you need to clean up the resources within your session. For example in a deployment script.