问题
从启用了异步重定向的C#执行PSExec作为进程我的数据接收事件处理程序永远不会被调用。
上下文
我有一个C#应用程序,用于连接和管理同一本地网络上多台其他计算机上的从属应用程序。主应用程序和从属应用程序都是Windows窗体应用程序。我的应用程序的一个功能是能够远程启动从属应用程序。由于这需要一些时间,我希望使用PSexec的输出来显示实际发生的事情。下面是我用来启动远程应用程序的代码。
ProcessStartInfo start = new ProcessStartInfo();
start.CreateNoWindow = true;
start.RedirectStandardError = true;
start.RedirectStandardOutput = true;
start.RedirectStandardInput = true;
start.UseShellExecute = false;
start.WorkingDirectory = System.Windows.Forms.Application.StartupPath + "\\PSTools\\";
start.FileName = start.WorkingDirectory + "PsExec.exe";
start.Arguments = "\\\\" + ComName + " -u \"" + UserName + "\" -p \"" + Password +
"\" -i -w " + RemoteDrectory + "\" \"" + RemoteDrectory + "\\" + ApplicationName + "\"";
try
{
if (proc != null)
{
if (proc.HasExited == false)
{
proc.Kill();
proc.Close();
}
proc.Dispose();
proc.OutputDataReceived -= ProcDataReceaved;
proc.ErrorDataReceived -= ProcDataReceaved;
}
proc = new Process();
proc.EnableRaisingEvents = true;
proc.OutputDataReceived += new DataReceivedEventHandler(ProcDataReceaved);
proc.ErrorDataReceived += new DataReceivedEventHandler(ProcErrorReceaved);
proc = Process.Start(start);
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
}
catch(Exception e)
{
Console.WriteLine("An error occurred while launching slave application\r\n" + e.ToString());
}
这实际上具有在远程机器上启动从属应用程序而没有问题的预期效果。此外,如果我禁用输出重定向,控制台将显示以下内容:
PsExec v2.2 - 远程执行过程版权所有(C)2001-2016 Mark Russinovich Sysinternals - www.sysinternals.com
在ComName上启动PSEXESVC服务...
建立连接后,最后一行将被删除。如果它现在很重要,事件处理程序定义如下:
public void ProcDataReceaved(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Data Received from process\r\n" + e.Data);
}
public void ProcErrorReceaved(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Data Received from process\r\n" + e.Data);
}
可选目标
如果有的话,奴隶应用程序不会有太多的控制台输出。但是,如果我可以通过PSexec输出准备从奴隶的任何输出,那将是甜蜜的。
潜在领导3/8/17
今天我和https://www.poweradmin.com/paexec/的代表谈过,特别是一位名叫大卫的代表。有版本的PSExec(PAExec)使用WriteConsole命令来写我想要接收的输出。因此,此输出不会写入stderr或stdout,而是直接写入控制台的屏幕缓冲区。显然这并没有说PSExec,因为他们不是同一个人。但它确实表明这可能是我的问题。如果是这样,我需要另一种方法来跟踪连接进度。
似乎一切都归结为速度。如果进程运行得太快,则事件不会引发。见下文。
using System;
namespace PSExec_42280413
{
class Program
{
static void Main(string[] args)
{
DoIt();
}
private static void DoIt()
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.ErrorDataReceived += Proc_ErrorDataReceived;
proc.OutputDataReceived += Proc_OutputDataReceived;
proc.Exited += Proc_Exited;
proc.StartInfo.FileName = @"c:\windows\syswow64\psexec_64.exe";
proc.StartInfo.Arguments = @"-i -u usr -p pwd \\server -c M:\StackOverflowQuestionsAndAnswers\PSExec_42280413\PSExec_42280413\TheBatFile.bat";
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.EnableRaisingEvents = true;
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
//if the process runs too quickly, the event don't have time to raise and the application exits.
//this here takes care of slooowwwwwiiinnnnnggggg things down
//there are more elegant ways to wait and it does not have to be this long
Int64 bigone = 200000000;
while (bigone > 0)
{
bigone--;
}
}
private static void Proc_Exited(object sender, EventArgs e)
{
//string out = ((System.Diagnostics.Process)sender).StandardError.ReadToEnd();//not while BeginErrorReadLine() is set.
Console.WriteLine("done");
System.Diagnostics.Debugger.Break();
}
private static void Proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
Console.Write("1");
//System.Diagnostics.Process process = (System.Diagnostics.Process)sender;
//System.Diagnostics.Debugger.Break();
}
private static void Proc_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
Console.Write("0");
//System.Diagnostics.Process process = (System.Diagnostics.Process)sender;
//System.Diagnostics.Debugger.Break();
}
}
}
我花了一些时间,但我找到了一个解决方案,我希望是这样的。
获得输出的唯一方法是通过CMD回显结果。
我这样做是为了远程获取可用网站的信息,它只返回PSExec文本,并且它没有返回AppCmd.exe(或psexesvc)返回的文本。
我找到了两个解决方案来获取数据。(字符串被转义,因为有一个带引号的小游戏)
[Hard Way]使用命令在远程计算机或本地计算机上创建文本文件**然后检查我正在寻找的是否存在
"\\\\hostIP -u user -p password cmd /c \"%systemroot%\\system32\\inetsrv\\AppCmd.exe list sites > c:\\sites.txt\""
然后你可以使用命令读出sites.txt文件
\\\\hostIP -u user -p password find \"Site\" c:\\sites.txt"
[简单方法]使用将反映为控制台输出的ECHO,因此您将能够读取psexesvc返回的文本。我做它不起作用的方式是这样的:
"\\\\hostIP -u user -p password cmd ECHO /c \"%systemroot%\\system32\\inetsrv\\AppCmd.exe list sites\""
希望这有助于,享受。