Reputation: 48402
I'm working on debugging a Powershell project. I'm using Import-Module
to load the PS module from my C# dll and everything works fine. Calling Remove-Module
does not fully unload the module though as the DLL is still locked and can not be deleted.
Is there a way to get PSH to fully unload the module and release the DLL so that I can copy over it and reload it again using Import-Module
without restarting the PSH console?
Update
So if you load a module into a seperate AppDomain does it still work like a normal module? Can anyone provide an example?
Upvotes: 53
Views: 57151
Reputation: 4765
I had some external module (ImportExcel) I wanted to update like this and it did not work. For this situation Install-Module -Scope CurrentUser <MyExternalModuleName>
(in case it is in your users module folder, otherwise omit the -Scope ...
param) worked.
(How they managed it internally - I don't know, but you keep your current PS session.)
Upvotes: 0
Reputation: 158
I use a simple script that renames target DLL and loads it as module. Here we have 2 hacks:
previous assemblies stay unused in domain
script must be run after each project rebuild
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
Upvotes: 3
Reputation: 7163
In the context of Cmdlet development, and having issues with unloading your DLL, there are two approaches that I use.
Firstly, I develop in Visual Studio, and setup an external program (PowerShell) to load my Cmdlet. This way, my module loads when I start debugging, and unloads when I stop debugging.
Second, on those occasions where I know I want to load a module, do some work, and ensure the module is unloaded afterwards, I use a second instance of PowerShell. This has been discussed in other answers, and my answer below shows how I enable this workflow by using a function with an alias in my Profile. I change the prompt so I can have a visual reminder that I am in a "recursive PowerShell window".
Create a script in your profile to start 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
Edit debug settings for your Cmdlet project
Start external program:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Command line arguments:
-NoProfile -NoExit -Command "Import-Module .\MyCoolCmdlet.dll"
Debug your Module
Now from Visual Studio, start debugger with F5, and you have a new PowerShell window with your Cmdlet loaded, and you can debug it however you like.
Use the 'sdp' alias from any PowerShell window
Since the Start-DebugPowerShell function is in our profile and we gave it an alias sdp
, you can use this to start a second instance of PowerShell anytime you need it.
Upvotes: 8
Reputation: 459
I see a few workable answers here, but here's mine, in case this is still a problem for someone (and this is pretty lazy, which is nice).
Enter-PSSession -localcomputername
[load dlls]
[execute script(s)]
Exit-PSSession
Long story short, creating a PSSession for your local computer creates a different powershell session, including what's considered "loaded", and when you exit, it cleans things up for you.
Upvotes: 12
Reputation: 1587
The PS modules are .net assemblies, when you Import-Module
, you load them into the AppDomain of the PowerShell Host(the application). Remove-Module
just removes modules from the current session.
According to msdn, http://msdn.microsoft.com/en-us/library/ms173101(v=vs.80).aspx
There is no way to unload an individual assembly without unloading all of the application domains that contain it. Use the Unload method from AppDomain to unload the application domains. For more information, see Unloading an Application Domain.
You could start a new PowerShell host in a new AppDomain, import your module to the host and do the PowerShell job. The module is as normal as it was running in your previous host. The only difference is that it is in a host running in a different AppDomain.
Upvotes: 0
Reputation: 29934
There is a workaround. Open up another instance of PowerShell:
PS > powershell
PS > [load DLL]
PS > [do work]
PS > exit
After the exit, you'll be brought back to the instance of PowerShell from which you made this call (assuming you made the powershell
call inside and instance of PowerShell). You can pass in any of the normal arguments to powershell
, so you can use -Command or -File. E.g.,
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.
When PowerShell exits, it will release the lock on the DLL, allowing you to continue working.
All of this was done from the PowerShell command line interface. I haven't tested what happens if you throw powershell
in the middle of a script or if this works within ISE. (I suspect it works within ISE.) Even if if doesn't work inside a script, this is still useful during development.
Edit:
Did some checking. So this seems to work fine from within scripts and in ISE, but there's a caveat within ISE. From ISE, you can't read any input from the user while you're inside the separate PowerShell process. If you try, the script or commands stop to wait, but no input box is shown like normal, and of course, you can't type directly into the output window in ISE. So if you need to prompt for input in the middle of [do work]
, prompt before firing up a new instance of PowerShell, and pass it into the work as a parameter. These aren't problems at all if you're using the regular PowerShell command line.
Upvotes: 36
Reputation: 3242
Make a copy of the DLL and load that copy. You can Reload DLLs.
Upvotes: 1
Reputation: 1492
I've had the same issues and ended up wrapping the DLL I wanted to load inside a commandline exe which I then called from the script. In that way I avoided loading the DLL inside my application at all.
Upvotes: 3
Reputation: 901
No. As PowerShell uses .NET underneath it has the same requirements. You cannot unload a dll from a .NET AppDomain without unloading the AppDomain itself. As the PowerShell UI lives in the same AppDomain this is not possible.
Upvotes: 18
Reputation: 46386
I believe this holds true for PowerShell: in the .NET world the only way to unload an assembly is to load it into a different AppDomain
; once an assembly is loaded to an AppDomain
it remains loaded for the lifetime of that AppDomain
.
Here's an example from a thread asking pretty much the same question and showing a couple ways to create and load the module into a new AppDomain:
http://www.eggheadcafe.com/conversation.aspx?messageid=30789124&threadid=30766269
Upvotes: 5