【问题标题】:How to install a nuget package such as it can be loaded from Powershell如何安装可以从 Powershell 加载的 nuget 包
【发布时间】:2021-07-29 03:26:45
【问题描述】:

我有一个使用 nuget 包 Vanara.PInvoke.Shell32 的 c# 源代码。正如预期的那样,当我尝试在 Powershell 中使用 Add-Type 使用此源时,但它在“使用 Vanara.Pinvoke”语句中阻塞

我尝试使用“Install-Package Vanara.PInvoke.Shell32”,但安装失败

如何使这个模块在 Powershell 核心中可用?

【问题讨论】:

  • 请将 EXACT、COMPLETE 错误信息添加到您的问题中......并将其包装在代码格式标记中,以便所有人都能看到和轻松阅读。
  • 好的,简而言之:1)将dll复制到一个文件夹中。 2) 运行 Add-Type -Path folder_path ?
  • 我试过了。但是在尝试使用 Add-Type 加载我的 c# 代码时我仍然遇到问题。它仍然卡在“import Vanara.PInvoke”
  • 如果我明确列出所有程序集,它就可以工作: Add-Type -TypeDefinition $src -ReferencedAssemblies .\bin\Release\net6.0\publish\Vanara.PInvoke.Shell32.dll,"System. Windows", ... 对我来说有点太复杂了

标签: powershell nuget


【解决方案1】:

听起来您已经下载了Vanara.PInvoke.Shell32 NuGet package 并且知道包含感兴趣的程序集的.dll 文件的完整路径:

  • This answer 展示了如何下载 NuGet 包及其所有依赖项以在 PowerShell 中使用(请注意,Install-Package 虽然原则上能够下载 NuGet 包,但不会自动打包目标包所依赖的);该技术也用于下面的演示代码中。

使用Vanara.PInvoke.*.dll 程序集来自 PowerShell 代码 - 通过将它们加载到带有 Add-Type -LiteralPath 的会话中,然后进行诸如 [Vanara.PInvoke.User32]::GetForegroundWindow() 之类的调用 - 似乎工作无需额外的努力。

但是,您的用例需要使用传递给Add-Type-TypeDefinition 参数的临时编译的C# 源代码 中的程序集,并且,当您已经发现,这需要更多的努力,而不仅仅是将Vanara.PInvoke.*.dll 文件的路径传递给-ReferencedAssemblies 参数,至少从PowerShell 7.1 开始:

  • 莫名其妙地,为了以后的 Add-Type -TypeDefinition 调用成功,NuGet 包中的程序集必须首先通过它们的完整路径显式加载到带有 Add-Type -LiteralPath 的会话中 - 这闻起来很臭像一个错误。

  • 如果程序集是 .NET Standard DLL,就像手头的情况一样,您还必须在调用 Add-Type -TypeDefinition 时将 netstandard 程序集传递给 -ReferencedAssemblies

  • 对于要在两个 PowerShell 版本中运行的代码,.NET SDK 项目(参见下面的代码)应以--framework netstandard2.0 为目标。

  • 默认情况下,PowerShell 会话本身中默认可用的所有程序集(及其类型)也可以在传递给-TypeDefinition 的 C# 源代码中引用:

    • Windows PowerShell 中,任何传递给 -ReferencedAssemblies 的程序集都会添加到隐式可用的类型中。
    • 相比之下,在 PowerShell (Core) 7+ 中,使用-ReferencedAssemblies 排除通常隐式可用的程序集,因此必须传递所有必需的程序集明确地(例如,System.Console 以便使用 Console.WriteLine())。

演示

以下是一个独立的、易于定制的示例,其中包含详细的 cmets,可在 Windows PowerShellPowerShell (Core) 7+ 中运行,并执行以下操作:

  • 按需下载给定的 NuGet 包。
  • 创建一个辅助。 NET SDK 项目,它引用包并发布项目,以便相关程序集 (*.dll) 随时可用。
  • 首先直接从 PowerShell 使用包的程序集,然后通过临时编译的 C# 代码(传递给 Add-Type -TypeDefinition)。

注意:

  • 必须安装.NET SDK

  • 忽略损坏的语法突出显示。

$ErrorActionPreference = 'Stop'; Set-StrictMode -Off

