Powershell卸载模块......完全

问题描述 投票:32回答:10

我正在调试一个Powershell项目。我正在使用Import-Module从我的C#dll加载PS模块,一切正常。虽然DLL仍然被锁定且无法删除,但调用Remove-Module并未完全卸载模块。

有没有办法让PSH完全卸载模块并释放DLL,以便我可以复制它并使用Import-Module重新加载它而无需重新启动PSH控制台?

更新 因此,如果您将模块加载到单独的AppDomain中,它仍然像普通模块一样工作吗?谁能提供一个例子?

.net powershell
10个回答
23
投票

有一个解决方法。打开另一个PowerShell实例:

PS > powershell
PS > [load DLL]
PS > [do work]
PS > exit

在退出之后,您将被带回到PowerShell的实例,您从中进行了此调用(假设您在内部进行了powershell调用和PowerShell实例)。您可以将任何常规参数传递给powershell,因此您可以使用-Co​​mmand或-File。例如。,

PS > powershell -Command '[load DLL]; [do work]' # Executes a command and exits
PS > powershell -Command '.\myscript.ps1 param1 param2' # Executes the script and exits
PS > powershell -File .\myscript.ps1 param1 param2 # Executes a script and exits.

当PowerShell退出时,它将释放DLL上的锁定,允许您继续工作。

所有这些都是通过PowerShell命令行界面完成的。我没有测试如果你在脚本中间扔powershell或者如果这在ISE中工作会发生什么。 (我怀疑它在ISE中有效。)即使if在脚本中不起作用,这在开发过程中仍然有用。

编辑:

做了一些检查。所以这似乎在脚本和ISE中都可以正常工作,但在ISE中有一个警告。在ISE中,当您在单独的PowerShell进程中时,无法读取用户的任何输入。如果您尝试,脚本或命令停止等待,但没有输入框像正常一样显示,当然,您不能直接键入ISE的输出窗口。因此,如果您需要在[do work]中间提示输入,请在启动PowerShell的新实例之前进行提示,并将其作为参数传递给工作。如果您使用常规PowerShell命令行,这些都不是问题。


0
投票

我有一些外部模块(ImportExcel)我想像这样更新它和did not work。对于这种情况,Install-Module -Scope CurrentUser <MyExternalModuleName>(如果它在你的用户模块文件夹中,否则省略-Scope ... param)工作。

(他们如何在内部管理它 - 我不知道,但你保留了当前的PS会话。)


17
投票

不会。由于PowerShell在其下使用.NET,因此具有相同的要求。在不卸载AppDomain本身的情况下,无法从.NET AppDomain卸载dll。由于PowerShell UI位于同一AppDomain中,因此无法实现。


8
投票

我在这里看到一些可行的答案,但这是我的,以防这对某人来说仍然是一个问题(而且这很懒,这很好)。

Enter-PSSession -localcomputername
[load dlls]
[execute script(s)]
Exit-PSSession

简而言之,为本地计算机创建PSSession会创建一个不同的PowerShell会话,包括被认为是“已加载”的会话,当您退出时,它会为您清理。


5
投票

我相信这适用于PowerShell:在.NET世界中卸载程序集的唯一方法是将其加载到不同的AppDomain中;一旦装配装载到AppDomain,它将在AppDomain的整个生命周期内保持装载状态。


这是一个来自一个线程的例子,它提出了几乎相同的问题并展示了一些创建和加载模块到新AppDomain的方法:

http://www.eggheadcafe.com/conversation.aspx?messageid=30789124&threadid=30766269


3
投票

我有同样的问题,最终包装我想加载到命令行exe中的DLL,然后我从脚本调用。通过这种方式,我完全避免在我的应用程序中加载DLL。


3
投票

在Cmdlet开发的上下文中,并且在卸载DLL时遇到问题,我使用了两种方法。

首先,我在Visual Studio中开发,并设置外部程序(PowerShell)来加载我的Cmdlet。这样,我的模块在开始调试时加载,在我停止调试时卸载。

其次,在我知道我想要加载模块的那些场合,做一些工作,并确保之后卸载模块,我使用PowerShell的第二个实例。这在其他答案中已经讨论过了,下面的答案显示了如何通过在我的个人资料中使用带别名的功能来启用此工作流程。我更改了提示,因此我可以通过视觉提醒我处于“递归PowerShell窗口”中。

在配置文件中创建脚本以启动PowerShell

function Start-DebugPowerShell
{
    PowerShell -NoProfile -NoExit -Command {
        function prompt {
            $newPrompt = "$pwd.Path [DEBUG]"
            Write-Host -NoNewline -ForegroundColor Yellow $newPrompt
            return '> '
        }
    }
}
Set-Alias -Name sdp -Value Start-DebugPowerShell

编辑Cmdlet项目的调试设置

启动外部程序:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

命令行参数:

-NoProfile -NoExit -Command "Import-Module .\MyCoolCmdlet.dll"

调试您的模块

现在从Visual Studio,使用F5启动调试器,并且您已经加载了Cmdlet的新PowerShell窗口,您可以随意调试它。

使用任何PowerShell窗口中的“sdp”别名

由于Start-DebugPowerShell函数在我们的配置文件中,并且我们给它一个别名sdp,您可以使用它在任何需要的时候启动PowerShell的第二个实例。


2
投票

我使用一个简单的脚本重命名目标DLL并将其作为模块加载。这里我们有2个黑客:

  1. 当模块从.net程序集对象加载时,我们得到了名为“dynamic_code_module_FirstPowershellModule”的加载模块
  2. 所以在导入之前我们卸载这个模块并从重命名的文件中创建一个新模块

以前的程序集在域中保持未使用

必须在每个项目重建后运行脚本

Get-Module -Name "*FirstPowershellModule*" | Remove-Module
$ii++
$destPath = "D:\Dev\FirstPowershellModule\FirstPowershellModule\bin\Debug\FirstPowershellModule" + $ii+ ".dll"
Copy-Item D:\Dev\FirstPowershellModule\FirstPowershellModule\bin\Debug\FirstPowershellModule.dll -Destination $destPath
$ass = [System.Reflection.Assembly]::LoadFile($destPath)
import-module -Assembly $ass

0
投票

制作DLL的副本并加载该副本。您可以重新加载DLL。


0
投票

PS模块是.net程序集,当您使用Import-Module时,将它们加载到PowerShell主机(应用程序)的AppDomain中。 Remove-Module只是从当前会话中删除模块。

根据msdn,http://msdn.microsoft.com/en-us/library/ms173101(v=vs.80).aspx

如果不卸载包含它的所有应用程序域,就无法卸载单个程序集。使用AppDomain中的Unload方法卸载应用程序域。有关更多信息,请参阅卸载应用程序域。

您可以在新的AppDomain中启动新的PowerShell主机,将模块导入主机并执行PowerShell作业。该模块与在先前主机中运行的模块一样正常。唯一的区别是它位于运行在不同AppDomain中的主机中。

© www.soinside.com 2019 - 2024. All rights reserved.