2009-08-26 18 views
24

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 al Remove-Module no descarga completamente el módulo, ya que el archivo DLL aún está bloqueado y no se puede eliminar.Powershell Unload Module ... completamente

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

actualización
Así que si se carga un módulo en un dominio de aplicación independiente ¿todavía funciona como un módulo normal? ¿Alguien puede dar un ejemplo?

+0

He estado explorando esto también - Quiero distribuir un paquete nuget como un complemento de Powershell, y para permitir que el paquete se actualice, necesita poder eliminar la versión anterior del paquete (que contiene el dlls de soporte). Creo que envolver las funciones en un ejecutable de línea de comandos probablemente sea la mejor opción. :( –

Respuesta

5

Creo que esto es válido para PowerShell: en el mundo .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 la vida útil de ese AppDomain.


He aquí un ejemplo de un hilo preguntando más o menos la misma pregunta y que muestra un par de 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

+0

Esperaba que este fuera el caso, sin embargo, no creo que pueda cargar una interacción con los módulos de PSH de esta manera. Por lo tanto, la respuesta real es probable que lo que intento lograr no sea posible. –

+0

Es posible cargue y descargue las DLL y reemplace la DLL usando la función de copia oculta AppDomain. http://www.codeproject.com/Articles/633140/MEF-and-AppDomain-Remove-Assemblies-On-The-Fly –

16

No. Como PowerShell utiliza. 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.

3

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.

+0

tristemente, creo que es lo que también puedo terminar haciendo –

20

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

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

Después de la salida, se le trajo de vuelta a la instancia de PowerShell desde la que hizo esta llamada (suponiendo que haya hecho la llamada powershell dentro como instancia de PowerShell). Puede pasar cualquiera de los argumentos normales al powershell, para que pueda usar -Command o -File. Por ejemplo,

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 sale de PowerShell, que dará a conocer la cerradura de la DLL, lo que le permite seguir trabajando.

Todo esto se realizó desde la interfaz de línea de comandos de PowerShell. No he probado lo que sucede si arroja powershell en el 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 medio de [do work], solicite antes de encender una nueva instancia de PowerShell y pasarla al trabajo como parámetro. Estos no son problemas en absoluto si está utilizando la línea de comandos de PowerShell regular.

+0

Unos años tarde para la fiesta, pero una solución similar es hacer el trabajo en un [trabajo de fondo] (http://technet.microsoft.com/library/hh847783.aspx) Los trabajos en segundo plano de Powershell se ejecutan como procesos separados. Todavía no se puede leer la entrada del usuario, pero es un poco más flexible que generar una nueva instancia de Powershell. – JamesQMurphy

+0

@JamesQMurphy Los trabajos en segundo plano parecen diseñados para operaciones asincrónicas.Si eso es lo que necesita de todos modos, parece una excelente opción, pero si no lo hace, se siente una gran complejidad cuando puede ejecutar el proceso en línea. ¿Hay otros beneficios que no estoy viendo? – jpmc26

+3

Dos beneficios en los que puedo pensar: Primero, pasa un bloque de scripts a un trabajo, lo que le permite mantener el script en un archivo fuente. Y segundo, puede pasar objetos (serializables) como parámetros al trabajo, en lugar de solo los parámetros de texto. Es una solución similar a la suya, y es por eso que no la publiqué como una respuesta separada. – JamesQMurphy

0

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

0

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

Según MSDN, http://msdn.microsoft.com/en-us/library/ms173101(v=vs.80).aspx

No hay manera de descargar un montaje 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 Dominio de aplicación, 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.

6

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 resumir, la creación de una PSSession para su computadora local crea una sesión de PowerShell diferentes, incluyendo lo que se considera "cargado", y que cuando la salida, que limpia las cosas para usted.

+0

Gracias, amigo. Funciona :) – Oleksii

2

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 programa 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".

Crear 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 

Editar configuración de depuración para su proyecto de cmdlet

programa externo de inicio:

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

argumentos de línea de comandos:

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

depurar el módulo

Ahora desde Visual Studio, iniciar depurador con F5, y usted tiene una nueva ventana de PowerShell con su carga de cmdlet, y puedes depurarlo como desees.

utiliza el alias 'SDP' desde cualquier ventana de PowerShell

Dado que la función de puesta en DebugPowerShell está en nuestro perfil y que nos dio un alias sdp, se puede usar esto para iniciar una segunda instancia de PowerShell en cualquier momento lo necesita.

0

Utilizo 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 está cargando desde el objeto ensamblado de .NET conseguimos módulo con el nombre "dynamic_code_module_FirstPowershellModule" cargado
  2. así que antes de la importación descargamos este módulo y crear uno nuevo desde archivo renombrado

asambleas anteriores se quedan sin utilizar en dominio

script se debe ejecutar después de cada proyecto reconstrucción

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