Unable to install the Bcrypt.Net-Next assembly in to the GAC on Windows 10 using Powershell
To complement your own answer:
I'm surprised that [Reflection.Assembly]::LoadWithPartialName('System.EnterpriseServices')
didn't work - it does for me, but it's a moot point, because in PowerShell it's preferable to use
Add-Type -AssemblyName
, which:
- is more PowerShell-idiomatic in terms of its syntax, and
- reports a (statement-terminating) error if the assembly cannot be loaded (whereas
[Reflection.Assembly]::LoadWithPartialName()
is a quiet no-op in case of failure to load the assembly).
# Try to load the latest System.EnterpriseServices.dll assembly
# from the GAC.
Add-Type -AssemblyName System.EnterpriseServices
Like [Reflection.Assembly]::LoadWithPartialName()
, Add-Type -AssemblyName
allows you to load GAC assemblies by their simple name (as also reflected in the DLL/executable file name without extension), which neither requires you to know the assembly's version number nor its public key (however, Add-Type -AssemblyName
does not also first look in the application directory, which in PowerShell would presumably be the location of the PowerShell executable itself).
Note that [Reflection.Assembly]::LoadWithPartialName()
is officially declared obsolete, because loading assemblies by simple name can break existing code later, due to installation of incompatible versions or assemblies with duplicate simple names.
However, in a late-bound scripting language such as PowerShell, loading by simple name may be acceptable (Add-Type -AssemblyName
is not obsolete), and simplifies loading (you can specify an assembly's unambiguous full name, however - see below).
Of course, if [Reflection.Assembly]::LoadWithPartialName('System.EnterpriseServices')
inexplicably didn't work for you, Add-Type -AssemblyName System.EnterpriseServices
may similarly fail, but the general point stands.
The recommended replacement for [Reflection.Assembly]::LoadWithPartialName()
is [Reflection.Assembly]::Load()
, which is what you ended up using. It requires you to know the assembly's full name, which must include the assembly's full version number and its public key - although a lower version number than the one actually present in the GAC is seemingly accepted.
Note that Add-Type -AssemblyName
accepts (strong-named) full assembly names too:
# Load from the GAC by *full assembly name*.
# Equivalent of [Reflection.Assembly]::Load()
Add-Type -AssemblyName 'System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
Taking a step back:
I suggest avoiding putting a custom assembly in the GAC:, for two reasons:
PowerShell [Core] 6+, which will eventually make Windows PowerShell obsolete, is built on .NET Core, which doesn't have a GAC anymore, so you'll need a different approach when you migrate.
- In PowerShell [Core],
Add-Type -AssemblyName
looks for assemblies given by simple name among the assemblies that ship with PowerShell itself, though it looks in the current directory first.
- In PowerShell [Core],
The
System.EnterpriseServices.Internal.Publish
type you're using isn't officially supported for direct use: "Publish
is used internally by the .NET Framework. You do not need to use it directly in your code.", and the only officially supported way to install assemblies in the GAC is via Windows Installer (gacutil.exe
is only meant to be used during development).
Instead, I recommend the following approach:
Author an auxiliary PowerShell module that wraps the assembly of interest, named, say,
BCrypt
.Place that module in one of the directories listed in
$env:PSModulePath
, so that any script executingImport-Module BCrypt
implicitly loads the assembly of interest into the session.
Such a module is easy to author:
Choose a suitable directory listed in
$env:PSModulePath
and create a subdirectory named, say,BCrypt
in it.Copy your assembly DLL (
BCrypt.Net-Next.dll
) into that subdirectory.Change to the subdirectory and create a module manifest
BCrypt.psd1
there:New-ModuleManifest BCrypt.psd1 -RequiredAssemblies BCrypt.Net-Next.dll -ModuleVersion 1.0
Provide additional New-ModuleManifest
arguments as needed and/or edit the module manifest after the fact, as needed.
The Powershell script I was using is incorrect.
Instead of using LoadWithPartialName
, we must use Load
# NOTE: Run powershell as administrator
[System.Reflection.Assembly]::Load("System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
$publisher = New-Object System.EnterpriseServices.Internal.Publish
# to uninstall a dll
$publisher.GacRemove("C:\temp\Bcrypt.net\BCrypt.Net-Next.StrongName.dll")
# to install a dll
$publisher.GacInstall("C:\temp\Bcrypt.net\BCrypt.Net-Next.StrongName.dll")
This will install the assembly into the following path:
%windir%\Microsoft.NET\assembly\GAC_MSIL\BCrypt.Net-Next.StrongName\v4.0_3.2.1.0__cb41ca561ed0708f\BCrypt.Net-Next.StrongName.dll
Note: I am using the most recent version available on Git, which is 3.2.1. So, future releases will result in a different sub-folder path as seen above (i.e., v4.0_*).
The challenge here is that I still do not see the assembly available in any of the script component's references. I will continue to research this and post a new question if needed.