远程启动应用程序时无法从 psexec 接收异步输出

Posted

技术标签:

【中文标题】远程启动应用程序时无法从 psexec 接收异步输出【英文标题】:Can't receive asynchronous output from psexec when launching application remotely 【发布时间】:2017-02-16 17:07:10 【问题描述】:

问题

在启用异步重定向的情况下从 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 标记 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,因为他们不是同一个人。但这确实表明这可能是我的问题。如果是这样,我需要另一种方法来跟踪连接进度。

【问题讨论】:

只是在这里大声思考......如果不是启动和监视 PSExec,而是启动 CMD,然后在 CMD 中启动 PSExec。难道你不能从 CMD 中获取输出,这将具有 PSexec 的输出吗? @blaze_125 奇怪的是,我没有看到应用程序的行为有任何显着差异。 【参考方案1】:

似乎一切都归结为速度。如果进程运行得太快,则不会引发事件。见下文。

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:\***QuestionsAndAnswers\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();
        
    

【讨论】:

很遗憾,您的建议并未解决问题。调用和从属应用程序都是 GUI 的,因此添加延迟的影响很小。我会更新问题以反映这一点。 由于它有GUI,我的测试应用没有,如果你使用proc.WaitForInputIdle();而不是混蛋延迟呢? 结果看起来和我一样,虽然当我杀死从应用程序时,你添加的 proc.exited 事件也没有被调用。【参考方案2】:

我花了一些时间,但我找到了解决方案,我希望是这样。

获得输出的唯一方法是通过 CMD 回显结果。

我这样做是为了远程获取可用网站的信息,它只返回 PSExec 文本,并没有返回 AppCmd.exe(或 psexesvc)返回的文本。

我找到了2个解决方案来获取数据。(字符串被转义,因为有一个带引号的小游戏)

[Hard Way] 使用该命令在远程计算机或本地计算机上创建一个文本文件** r 然后检查我要查找的内容是否存在 "\\\\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\""

希望这有帮助,享受吧。

【讨论】:

感谢您发布解决方案,尤其是在这么长时间之后。很抱歉花了这么长时间来尝试您的解决方案。不幸的是,这些都不适用于我的特殊情况。由于 Slave 应用程序是 WInform,因此必须使用 -i 选项,如果它是命令的一部分,它还会向从属用户显示 cmd。这使从属用户可以访问比他们应该拥有的更高的权限。重定向似乎也没有捕获 PsExec 的输出

以上是关于远程启动应用程序时无法从 psexec 接收异步输出的主要内容,如果未能解决你的问题,请参考以下文章

通过 PsExec 时程序输出丢失

如何使用 psexec 向远程计算机上启动的 cmd.exe 提供多个命令

VBScript 和 PSEXEC - 远程执行应用程序

如何使用 WMIC 从远程服务器启动应用程序

从 SSH linux 在远程窗口上启动应用程序 GUI [关闭]

执行 PSEXEC 时出错