Как получить доступ к базовому пространству выполнения PowerShell из командлета C #?

#c# #powershell #.net-core

#c# #powershell #.net-core

Вопрос:

Я создаю несколько командлетов PowerShell с использованием API-интерфейсов C # / .NET, и у меня есть командлет, которому требуется доступ ко всем импортируемым в данный момент модулям. Если я запускаю Get-Module в терминале, я вижу загруженные модули 10 , но при запуске моего командлета:

 using(var ps = PowerShell.Create(RunspaceMode.Current))
{
    var modules = ps.AddCommand("Get-Module").Invoke<PSModuleInfo[]>();
    /// ...
}
 

modules здесь пусто. Это почти как проблема с областью действия, но я подумал RunspaceMode.Current , что это позволит вам получить доступ к тому базовому пространству выполнения, которое было открыто при первом открытии pwsh оболочки.

Должно быть, я что-то упускаю или неправильно понимаю, как именно работают пространства выполнения.

Комментарии:

1. «командлет, которому требуется доступ ко всем импортируемым в данный момент модулям», звучит как запах кода… чего вы пытаетесь достичь?

2. Ну, это не совсем тот код, с которым я имею дело, просто базовый пример. Однако мой код выполняет некоторую генерацию прокси-сервера динамического модуля PS, и после того, как мой командлет «выполнен», мне нужно, чтобы он очистился после себя. Итак, я пытаюсь вызвать «Remove-Module» для этих динамических модулей, и, похоже, ничего не происходит, когда это выполняется изнутри командлета. Похоже, это та же проблема, что и у «Get-Module», и это более простой пример, поэтому я использовал это в своем вопросе.

3. Если бы я написал свой командлет непосредственно в PS, все это сработало бы, поэтому, я думаю, я не понимаю, почему это не работает, как только вы попробуете это на C #.

Ответ №1:

Вы не должны возиться с вызывающим пространством выполнения непосредственно из командлета — оно уже занято выполнением вашего командлета. Вы также не можете вызвать a PSCmdlet непосредственно из другого по той же причине.

Вместо этого вы захотите использовать PSCmdlet.InvokeCommand.InvokeScript() , чтобы среда выполнения выполняла для вас сценарий (точно так же, как если бы у вас был Get-Module оператор в функции PowerShell):

 [Cmdlet(VerbsCommon.Get, "ModuleProxy")]
public class GetModuleProxyCommand : PSCmdlet
{
    // ...

    protected override void EndProcessing()
    {
        foreach(var module in InvokeCommand.InvokeScript("Get-Module"))
        {
            WriteObject(module);
        }
    }
}
 

При этом, если то, что вы делаете, влияет на среду таким образом, что требует от вас выполнения Remove-Module или очистки в контексте вызывающих абонентов, вам, вероятно, гораздо лучше выполнять свою работу в отдельном пространстве выполнения в любом случае 🙂

Комментарии:

1. Хорошо, спасибо, что, по крайней мере, проясняет «почему», похоже, это не позволяет вам это сделать. Я делаю здесь некоторые очень нетрадиционные вещи с PS, так что это не так просто, как можно было бы подумать. Эти «дополнительные» прокси являются временными и предназначены только для использования в качестве фасада для обеспечения кэширования подключения / авторизации. Это долгая история, ха-ха. Но я постараюсь сделать это другим способом, подобным тому, что вы изложили. Спасибо

2. @MattHintzke Это не звучит особенно нетрадиционно или странно вообще — на самом деле это очень похоже на JEA 🙂

3. Ха-ха, хорошо, это хорошо. Поскольку вы кажетесь гуру PowerShell, вам может быть интересно посмотреть, что мы создаем. skykick.com/manage . Цель состоит в том, чтобы помочь снизить когнитивную нагрузку на управление ИТ, поскольку SaaS, IaaS и PaaS становятся все более сложными.