Можете ли вы снова удалить добавленный тип в PowerShell?

В настоящее время я пишу библиотеку на С# и в некоторых случаях использовал PowerShell для ее быстрого тестирования. Однако это мешает мне пересобрать проект, поскольку PowerShell, очевидно, по-прежнему имеет открытую DLL.

Есть ли способ снова выгрузить DLL после добавления ее с помощью Add-Type? Документация, кажется, не имеет подсказок по этому поводу, и очевидным кандидатом будет Remove-Type (которого не существует — в любом случае есть только одна команда с Type в качестве существительного). Закрывать PowerShell и выполнять все действия по переходу к каталогу сборки и повторному добавлению типа каждый раз, когда я хочу перестроить, становится обременительно.


person Joey    schedule 30.07.2010    source источник
comment
Теперь в документации сказано следующее: вы не можете выгрузить тип или изменить его. См. docs.microsoft.com/en -us/powershell/модуль/   -  person Alan McBee    schedule 02.02.2020


Ответы (6)


Как говорят другие, это поведение .NET. Сборки, загруженные в AppDomain, не могут быть выгружены. Выгрузить можно только домен приложения, а powershell использует один домен приложения. Несколько лет назад я писал об этом в блоге:

https://web.archive.org/web/20170707034334/http://www.nivot.org/blog/post/2007/12/07/WhyAppDomainsAreNotAMagicBullet

Когда я тестирую таким образом, я обычно держу оболочку открытой и использую вложенную оболочку для выполнения тестов. запустите powershell, перейдите в папку с компакт-диском, затем запустите «powershell», чтобы запустить вложенную оболочку (новый процесс). «выход», чтобы начать заново, и снова запустите «powershell».

person x0n    schedule 30.07.2010
comment
@jpaugh, к сожалению, моя хостинговая компания удалила мою бесплатную учетную запись, и я забыл экспортировать данные своего блога. Я обновил ссылку на архив dot org. - person x0n; 13.09.2019
comment
Жаль это слышать! Могу сказать, что logdown.com не удалил мой бесплатный аккаунт; после нескольких лет бездействия я возвращаюсь к этому. YMMV, но я рекомендую это. Изменить: URL-адрес - person jpaugh; 14.09.2019

Я считаю, что самый простой способ обойти эту проблему — поместить Add-Type и тестовый код в Start-Job. Start-Job создаст фоновый процесс, и тип будет загружен туда. Как только вы закончите, процесс завершится, и вы сможете повторить попытку.

Вот пример того, как это выглядит:

$job = Start-Job -ScriptBlock {

    Add-Type -path 'my.dll'
    $myObj = new-object My.MyTestClassName

    $result = $myObj.TestMethod
    $result
}
Wait-Job $job
Receive-Job $job

Вывод тестового метода будет выведен на консоль.

person Start-Automating    schedule 30.07.2010
comment
Мне нравится этот метод, хотя в моем случае мне нужен результат набора переменных в фоновом задании. Без записи в файл я не уверен, как получить эту информацию. Тем не менее, я думаю, что это заслуживает +1 - person Slogmeister Extraordinaire; 22.09.2015
comment
@SlogmeisterExtraordinaire В задании повторите результат. В основном скрипте вызовите Receive-Job, чтобы получить его. - person Micha Wiedenmann; 17.12.2015
comment
Я добавил пример того, как это сделать. Любые опечатки или ошибки в примере принадлежат только мне, а не @Start-Automating. - person Katie Kilian; 02.02.2017
comment
Спасибо. Это действительно помогло мне с запутанными загруженными сборками: stackoverflow.com/a/58417163/6466378 - person halllo; 16.10.2019

Если ваша сборка не требует контекста привязки можно сделать так:

$bytes = [System.IO.File]::ReadAllBytes("Path_To_Your_Dll.dll")
[System.Reflection.Assembly]::Load($bytes)
person George Howarth    schedule 30.07.2010
comment
и как это позволило бы разгрузить сборку? - person Farrukh Waheed; 11.10.2013
comment
Вы не сможете выгрузить, но OTOH DLL не будет оставаться открытой и заблокированной, поскольку чтение не зависит от загрузки (ReadAllBytes не сохраняет файл открытым или заблокированным). Так что это зависит от того, чего хочет ОП; это решит проблему, связанную с тем, что я не могу построить, которая, казалось, была причиной желания выгрузить. - person Chris J; 07.02.2014
comment
Это приятно. Одно небольшое несоответствие/недостаток заключается в том, что Add-Type загружает типы в указанной сборке и все ее зависимости, в то время как это загружает только типы из указанной сборки. Поэтому, если вам нужны типы из вышестоящих сборок, вам также нужно будет использовать это для их загрузки. - person JLRishe; 22.06.2019
comment
Это прекрасно работает! Даже для DLL, которые я загрузил в память. Нет необходимости сначала сохранять их в виде файла. Большое спасибо, что поделились. - person Carsten; 29.08.2020

Вот полный пример, который позволяет запускать команду Add-Type в качестве фонового задания, чтобы сборка выгружалась после ее завершения:

# Start-Job will not preserve the working directory, so do it manually
# Other arguments can also be passed to the job this way
$cd = Split-Path $MyInvocation.MyCommand.Path
$jobParams = @{
    'cd' = $cd
}

Start-Job -InputObject $jobParams -ScriptBlock {
    cd $Input.cd
    Add-Type -Path assembly.dll
} | Receive-Job -Wait -AutoRemoveJob

Receive-Job -Wait позаботится о том, чтобы выходные данные задания были получены, иначе они будут потеряны.

person Knaģis    schedule 10.01.2017

Я столкнулся с подобной проблемой. Невозможно выгрузить тип/сборку (потому что это относится к платформе .NET).

В .NET вы можете решить эту проблему, если создадите новый домен приложения (System.AppDomain) и загрузите сборку в этот домен. Можно выгрузить домен приложения, а также выгрузить все dll.

Я еще не пробовал, потому что для меня гораздо проще закрыть вкладку в Консоли и открыть новый.

person stej    schedule 30.07.2010
comment
Powershell использует только один домен приложения, создавая новый домен приложения, загружая типы в этот домен приложения, а затем выгружая домен приложения, тип не выгружается. - person Scott Mackay; 04.09.2015

Существует простое решение этой проблемы. Просто добавьте случайный суффикс к имени класса, и все готово.

$id = get-random
$code = @"
using System;
namespace HelloWorld    {
    public class Program$id
{
    public static void Main(){
        Console.WriteLine("Hello world!");
    }
}
}"@

Add-Type -TypeDefinition $code -Language CSharp 
iex "[HelloWorld.Program$id]::Main()"

(из: Выполнение кода C# с использованием сценария PowerShell — Random IT Utensils https://blog.adamfurmanek.pl/2016/03/19/executing-c-code-using-powershell-script)

person nergeia    schedule 07.05.2021