2009-08-26 12 views
24

Sto lavorando al debug di un progetto PowerShell. Sto usando Import-Module per caricare il modulo PS dal mio C# dll e tutto funziona bene. Chiamare Remove-Module non scarica completamente il modulo anche se la DLL è ancora bloccata e non può essere cancellata.Powershell Unload Module ... completamente

C'è un modo per ottenere PSH per scaricare completamente il modulo e rilasciare la DLL in modo che sia possibile copiarlo e ricaricarlo di nuovo utilizzando Import-Module senza riavviare la console PSH?

Aggiornamento
Quindi, se si carica un modulo in un AppDomain separato Ha ancora lavorare come un modulo normale? Qualcuno può fornire un esempio?

+0

Ho esplorato anche questo - voglio distribuire un pacchetto di nuget come plug-in di PowerShell e, per consentire l'aggiornamento del pacchetto, deve essere in grado di eliminare la vecchia versione del pacchetto (che contiene il supporto dll). Penso che avvolgere le funzioni in una riga di comando exe sarà probabilmente la migliore scommessa. :( –

risposta

5

Credo che questo sia vero per PowerShell: nel mondo .NET l'unico modo per scaricare un assembly è caricarlo in un diverso AppDomain; una volta caricato un assieme su un AppDomain, rimane caricato per la durata di tale AppDomain.


Ecco un esempio da un thread che chiede più o meno la stessa domanda e mostrando un paio di modi per creare e caricare il modulo in un nuovo AppDomain:

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

+0

Mi aspettavo che questo potesse essere il caso, tuttavia non penso che tu possa caricare un'interazione con i moduli PSH in questo modo, quindi la vera risposta è probabile che ciò che sto cercando di ottenere non sia possibile –

+0

E 'possibile caricare e scaricare DLL e sostituire la DLL utilizzando la funzionalità di copia shadow AppDomain http://www.codeproject.com/Articles/633140/MEF-and-AppDomain-Remove-Assemblies-On-The-Fly –

16

No. Come PowerShell utilizza. NET sotto di esso ha gli stessi requisiti. Non è possibile scaricare una DLL da un AppDomain .NET senza scaricare AppDomain stesso. Poiché l'interfaccia utente di PowerShell vive nello stesso AppDomain, ciò non è possibile.

3

Ho avuto gli stessi problemi e ho finito con il wrapping della DLL che volevo caricare all'interno di un exe della linea di comando che poi ho chiamato dallo script. In questo modo ho evitato di caricare la DLL all'interno della mia applicazione.

+0

purtroppo, penso che è quello che potrei fare anche io –

20

C'è una soluzione alternativa. Aprire un'altra istanza di PowerShell:

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

Dopo l'uscita, verrà portato indietro all'istanza di PowerShell da cui hai fatto questa chiamata (supponendo che hai fatto la powershell chiamata dentro e istanza di PowerShell). È possibile passare uno qualsiasi degli argomenti normali a powershell, quindi è possibile utilizzare -Command o -File. Per esempio,

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. 

Quando PowerShell esce, che rilascerà il blocco sul DLL, che consente di continuare a lavorare.

