Почему мою функцию powershell не удается вызвать из C#?

Попытка использовать сценарий PowerShell со следующей функцией:

    function MoveCompressFiles{
 Param
    (
         [Parameter(Mandatory=$true )]
         [string] $Des,
         [Parameter(Mandatory=$true)]
         [string] $Src
    )
Add-Type -AssemblyName System.Drawing 
$files = Get-ChildItem $Src 
 foreach ($f in $files) {    
    if (($f.Length / 1KB) -lt [int32]200) {
        Copy-Item -Path $f.FullName -Destination $Des
    } 
    else {      
       Copy-Item -Path $f.FullName -Destination $Des
        while (((Get-Item (($Des).ToString() + "\$f")).Length / 1KB ) -gt 500) {
            $img = [System.Drawing.Image]::FromFile((($Des).ToString() + "$f"))
            [int32]$new_width = $img.Width * (20 / 100);
            [int32]$new_height = $img.Height * (20 / 100);
            $img2 = New-Object System.Drawing.Bitmap($new_width, $new_height)
            $graph = [System.Drawing.Graphics]::FromImage($img2)
            $graph.DrawImage($img, 0, 0, $new_width, $new_height)
            $newImgName = "M".ToString() + $f.ToString()
            $img2.Save(($Des).ToString()+"\$newImgName")
            $img.Dispose()
            $img2.Dispose()
            Remove-Item ($Des.ToString()+$f)
            Rename-Item -Path ($Des.ToString()+$newImgName) -NewName "$f"            
            Write-Host ((Get-Item ($Des.ToString()+$f)).Length / 1KB )
        } 
        $filesize = $f.Length * 0.8
        $filesize=($filesize / 1KB)
        #$filesize = [math]::round(($filesize / 1KB), 0)
        $abc = "KB"
        $filesizeSTR = $filesize.ToString() + $abc
        Push-Location $Src
        mogrify -path $Des -define jpeg:extent=$filesizeSTR $f
        Pop-Location
        Write-Host "Moved file $f"
    } 
}
}

Работает в Powershell, однако, когда я пытаюсь сделать это в своем решении,

private static void Powershell()
        {
            string SCRIPT_PATH = System.IO.File.ReadAllText(@"C:\Untitled2.ps1");
           using (Runspace runspace = RunspaceFactory.CreateRunspace())
            {
                runspace.Open();
                PowerShell ps = PowerShell.Create();
                ps.Runspace = runspace;
                ps.AddScript(SCRIPT_PATH);
                ps.Invoke();
                ps.AddCommand("MoveCompressFiles").AddParameters(new Dictionary<string, string>
                {
                    {"Des" , @"C:\Des"},
                    {"Src", @"C:\Src"}
                });
            }
     }

Это не работает, я пробовал другие методы вызова функции из сценария ps, но он все еще не может даже переместить файлы в другое место

1 ответ

Решение

Поскольку вам нужно указать источник вашего файла сценария (. <script>) для того, чтобы MoveCompressFilesдоступна функция, для которой требуется.AddScript() вызова, я предлагаю создать единый фрагмент кода PowerShell в строковой переменной, которая будет использовать точечный источник скрипта и вызывать вашу функцию через один .AddScript() вызов.

Однако, чтобы гарантировать, что.AddScript()работает, вы должны сначала убедиться, что политика выполнения PowerShell разрешает вызов сценария, используя вызовSet-ExecutionPolicy; в приведенном ниже коде используется-Scope Process, чтобы ограничить изменение текущего процесса.

var SCRIPT_PATH = @"C:\Untitled2.ps1";
var src = @"C:\Src";
var des = @"C:\Des";
var script = $@". ""{SCRIPT_PATH}""; MoveCompressFiles -Des ""{des}"" -Src ""{src}""";

using (PowerShell ps = PowerShell.Create())
{
  // Make sure that script execution is allowed.
  ps.AddCommand("Set-ExecutionPolicy")
        .AddParameter("Scope", "Process")
        .AddParameter("ExecutionPolicy", "Bypass")
        .AddParameter("Force", true);
  ps.Invoke();

  // Add the PowerShell code constructed above and invoke it.
  ps.AddScript(script);
  // Use foreach (var o in ps.Invoke()) { Console.WriteLine(o); } to print the output.
  ps.Invoke();
}

Обратите внимание на упрощенное, неявное создание пространства выполнения с помощьюPowerShell.Create() только.

Встроенный код PowerShell является источником вашего файла сценария (. <script>), чтобы определить MoveCompressFiles функция, а затем вызывает функцию.

Обратите внимание, что приведенный выше, как ваш собственный код, не захватывает и не печатает вывод из кода PowerShell (.Invoke()вывод).

Чтобы узнать, произошли ли ошибки, вы можете проверить ps.HadErrors и изучить ps.Streams.Error или любой из других потоков, например .ps.Streams.Information для Write-Hostвывод (вывод потока успеха - это то, что.Invoke() возвращается напрямую).

Например, используйте что-то вроде следующего, чтобы распечатать все ошибки (только сообщения), которые произошли в стандартном потоке ошибок консоли:

foreach (var o in ps.Streams.Error) {
  Console.Error.WriteLine(o);
}

Что касается того, что вы пробовали:

ps.AddScript(SCRIPT_PATH); ps.Invoke();

Пока это выполняет ваш скрипт, он делает это в дочерней области, поэтому встроенная функцияMoveCompressFilesопределение не добавляется в область верхнего уровня вашего сеанса, поэтому последующие.AddCommand() вызов не удается, потому что MoveCompressFiles функция недоступна.

Вместо этого вы должны использовать точечный код для своего скрипта (. <script>), что заставляет его работать в области действия вызывающего и, следовательно, делает доступным там определение его функции.

В стороне: несмотря на .AddScript()имя метода, его основная цель - выполнить фрагмент кода PowerShell, а не файл сценария. Чтобы выполнить последнее (без использования точек), используйте.AddCommand().

Другие вопросы по тегам