简体   繁体   English

为什么我的powershell function无法从C#调用?

[英]Why does my powershell function fail to be called from C#?

Trying to use a powershell script with a function as follows:尝试将 powershell 脚本与 function 一起使用,如下所示:

    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"
    } 
}
}

Works in Powershell, however when i try to do it it in my solution,在 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"}
                });
            }
     }

It doesn't work, I've tried some other methods of calling a function from a ps script but it still fails to even move the files to another location它不起作用,我尝试了一些从 ps 脚本调用 function 的其他方法,但它仍然无法将文件移动到另一个位置

Since you need to dot-source your script file ( . <script> ) in order to make the MoveCompressFiles function available , which requires an .AddScript() call, I suggest constructing a single piece of PowerShell code in a string variable that both dot-sources the script and invokes your function via a single .AddScript() call.由于您需要点源您的脚本文件 ( . <script> ) 以使MoveCompressFiles function 可用,这需要.AddScript()调用,我建议在一个字符串变量中构造一段 PowerShell 代码- 获取脚本并通过单个.AddScript()调用调用您的 function。

However, in order to guarantee that .AddScript() works, you must first ensure that the PowerShell execution policy allows script invocation , using a call to Set-ExecutionPolicy ;但是,为了保证.AddScript()有效,您必须首先确保PowerShell 执行策略允许脚本调用,使用对Set-ExecutionPolicy的调用; the code below uses -Scope Process , so as to limit the change to the current process.下面的代码使用-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();
}

Note the simplified, implicit runspace creation, by using PowerShell.Create() only.请注意简化的隐式运行空间创建,仅使用PowerShell.Create()

The embedded PowerShell code dot-sources your script file ( . <script> ) in order to define the MoveCompressFiles function, and then invokes the function.嵌入式 PowerShell 代码点源您的脚本文件 ( . <script> ) 以定义MoveCompressFiles function,然后调用 ZC1C425268E68385D1AB5074C17A94F。

Note that the above, as your own code, doesn't capture or print the output from the PowerShell code ( .Invoke() 's output).请注意,以上作为您自己的代码,不会从 PowerShell 代码( .Invoke()的输出)中捕获或打印 output。

To see if errors occurred, you can check ps.HadErrors and examine ps.Streams.Error or any of the other streams, such as .ps.Streams.Information for the Write-Host output (the success stream's output is what .Invoke() returns directly).要查看是否发生错误,您可以检查ps.HadErrors并检查ps.Streams.Error或任何其他流,例如Write-Host .ps.Streams.Information的 .ps.Streams.Information (成功流的 output 是什么.Invoke()直接返回)。

For instance, use something like the following to print all errors (messages only) that occurred to the console's standard error stream:例如,使用类似以下的内容来打印控制台的标准错误 stream 发生的所有错误(仅限消息):

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

As for what you tried :至于你尝试了什么:

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

While this executes your script, it does so in a child scope , so the embedded function MoveCompressFiles definition is not added to your session's top-level scope, so the subsequent .AddCommand() call fails, because the MoveCompressFiles function isn't available. While this executes your script, it does so in a child scope , so the embedded function MoveCompressFiles definition is not added to your session's top-level scope, so the subsequent .AddCommand() call fails, because the MoveCompressFiles function isn't available.

Instead, you must dot-source your script ( . <script> ), which makes it run in the caller's scope and therefore makes its function definition available there.相反,您必须对您的脚本 ( . <script> ) 进行点源处理,这使其在调用者的 scope 中运行,因此其 function 定义在那里可用。

As an aside: Despite the .AddScript() method's name, its primary purpose is to execute a piece of PowerShell code , not a script file .顺便说一句:尽管有.AddScript()方法的名称,但它的主要目的是执行一段 PowerShell 代码,而不是脚本文件 To execute the latter (without dot-sourcing), use .AddCommand() .要执行后者(没有点源),请使用.AddCommand()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM