1

C# API を介して PowerShell を使用して、次のことを実行しようとしています。

  1. リモートプロセスを開始する
  2. それらを終了します
  3. リモートプロセスの標準およびエラー出力をキャプチャします (プロセスが終了した後ではなく、その出力が生成されているとき)。
  4. リモート プロセスの終了コードをキャプチャします。

次の 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;
}

これはローカルで実行されたプロセスの出力をキャプチャしますが、これはリモート プロセスでも機能しますか? (もう 1 つのあまり重要でない質問は、これがリモート プロセスでどのように機能するかということです。.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 には強く反対しています)。

更新:私がやったことは、リモートでスクリプトを実行するように指示されたpowershell.exeに(プロセスを開始するC#を介して-「powershell」+「-Command ...」を介して)呼び出すことでした(invoke-command)コンピューター。Powershell はデフォルトでリモート PC で生成された出力をキャプチャするため、この Process インスタンスを簡単に取得できました。キャンセルしたいときは、ローカルプロセスを強制終了しました。リモート コマンドのリターン コードは、よりトリッキーでした。コマンド、スクリプト、および C# スニペットをしばらく投稿します。

4

2 に答える 2

0

Process オブジェクトのドキュメントから、ローカル プロセスの開始と停止のみが許可されます。したがって、リモート プロセスを開始および停止する場合は、PowerShell リモート処理または psexec などのツールを使用する必要があります。あなたの C# コメントは、リモート実行空間をセットアップすることを示しているようです。これは、各リモート コンピューターに PowerShell v2 があり、リモート処理が有効になっていて、できればドメイン上にあり、適切な管理者資格情報を持っている場合にうまく機能します。リモート実行空間を設定する C# プロセスは管理者として実行する必要があり、使用する資格情報はリモート コンピューターで管理者権限を持っている必要があります (各リモート コンピューターで特別なリモート エンドポイントを設定していない場合)。

各リモート コンピューターに PowerShell v2 または v3 があり、リモート処理を有効にできる場合 (Enable-PSRemoting -force を実行するだけ)、必要なことを簡単に実行できるはずです。ただし、.NET Process オブジェクトを使用するためにドロップダウンする必要はありません。PowerShell コマンドレットを使用するだけです。このようなものをスクリプトに入れ、作成したパイプラインに追加して呼び出します。

$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)
于 2013-01-13T21:15:31.320 に答える
0

Powershell ジョブはリモートで実行でき、出力をキャプチャして元のマシンに返します。これにより、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

invoke-command出力にも必要な場合は、終了コードを取得するためにscriptblock パラメーターを微調整する必要があるだけです。

于 2013-01-13T21:45:52.390 に答える