Команда Powershell не распознается при вызове из C #

Это продолжение этого вопроса здесь, у меня есть Команда PowerShell, которую я создал и могу вызвать команду в окне PowerShell, но при попытке вызова из метода C # я получаю сообщение об ошибке, поскольку командлет не распознается, я пробовал использовать другие существующие команды и получил ту же ошибку, поэтому Я подозреваю, что возникла проблема с импортом модуля, хотя я не получаю эту ошибку в потоках. Ошибка. Единственная ошибка, которую я получаю, это Get-RowAndPartitionKey не распознанный cmndlt, проверьте правописание ...

Хотел бы знать, есть ли другой способ, я должен попробовать или могу ли я отладить больше здесь, чтобы увидеть, извлекает ли мой модуль все команды или нет. прямо сейчас я не знаю, как это исправить.

 public string RunScript( string contentScript, Dictionary<string, EntityProperty> parameters )
    {
        List<string> parameterList = new List<string>();
        foreach( var item in parameters )
        {
            parameterList.Add( item.Value.ToString() );
        }
        using( PowerShell ps = PowerShell.Create() )
                       
        {
            IAsyncResult async =
             ps.AddCommand( "Import-Module" ).AddArgument( @"C:\Users\...\.D.PowerShell.dll" )
               .AddStatement()
               .AddCommand( "Get-RowAndPartitionKey" ).AddParameter( "Properties", "test" )
               .BeginInvoke();

            StringBuilder stringBuilder = new StringBuilder();
            foreach( PSObject result in ps.EndInvoke( async ) )
            {
                stringBuilder.AppendLine( result.ToString() );
            }
            return stringBuilder.ToString();
        }
    }
}

Нижеприведенный метод не возвращает никаких ошибок в Streams.Error или Verbose, но также не выводит:

public async Task<IEnumerable<object>> RunScript( string scriptContents, List<string> scriptParameters )
        {
            // create a new hosted PowerShell instance using the default runspace.
            // wrap in a using statement to ensure resources are cleaned up.

            using( PowerShell ps = PowerShell.Create() )
            {
                // specify the script code to run.
                ps.AddScript( scriptContents );

                // specify the parameters to pass into the script.
                ps.AddParameter( "Properties" ,"test") ;

                // execute the script and await the result.
                var pipelineObjects = await ps.InvokeAsync().ConfigureAwait( false );                
                return pipelineObjects;
            }
        }

scriptContent

 "\"$path = 'C:\\Users...\\.TabularData.PowerShell.dll'\\r\\nImport-Module $path\\r\\nGet-RowAndPartitionKeys\""
    

