我正在尝试通过其C#API使用PowerShell来执行以下操作:
我正在使用以下PowerShell脚本启动远程进程:
$ps = new-object System.Diagnostics.Process
$ps
$ps.StartInfo.Filename = "c:\Echo.exe"
$ps.StartInfo.Arguments = ""
$ps.StartInfo.RedirectStandardOutput = $true
$ps.StartInfo.RedirectStandardError = $true
$ps.StartInfo.UseShellExecute = $false
$ps.start()
$ps.WaitForExit()
$ps.ExitCode
我的原型的C#部分如下所示:
// Note: in this example the process is started locally
using (Runspace runspace = RunspaceFactory.CreateRunspace(/*_remotePcConnectionInfo*/))
{
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
// Scripts.RunExecutable is that script above
pipeline.Commands.AddScript(Scripts.RunExecutable);
pipeline.InvokeAsync();
var process = (Process)pipeline.Output.Read().BaseObject;
bool started = (bool)pipeline.Output.Read().BaseObject;
// Not showing the dummy event handlers - they simply do a Console.WriteLine now.
process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
process.BeginOutputReadLine();
// Not showing the dummy event handlers - they simply do a Console.WriteLine now.
process.ErrorDataReceived += new DataReceivedEventHandler(process_ErrorDataReceived);
process.BeginErrorReadLine();
int processExitCode = (int) pipeline.Output.Read().BaseObject;
}
这确实捕获了本地运行的进程的输出,但是这也适用于远程进程吗?(另一个不太重要的问题是:这对于远程进程如何工作?是.Net Remoting以某种方式参与我是否获得了Process的代理?)如果没有,那么这样做的方法是什么?请注意,我需要输出正在生成的输出,而不是在过程终止后。
这不会捕获进程终止。我尝试先通过捕获进程Id然后从不同的运行空间运行“Stop Process”PowerShell脚本来终止。失败了,因为“管道已经在运行”而且管道无法并行运行......然后我尝试从C#调用process.Kill(),对我的本地进程有效,但SO报告它赢了“为远程进程工作... 然后我尝试调整我的PowerShell脚本,使其包含一个全局变量和一个等待循环,但我从未想过如何在管道启动后设置该变量。添加的循环看起来像这样:
while ($ps.HasExited -eq $false -and $global:cancelled -eq $false)
{
Start-Sleep -seconds 1
}
if ($global:cancelled -eq $true)
{
$ps.Kill()
}
所以,那也失败了。 我的方案有关流程终止的任何建议吗?
PowerShell是否适合这个?(我们强烈反对我们之前尝试使用的openSSH,因为它有分叉问题)。
更新:我最终做的是调用(通过C#启动进程 - “powershell”+“-Command ...”)进入powershell.exe,告知执行远程计算机上的脚本(invoke-command)。 Powershell默认捕获远程PC上产生的输出,因此我可以很容易地获得Process实例的这个。当我想取消时,我杀死了本地进程。远程命令的返回代码比较棘手。我会在一段时间内发布命令,脚本和C#-snippet。
答案 0 :(得分:0)
从Process对象的文档中,它只允许你启动&停止本地进程。因此,如果你想开始&停止远程进程然后你需要使用PowerShell远程处理或像psexec这样的工具。我看到您的C#注释表明您将设置一个远程运行空间。如果您在每台远程计算机上安装PowerShell v2,启用了远程处理,最好是在域上并拥有相应的管理员凭据,那么这种方法很有用。 IIRC设置远程运行空间的C#进程必须以管理员身份运行,并且它使用的凭据必须具有远程计算机上的管理员权限(除非您在每台远程计算机上设置了特殊的远程终端)。
如果您在每台远程计算机上都安装了PowerShell v2或v3,并且可以启用远程处理(只需运行Enable-PSRemoting -force),那么您应该可以轻松地执行所需的操作。但是,无需下拉即可使用.NET Process对象。只需使用PowerShell cmdlet。在脚本中添加这样的内容,将其添加到您创建的管道并调用它。
$p = Start-Process Echo.exe -Arg "output from echo.exe" -PassThru
$p | Wait-Process -Timeout 3 # 3 seconds
if (!$?) { # Success code $? returns $false if Wait-Process times out
$p | Stop-Process
}
对于异步支持,请尝试这种方式:
$script = {
$pid
ipconfig.exe /all
$LastExitCode
}
$job = Invoke-Command localhost $script -AsJob
do {
Receive-Job $job
}
while (!(Wait-Job $job -Timeout 1))
Receive-Job $job
Remove-Job $job
使用返回的$ pid,您可以将其与WMI一起使用,以便在必要时找到所有子进程以杀死它们,如下所示:
$childPids = @(Get-WmiObject -Class Win32_Process -Filter "ParentProcessID=$PID" |Select-Object -Property ProcessID)
答案 1 :(得分:0)
Powershell Jobs可以远程执行,并捕获其输出并将其返回到原始机器。有了这个,您就不必在C#中创建一个远程运行空间,只需要在本地运行空间。
执行此操作的powershell脚本如下所示:
$job = invoke-command -computername remotecomputer -scriptblock { start-process "C:\Echo.exe" -wait } -asjob
while ( $job.State -eq "Running" ) {
wait-job -job $job -timeout 1
receive-job -job $job -keep # print the current output but keep all output for later.
}
receive-job -job $job
您可能只需要将scriptblock参数调整为invoke-command
以获取退出代码,如果在输出中也是如此。