Tutto ciò è stato eseguito dall'interfaccia della riga di comando di PowerShell. Non ho provato cosa succede se passi powershell nel mezzo di uno script o se questo funziona all'interno di ISE. (Sospetto che funzioni all'interno di ISE.) Anche se non funziona all'interno di uno script, questo è ancora utile durante lo sviluppo.

Modifica:

Effettuato un controllo. Quindi questo sembra funzionare bene all'interno degli script e in ISE, ma c'è un avvertimento all'interno di ISE. Da ISE, non è possibile leggere alcun input dall'utente mentre si è all'interno del processo PowerShell separato. Se ci provi, lo script oi comandi si fermano per aspettare, ma nessuna casella di input viene mostrata come normale, e, naturalmente, non puoi digitare direttamente nella finestra di output in ISE.Pertanto, se è necessario richiedere l'immissione nel mezzo di [do work], richiedere prima del attivare una nuova istanza di PowerShell e passarla nel lavoro come parametro. Questi non sono affatto problemi se si utilizza la normale riga di comando di PowerShell.

+0

Alcuni anni dopo la festa, ma una soluzione alternativa è fare il lavoro in un [background job] (http://technet.microsoft.com/library/hh847783.aspx) I lavori in background di PowerShell vengono eseguiti come processi separati.Non è ancora possibile leggere l'input dell'utente, ma è un po 'più flessibile rispetto alla generazione di una nuova istanza di PowerShell: – JamesQMurphy

+0

@JamesQMurphy I processi in background sembrano progettati per operazioni asincrone.Se è quello di cui hai bisogno in ogni caso, sembra un'ottima opzione, ma se non lo fai, ti sembra molto più complessa quando puoi semplicemente eseguire il processo in linea. Ci sono altri benefici che non vedo? – jpmc26

+3

Due vantaggi a cui posso pensare: in primo luogo, si passa un blocco di script a un lavoro, che consente di mantenere lo script in un unico file di origine. E in secondo luogo, è possibile passare oggetti (serializzabili) come parametri al lavoro, anziché solo parametri di testo. È una soluzione simile alla tua, motivo per cui non l'ho postato come risposta separata. – JamesQMurphy

0

Creare una copia della DLL e caricare quella copia. È possibile ricaricare le DLL.

0

I moduli PS sono .net assembly, quando si Import-Module, li si carica nell'AppDomain del PowerShell Host (l'applicazione). Remove-Module rimuove solo i moduli dalla sessione corrente.

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

Non v'è alcun modo per scaricare un singolo assembly senza scaricare tutti i campi di applicazione che lo contengono. Utilizzare il metodo Unload da AppDomain per scaricare i domini dell'applicazione. Per ulteriori informazioni, consultare Scaricamento di un dominio dell'applicazione.

È possibile avviare un nuovo host PowerShell in un nuovo AppDomain, importare il modulo sull'host ed eseguire il lavoro di PowerShell. Il modulo è normale come funzionava nel tuo precedente host. L'unica differenza è che si trova in un host in esecuzione in un diverso AppDomain.

6

Vedo qui alcune risposte praticabili, ma questa è la mia, nel caso questo sia ancora un problema per qualcuno (e questo è piuttosto pigro, che è bello).

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

Per farla breve, la creazione di una sessione PSSession per il computer locale crea una sessione di PowerShell diversa, tra ciò che è considerato "caricato", e quando si esce, si pulisce le cose per voi.

+0

Grazie, amico. Funziona :) – Oleksii

2

Nel contesto dello sviluppo di cmdlet e problemi di scaricamento della DLL, esistono due approcci che utilizzo.

In primo luogo, sviluppo in Visual Studio e programma un programma esterno (PowerShell) per caricare il cmdlet. In questo modo, il mio modulo si carica quando avvio il debug e scarica quando interrompo il debug.

In secondo luogo, in quelle occasioni in cui so che voglio caricare un modulo, fare un po 'di lavoro e assicurarmi che il modulo venga scaricato successivamente, io uso una seconda istanza di PowerShell. Questo è stato discusso in altre risposte, e la mia risposta qui sotto mostra come abilito questo flusso di lavoro usando una funzione con un alias nel mio profilo. Io cambio il prompt in modo da avere un promemoria visivo in cui sono in una "finestra ricorsiva di PowerShell".

Creare uno script nel tuo profilo per iniziare a 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 

Modifica le impostazioni di debug per il progetto Cmdlet

programma esterno Inizio:

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

argomenti della riga di comando:

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

eseguire il debug del modulo

Ora da Visual Studio, avviare debugger con F5, e si dispone di una nuova finestra di PowerShell con il cmdlet caricato, e puoi eseguire il debug come preferisci.

utilizzare l'alias 'SDP' da qualsiasi finestra PowerShell

Dal momento che la funzione Start-DebugPowerShell è nel nostro profilo e abbiamo dato un alias sdp, è possibile utilizzare questo per avviare una seconda istanza di PowerShell in qualsiasi momento ne hai bisogno.

0

Uso un semplice script che rinomina la DLL di destinazione e la carica come modulo. Qui abbiamo 2 hack:

  1. quando il modulo sta caricando da un oggetto assembly .NET ci siamo modulo con nome "dynamic_code_module_FirstPowershellModule" caricato
  2. quindi prima dell'importazione scarichiamo questo modulo e creare nuove uno da file rinominato

assemblee precedenti rimangono inutilizzati nel dominio

script deve essere eseguito al termine di ogni progetto di ricostruzione

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