Размещенная оболочка PowerShell не может видеть командлеты в той же сборке

Я пытаюсь запустить сценарии PowerShell из своего кода C#, который будет использовать пользовательские командлеты из сборки, которая их запускает. Вот код:

using System;
using System.Management.Automation;

[Cmdlet(VerbsCommon.Get,"Hello")]
public class GetHelloCommand:Cmdlet
{
    protected override void EndProcessing()
    {
        WriteObject("Hello",true);
    }
}

class MainClass
{
    public static void Main(string[] args)
    {
        PowerShell powerShell=PowerShell.Create();
        powerShell.AddCommand("Get-Hello");
        foreach(string str in powerShell.AddCommand("Out-String").Invoke<string>())
            Console.WriteLine(str);
    }
}

Когда я пытаюсь запустить его, я получаю исключение CommandNotFoundException. Я неправильно написал свой командлет? Есть ли что-то, что мне нужно сделать, чтобы зарегистрировать мой командлет в PowerShell или в Runspace или что-то в этом роде?


person Idan Arye    schedule 26.09.2011    source источник


Ответы (3)


Самый простой способ сделать это с вашим текущим фрагментом кода выглядит следующим образом:

using System; 
using System.Management.Automation; 

[Cmdlet(VerbsCommon.Get,"Hello")] 
public class GetHelloCommand:Cmdlet 
{ 
    protected override void EndProcessing() 
    { 
        WriteObject("Hello",true); 
    } 
} 

class MainClass 
{ 
    public static void Main(string[] args) 
    { 
        PowerShell powerShell=PowerShell.Create();

        // import commands from the current executing assembly
        powershell.AddCommand("Import-Module")
            .AddParameter("Assembly",
                  System.Reflection.Assembly.GetExecutingAssembly())
        powershell.Invoke()
        powershell.Commands.Clear()

        powershell.AddCommand("Get-Hello"); 
        foreach(string str in powerShell.AddCommand("Out-String").Invoke<string>()) 
            Console.WriteLine(str); 
    } 
} 

Это предполагает PowerShell v2.0 (вы можете проверить свою консоль с помощью $psversiontable или по дате авторского права, которая должна быть 2009). Если вы используете win7, вы используете v2.

person x0n    schedule 26.09.2011

Еще один простой способ — зарегистрировать командлеты в конфигурации пространства выполнения, создать пространство выполнения с этой конфигурацией и использовать это пространство выполнения.

using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

[Cmdlet(VerbsCommon.Get, "Hello")]
public class GetHelloCommand : Cmdlet
{
    protected override void EndProcessing()
    {
        WriteObject("Hello", true);
    }
}

class MainClass
{
    public static void Main(string[] args)
    {
        PowerShell powerShell = PowerShell.Create();
        var configuration = RunspaceConfiguration.Create();
        configuration.Cmdlets.Append(new CmdletConfigurationEntry[] { new CmdletConfigurationEntry("Get-Hello", typeof(GetHelloCommand), "") });
        powerShell.Runspace = RunspaceFactory.CreateRunspace(configuration);
        powerShell.Runspace.Open();

        powerShell.AddCommand("Get-Hello");
        foreach (string str in powerShell.AddCommand("Out-String").Invoke<string>())
            Console.WriteLine(str);
    }
}

На всякий случай при таком подходе классы командлетов не обязательно должны быть общедоступными.

person Roman Kuzmin    schedule 26.09.2011
comment
Спасибо. Есть ли способ сделать это изнутри PowerShell? То есть загрузить DLL с помощью [Reflection.Assembly]::LoadFrom, а затем вызвать функцию, которая загружает командлеты в текущий сеанс PowerShell? - person Idan Arye; 27.09.2011
comment
Я так не пробовал (просто не пришлось). В PowerShell вы можете получить доступ к конфигурации пространства выполнения по умолчанию (более или менее текущего, но это зависит от) как: [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.RunspaceConfiguration. Затем попробуйте вызвать на нем .Cmdlets.Append(...). Было бы неплохо, если бы вы попробовали это и рассказали нам о результатах. - person Roman Kuzmin; 27.09.2011
comment
Я не могу заставить его работать. Похоже, что Runspace не будет загружать командлеты таким образом, если только он не находится в состоянии BeforeOpen. Есть ли способ загрузить командлеты в уже открытое пространство выполнения? - person Idan Arye; 27.09.2011

Вам необходимо зарегистрировать свой командлет, прежде чем вы сможете использовать его в сеансе Powershell. Обычно вы делаете это с помощью оснастки Powershell. Вот общий обзор процесса:

  • Создайте собственные командлеты (вы уже сделали эту часть)
  • Создайте оснастку Powershell, содержащую и описывающую ваши командлеты.
  • Установите новую оснастку в вашей системе, используя Installutil
  • Добавьте оснастку в конкретный сеанс Powershell, используя Add-PSSnapin

В MSDN есть пара полезных статей, которые довольно подробно объясняют процесс:

На ByteBlocks также есть серия из двух частей, в которой обсуждается написание пользовательских командлетов. Эта серия может быть вашим лучшим выбором, так как вы, кажется, уже сделали эквивалент части 1. Вы можете просто использовать часть 2 в качестве краткого справочника, и все готово.

person ajk    schedule 26.09.2011
comment
SnapIn — это способ работы powershell v1, и в наши дни он не рекомендуется, поскольку для его регистрации требуются права администратора. Он должен использовать модули powershell v2. - person x0n; 27.09.2011
comment
На самом деле не отвечает на вопрос, что он пытается сделать. - person manojlds; 27.09.2011
comment
@manojlds Это действительно отвечает на вопрос, но определенно не так актуально и элегантно, как ответ x0n. - person ajk; 27.09.2011
comment
@ x0n Я проголосовал за ваш ответ, так как он намного элегантнее и имеет дополнительное преимущество, состоящее в том, что он автономен. - person ajk; 27.09.2011