无法使用Powershell将Bcrypt.Net-Next程序集安装到Windows 10上的GAC中



我需要在SSIS包的数据流中实现BCrypt哈希。我想通过将Bcrypt程序集部署到GAC,然后在脚本组件中调用它来实现这一点。我从Git下载了该项目,构建了强命名项目(我创建了一个密钥),并使用Powershell将程序集部署到GAC。我使用Powershell而不是gacutil.exe,因为我运行的是Windows 10,它没有gacutil.exe.

https://github.com/BcryptNet/bcrypt.net

# NOTE: Run powershell as administrator
[Reflection.Assembly]::LoadWithPartialName("System.EnterpriseServices") | Out-Null         
[System.EnterpriseServices.Internal.Publish] $publish = New-Object System.EnterpriseServices.Internal.Publish
# to install a dll
$publish.GacInstall("C:tempBcrypt.netBCrypt.Net-Next.dll")

我没有收到任何错误,事实上我没有得到任何输出——它只是转到下一行。但是程序集未安装在%windir%\Microsoft.NET\assembly

知道为什么不起作用吗?

注意:我是这样做的,而不是使用NuGet,因为我将在SSIS脚本组件中使用它。显然,NuGet不适用于SSIS解决方案。

补充您自己的答案:

我很惊讶[Reflection.Assembly]::LoadWithPartialName('System.EnterpriseServices')没有起作用——对我来说确实如此,但这是一个没有意义的问题,因为在PowerShell中,最好使用
Add-Type -AssemblyName
,即:

  • 在语法方面更具PowerShell惯用性,并且
  • 如果无法加载程序集,则报告(语句终止)错误(而[Reflection.Assembly]::LoadWithPartialName()在加载程序集失败的情况下是安静无操作)
# Try to load the latest System.EnterpriseServices.dll assembly
# from the GAC.
Add-Type -AssemblyName System.EnterpriseServices

[Reflection.Assembly]::LoadWithPartialName()一样,Add-Type -AssemblyName允许您通过其简单名称加载GAC程序集(也反映在DLL/不带扩展名的可执行文件名中),它既不需要知道程序集的版本号,也不需要知道其公钥(但是,Add-Type -AssemblyName不需要也首先在应用程序目录中查找,该目录在PowerShell中可能是PowerShell可执行文件本身的位置)。

请注意,[Reflection.Assembly]::LoadWithPartialName()已被正式宣布为过时,因为通过简单名称加载程序集可能会在以后破坏现有代码。原因是安装了不兼容的版本或具有重复简单名称的程序集。

但是,后期绑定脚本语言(如PowerShell)中,通过简单名称加载可能是可以接受的(Add-Type -AssemblyName不是过时的),并简化了加载(但是,可以指定程序集的明确全名,请参见下文)。

当然,如果[Reflection.Assembly]::LoadWithPartialName('System.EnterpriseServices')莫名其妙地对你不起作用,Add-Type -AssemblyName System.EnterpriseServices可能也会失败,但总的来说是正确的

[Reflection.Assembly]::LoadWithPartialName()的推荐替代品是[Reflection.Assembly]::Load(),这就是您最终使用的。它要求您知道程序集的全名,其中必须包括程序集的完整版本号及其公钥-尽管似乎可以接受比GAC中实际存在的版本号低的版本号。

注意Add-Type -AssemblyName也接受(强命名的)完整程序集名称

# 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'

后退一步:

我建议避免在GAC:中放入自定义程序集,原因有两个:

  • PowerShell[Core]6+最终将淘汰Windows PowerShell,它是在.NETCore上构建的,不再有GAC,因此在迁移时需要不同的方法。

    • 在PowerShell[Core]中,Add-Type -AssemblyName在PowerShell本身附带的程序集中查找由简单名称给定的程序集,尽管它首先在当前目录中查找
  • 您正在使用的System.EnterpriseServices.Internal.Publish类型不被官方支持直接使用:";Publish由.NET Framework内部使用。您不需要在代码中直接使用它&";,在GAC中安装程序集的唯一官方支持方式是通过Windows安装程序(gacutil.exe仅用于开发)。


相反,我建议采用以下方法

  • 编写一个辅助PowerShell模块,该模块封装感兴趣的程序集,例如命名为BCrypt

  • 将该模块放置在$env:PSModulePath中列出的一个目录中,以便任何执行Import-Module BCrypt的脚本都可以将感兴趣的程序集隐式加载到会话中。

这样的模块很容易编写:

  • $env:PSModulePath中选择一个合适的目录,并在其中创建一个名为BCrypt的子目录。

  • 将程序集DLL(BCrypt.Net-Next.dll)复制到该子目录中。

  • 更改到子目录并在其中创建模块清单BCrypt.psd1

    New-ModuleManifest BCrypt.psd1 -RequiredAssemblies BCrypt.Net-Next.dll -ModuleVersion 1.0
    

根据需要提供额外的New-ModuleManifest参数和/或根据需要在事后编辑模块清单。

我使用的Powershell脚本不正确。

我们必须使用Load而不是使用LoadWithPartialName

# 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:tempBcrypt.netBCrypt.Net-Next.StrongName.dll")
# to install a dll
$publisher.GacInstall("C:tempBcrypt.netBCrypt.Net-Next.StrongName.dll")

这将把组件安装到以下路径:

%windir%\Microsoft.NET\assembly\GAC_MSIL\BCrypt.NET Next.StrongName\v4.0_3.2.1.0__cb41ca561ed0708f\BCrypt.NET Next.StrongName.dll

注意:我使用的是Git上的最新版本,即3.2.1。因此,未来的版本将产生一个不同的子文件夹路径,如上所述(即v4.0_*)

这里的挑战是,我仍然没有在脚本组件的任何引用中看到可用的程序集。我会继续研究这个问题,如果需要的话,我会提出一个新的问题。

最新更新