Eric Schoonover
Eric Schoonover

Reputation: 48402

Powershell Unload Module... completely

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

Answers (10)

Andreas Covidiot
Andreas Covidiot

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

UNdedss
UNdedss

Reputation: 158

I use a simple script that renames target DLL and loads it as module. Here we have 2 hacks:

  1. when module is loading from .net assembly object we got loaded module with name "dynamic_code_module_FirstPowershellModule"
  2. so before import we unload this module and create new one from renamed file

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

Kory Gill
Kory Gill

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

Jesse MacNett
Jesse MacNett

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

terry
terry

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

jpmc26
jpmc26

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

Sleeper Smith
Sleeper Smith

Reputation: 3242

Make a copy of the DLL and load that copy. You can Reload DLLs.

Upvotes: 1

Johann Strydom
Johann Strydom

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

Mischa
Mischa

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

STW
STW

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

Related Questions