Powershell Unload Module … completamente

Estoy trabajando en la depuración de un proyecto de Powershell. Estoy usando Import-Module para cargar el módulo PS desde mi C # dll y todo funciona bien. Llamar a Remove-Module no descarga completamente el módulo, ya que la DLL todavía está bloqueada y no se puede eliminar.

¿Hay alguna manera de hacer que PSH descargue completamente el módulo y liberar el DLL para que pueda copiarlo y volver a cargarlo usando Import-Module sin reiniciar la consola PSH?

Actualizar
Entonces, si carga un módulo en un AppDomain separado, ¿funciona igual que un módulo normal? ¿Alguien puede dar un ejemplo?

Hay una solución. Abra otra instancia de PowerShell:

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

Después de la salida, volverás a la instancia de PowerShell desde la que hiciste esta llamada (suponiendo que hayas realizado la llamada de PowerShell y una instancia de PowerShell). Puede pasar cualquiera de los argumentos normales a powershell , para que pueda usar -Command o -File. P.ej,

 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. 

Cuando se cierre PowerShell, liberará el locking en la DLL, lo que le permitirá seguir trabajando.

Todo esto se hizo desde la interfaz de línea de comandos de PowerShell. No he probado lo que sucede si lanzas powershell en medio de un script o si esto funciona dentro de ISE. (Sospecho que funciona dentro de ISE.) Incluso si no funciona dentro de un script, esto sigue siendo útil durante el desarrollo.

Editar:

Hice algunas comprobaciones. Así que esto parece funcionar bien desde los scripts y en ISE, pero hay una advertencia dentro de ISE. Desde ISE, no puede leer ninguna entrada del usuario mientras está dentro del proceso separado de PowerShell. Si lo intenta, el script o los comandos se detienen para esperar, pero no se muestra ningún cuadro de entrada como normal, y por supuesto, no puede escribir directamente en la ventana de salida en ISE. Por lo tanto, si necesita solicitar entrada en el medio de [do work] , solicítelo antes de encender una nueva instancia de PowerShell y páselo al trabajo como parámetro. Estos no son problemas en absoluto si está utilizando la línea de comandos de PowerShell regular.

No. Como PowerShell usa .NET debajo, tiene los mismos requisitos. No puede descargar un archivo DLL de un dominio de aplicación .NET sin descargar el propio dominio de aplicación. Como la IU de PowerShell vive en el mismo Dominio de aplicación, esto no es posible.

Veo algunas respuestas viables aquí, pero esta es mía, en caso de que esto siga siendo un problema para alguien (y esto es bastante flojo, lo cual es bueno).

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

Para abreviar, crear una PSSession para su computadora local crea una sesión de PowerShell diferente, que incluye lo que se considera “cargado”, y cuando sale, lo limpia por usted.

Creo que esto es válido para PowerShell: en el mundo de .NET, la única forma de descargar un ensamblaje es cargarlo en un AppDomain diferente; una vez que un ensamblaje se carga en un AppDomain , permanece cargado durante el tiempo de vida de ese AppDomain .


Aquí hay un ejemplo de un hilo que hace más o menos la misma pregunta y muestra algunas maneras de crear y cargar el módulo en un nuevo dominio de aplicación:

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

He tenido los mismos problemas y terminé envolviendo el archivo DLL que quería cargar dentro de un comando exe que llamé desde el script. De esa manera evité cargar la DLL dentro de mi aplicación.

En el contexto del desarrollo de Cmdlet, y teniendo problemas con la descarga de su DLL, hay dos enfoques que yo uso.

En primer lugar, desarrollo en Visual Studio y configuro un progtwig externo (PowerShell) para cargar mi Cmdlet. De esta forma, mi módulo se carga cuando comienzo la depuración y se descarga cuando dejo de depurar.

En segundo lugar, en las ocasiones en que sé que quiero cargar un módulo, hacer algún trabajo y asegurar que el módulo se descargue después, utilizo una segunda instancia de PowerShell. Esto se ha discutido en otras respuestas, y mi respuesta a continuación muestra cómo habilito este flujo de trabajo mediante el uso de una función con un alias en mi perfil. Cambio el mensaje para que pueda tener un recordatorio visual de que estoy en una “ventana recursiva de PowerShell”.

Cree una secuencia de comandos en su perfil para iniciar 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 

Edite la configuración de depuración para su proyecto Cmdlet

Comience el progtwig externo :

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

Argumentos de línea de comando :

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

Depure su Módulo

Ahora desde Visual Studio, inicie el depurador con F5 , y tiene una nueva ventana de PowerShell con su Cmdlet cargado, y puede depurarlo como desee.

Use el alias ‘sdp’ desde cualquier ventana de PowerShell

Dado que la función Start-DebugPowerShell está en nuestro perfil y le dimos un alias sdp , puede usar esto para iniciar una segunda instancia de PowerShell en cualquier momento que lo necesite.

Uso una secuencia de comandos simple que cambia el nombre de la DLL de destino y la carga como módulo. Aquí tenemos 2 hacks:

  1. cuando el módulo se carga desde el objeto de ensamblaje .net, recibimos el módulo cargado con el nombre “dynamic_code_module_FirstPowershellModule”
  2. entonces antes de la importación descargamos este módulo y creamos uno nuevo del archivo renombrado

las asambleas anteriores permanecen sin usarse en el dominio

script debe ejecutarse después de cada reconstrucción del proyecto

 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 

Haga una copia de la DLL y cargue esa copia. Usted puede volver a cargar las DLL.

Los módulos PS son ensamblados .NET, cuando Import-Module , los carga en AppDomain del host PowerShell (la aplicación). Remove-Module simplemente elimina módulos de la sesión actual.

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

No hay forma de descargar un ensamblaje individual sin descargar todos los dominios de aplicación que lo contienen. Utilice el método de Descarga de AppDomain para descargar los dominios de la aplicación. Para obtener más información, consulte Descargar un dominio de aplicación.

Puede iniciar un nuevo host de PowerShell en un nuevo AppDomain, importar su módulo al host y realizar el trabajo de PowerShell. El módulo es tan normal como se estaba ejecutando en su host anterior. La única diferencia es que está en un host que se ejecuta en un dominio de aplicación diferente.

Tenía un módulo externo ( ImportExcel ) que quería actualizar así y no funcionó . Para esta situación Install-Module -Scope CurrentUser (en caso de que esté en la carpeta del módulo de usuarios, de lo contrario omita el -Scope ... param) funcionó.

(Cómo lo manejaron internamente, no lo sé, pero mantienes tu sesión actual de PS).