person ZZZSharePoint    schedule 20.02.2021    source источник
comment
Что касается второго фрагмента: для параметров, передаваемых в Get-RowAndPartitionKey (обратите внимание, что вы использовали форму единственного числа - без s в конце - в первом фрагменте, поэтому я использую это и здесь, вместе с дословной строкой, поэтому вам не нужно экранировать " символов.), вы должны передать ее @args: ps.AddScript(@"Import-Module ""C:\Users...\.TabularData.PowerShell.dll""; Get-RowAndPartitionKey @args")   -  person mklement0    schedule 22.02.2021
comment
Однако нет никакой пользы от добавления дополнительных переменных в микс: если вы не можете заставить исходный фрагмент работать, второй тоже не будет работать.   -  person mklement0    schedule 22.02.2021


Ответы (1)


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

  • Он показывает, что подход работает в принципе, как описано в этом ответе на ваш исходный вопрос.

    • Предварительные требования: пакет SDK PowerShell и среда выполнения .NET, используемые в проекте C #, который вызывает ваш собственный Get-RowAndPartitionKey" командлет должен быть совместим с PowerShell SDK и средой выполнения .NET, которую вы использовали для компиляции библиотеки DLL сборки, содержащей этот командлет, для импорта через Import-Module.

    • Приведенный ниже пример кода гарантирует это неявно, путем запуска непосредственно из PowerShell с использованием Add-Type для компиляции кода C # по запросу - он работает как в Windows PowerShell, так и в PowerShell (Core) 7+.

      • In practice I've found that a .NET Framework-compiled DLL (from Windows PowerShell) also works in PowerShell (Core) (.NET (Core) 5.0), but not vice versa.
  • В нем показаны методы устранения неполадок, а именно:

    • Adding the -Verbose switch to the Import-Module call to produce verbose output that lists the commands being imported from the given module (DLL).
    • Печать этих подробных сообщений (ищите // --- TROUBLESHOOTING CODE)
    • Печать любых возникших непрекращающихся ошибок PowerShell (в отличие от исключений, которые вам придется обрабатывать в коде C #).
# Create a (temporary) assembly containing cmdlet "Get-RowAndPartitionKey".
# This assembly can directly be imported as a module from PowerShell.
# The cmdlet simply outputs "Hi from Get-RowAndPartitionKey" and
# echoes the elements of the list passed to -Properties, one by one.
$tempModuleDll = Join-Path ([IO.Path]::GetTempPath()) "TempModule_$PID.dll"
Remove-Item -ErrorAction Ignore $tempModuleDll
Add-Type @'
  using System.Management.Automation;
  using System.Collections.Generic;
  [Cmdlet("Get", "RowAndPartitionKey")]
  public class GetRowAndPartitionKeyCmdlet : PSCmdlet {
    [Parameter] public List<string> Properties { get; set; }
    protected override void ProcessRecord() {
      WriteObject("Hi from Get-RowAndPartitionKey: ");
      WriteObject(Properties, true);
    }
  }
'@ -ErrorAction Stop -OutputAssembly $tempModuleDll

# Compile a C# class ad hoc to simulate your project, and call its static
# method, which imports the module and effectively calls 
#   Get-RowAndPartitionKey -Properties "foo", "bar"
(Add-Type @"
  using System;
  using System.Management.Automation;
  using System.Collections.Generic;
  using System.Text;

  public static class Foo {
    public static string RunScript(List<string> parameterList)
    {
      using (System.Management.Automation.PowerShell ps = PowerShell.Create())
      {
        IAsyncResult async =
          // Add -Verbose to the Import-Module call, so that the list of 
          // commands being imported is written to the verbose output stream.
          ps.AddCommand("Import-Module").AddArgument(@"$tempModuleDll").AddParameter("Verbose", true)
            .AddStatement()
            .AddCommand("Get-RowAndPartitionKey").AddParameter("Properties", parameterList)
            .BeginInvoke();

        StringBuilder stringBuilder = new StringBuilder();
        foreach (PSObject result in ps.EndInvoke(async))
        {
          stringBuilder.AppendLine(result.ToString());
        }
        
        // --- TROUBLESHOOTING CODE

        // Print verbose output from the Import-Module call
        foreach (var v in ps.Streams.Verbose) { Console.WriteLine("VERBOSE: " + v.ToString()); }

        // Print any errors.
        foreach (var e in ps.Streams.Error) { Console.WriteLine("ERROR: " + e.ToString()); }

        // ---

        return stringBuilder.ToString();
      }
    }
  }
"@ -ErrorAction Stop -PassThru)::RunScript(("foo", "bar"))

# Clean-up instructions:
if ($env:OS -eq 'Windows_NT') {
  Write-Verbose -vb "NOTE: Re-running this code requires you to start a NEW SESSION."
  Write-Verbose -vb "After exiting this session, you can delete the temporary module DLL(s) with:`n`n  Remove-Item $($tempModuleDll -replace '_.+', '_*.dll')`n "
} else {
  Write-Verbose -vb "NOTE: Re-running this code after modifying the embedded C# code requires you to start a NEW SESSION."
  Remove-Item $tempModuleDll
}

На моем компьютере с Windows 10, как из PowerShell (Core) 7.0.5, так и из Windows PowerShell 5.1, приведенное выше дает (инструкции по очистке опущены) следующее, показывая, что все работает как задумано:

VERBOSE: Loading module from path 'C:\Users\jdoe\AppData\Local\Temp\TempModule_11876.dll'.
VERBOSE: Importing cmdlet 'Get-RowAndPartitionKey'.
Hi from Get-RowAndPartitionKey:
foo
bar

В частности, строка VERBOSE: Importing cmdlet 'Get-RowAndPartitionKey'. указывает, что настраиваемый командлет был успешно импортирован в сеанс.

person mklement0    schedule 20.02.2021
comment
У этих двух библиотечных пакетов разные роли: PowerShellStandard.Library и Microsft. Powershell.SDK. Я вижу, что раньше была доступна PowerShellStandardLibrary 5.1.1, но теперь ее нет в диспетчере пакетов nuget - person ZZZSharePoint; 21.02.2021
comment
@ZZZSharePoint, да, первая состоит только из эталонных сборок, которые во время выполнения полагаются на обычную установку PowerShell; последний включает сборки для полного размещения PowerShell (Core) как части приложения. Как уже говорилось, перед использованием одного или другого: см. этот ответ (также ссылка на отвечать). - person mklement0; 21.02.2021
comment
Также я опубликовал еще один вопрос, сделав команду как можно более простой ... может быть, вы можете посмотреть это stackoverflow.com/questions/66313904/ - person ZZZSharePoint; 22.02.2021