# -- BEGIN: CUSTOMIZE THIS PART.
  # Name of the NuGet package to download.
  $pkgName = 'Vanara.PInvoke.Shell32'

  # If the package assemblies are .NET Standard assemblies, the 'netstandard'
  # assembly must also be referenced - comment out this statement if not needed.
  # Note: .NET Standards are versioned, but seemingly just specifying 'netstandard'
  #       is enough, in both PowerShell editions. If needed, specify the fully qualified,
  #       version-appropriate assembly name explicit; e.g., for .NET Standard 2.0:
  #          'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
  #       In *PowerShell (Core) 7+* only, a shortened version such as 'netstandard, Version=2.0' works too.
  $netStandardAssemblyName = 'netstandard'

  # The target .NET framework to compile the helper .NET SDK project for.
  # Targeting a .NET Standard makes the code work in both .NET Framework and .NET (Core).  
  # If you uncomment this statement, the SDK's default is used, which is 'net5.0' as of this writing.
  $targetFrameworkArgs = '--framework', 'netstandard2.0'

  # Test command that uses the package from PowerShell.
  $testCmdFromPs = { [Vanara.PInvoke.User32]::GetForegroundWindow().DangerousGetHandle() }

  # C# source that uses the package, to be compiled ad-hoc.
  # Note: Modify only the designated locations.
  $csharpSourceCode = @'
    using System;
    // == Specify your `using`'s here.
    using Vanara.PInvoke;
    namespace demo {
      public static class Foo {
        // == Modify only this method; make sure it returns something, ideally the same thing as
        //    PowerShell test command.
        public static IntPtr Bar() { 
          return User32.GetForegroundWindow().DangerousGetHandle();
        }
      }
    }
'@

# -- END of customized part.

# Make sure the .NET SDK is installed.
$null = Get-command dotnet

# Helper function for invoking external programs.
function iu { $exe, $exeArgs = $args; & $exe $exeArgs; if ($LASTEXITCODE) { Throw "'$args' failed with exit code $LASTEXIDCODE." } }


# Create a 'NuGetFromPowerShellDemo' subdirectory in the TEMP directory and change to it.
Push-Location ($tmpDir = New-Item -Force -Type Directory ([IO.Path]::GetTempPath() + "/NuGetFromPowerShellDemo"))

try {
  
  # Create an aux. class-lib project that downloads the NuGet package of interest.
  if (Test-Path "bin\release\*\publish\$pkgName.dll") {
    Write-Verbose -vb "Reusing previously created aux. .NET SDK project for package '$pkgName'"
  }
  else {
    Write-Verbose -vb "Creating aux. .NET SDK project to download and unpack NuGet package '$pkgName'..."
    iu dotnet new classlib --force @targetFrameworkArgs >$null
    iu dotnet add package $pkgName >$null
    iu dotnet publish -c release >$null
  }

  # Determine the full paths of all the assemblies that were published (excluding the helper-project assembly).
  [array] $pkgAssemblyPaths = (Get-ChildItem bin\release\*\publish\*.dll -Exclude "$(Split-Path -Leaf $PWD).dll").FullName

  # Load the package assemblies into the session.
  # !! THIS IS NECESSARY EVEN IF YOU ONLY WANT TO REFERENCE THE PACKAGE
  # !! ALL YOU WANT DO TO IS TO USE THE PACKAGE TO AD HOC-COMPILE C# SOURCE CODE.
  # Write-Verbose -vb "Loading assembly file paths, from $($pkgAssemblyPaths[0] | Split-Path):`n$(($pkgAssemblyPaths | Split-Path -Leaf) -join "`n")"
  Add-Type -LiteralPath $pkgAssemblyPaths

  # Write-Verbose -vb 'Performing a test call FROM POWERSHELL...'
  & $testCmdFromPs

  # Determine the assemblies to pass to Add-Type -ReferencedAssemblies.
  # The NuGet package's assemblies.
  $requiredAssemblies = $pkgAssemblyPaths
  # Additionally, the approriate .NET Standard assembly may need to be referenced.
  if ($netStandardAssemblyName) { $requiredAssemblies += $netStandardAssemblyName }
  # Note: In *PowerShell (Core) 7+*, using -ReferencedAssemblies implicitly
  #       excludes the assemblies that are otherwise available by default, so you
  #       may have to specify additional assemblies, such as 'System.Console'.
  #       Caveat: In .NET (Core), types are often forwarded to other assemblies,
  #               in which case you must use the forwarded-to assembly; e.g.
  #               'System.Drawing.Primitives' rather than just 'System.Drawing' in
  #               order to use type System.Drawing.Point.
  #               What mitigates the problem is that failing to do so results in a 
  #               an error message that mentions the required, forwarded-to assembly.
  # E.g.:
  #  if ($IsCoreCLR) { $requiredAssemblies += 'System.Console' }

  Write-Verbose -vb 'Ad-hoc compiling C# CODE that uses the package assemblies...'
  Add-Type -ReferencedAssemblies $requiredAssemblies -TypeDefinition $csharpSourceCode
  
  Write-Verbose -vb 'Performing a test call FROM AD HOC-COMPILED C# CODE...'
  [demo.Foo]::Bar()

} 
finally {
  Pop-Location
  Write-Verbose -vb "To clean up the temp. dir, exit this session and run the following in a new session:`n`n  Remove-Item -LiteralPath '$tmpDir' -Recurse -Force"
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-22
    • 2018-12-26
    • 2017-09-15
    • 1970-01-01
    • 2016-04-18
    • 2022-11-02
    • 1970-01-01
    相关资源
    最近更新 更多