Не удается получить асинхронный вывод от psexec при удаленном запуске приложения

Проблема

Выполнение PSExec как процесса из C# с включенным асинхронным перенаправлением Обработчики событий, полученные моими данными, никогда не вызываются.

Контекст

У меня есть приложение С#, предназначенное для подключения и управления подчиненным приложением на нескольких других компьютерах в той же локальной сети. И основное приложение, и подчиненные приложения являются приложениями Windows Forms. Одной из функций моего приложения является возможность удаленного запуска ведомого приложения. Поскольку это требует некоторого времени, я надеялся использовать вывод 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 - Удаленное выполнение процессов Copyright (C) 2001-2016 Марк Руссинович Sysinternals - www.sysinternals.com

Запуск службы PSEXESVC на ComName...

Последняя строка удаляется после установления соединения. Если это важно сейчас, обработчики событий определены следующим образом:

    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, это было бы мило.

Потенциальный потенциальный клиент 08.03.17

Сегодня я разговаривал с представителями по адресу https://www.poweradmin.com/paexec/. , в частности представитель по имени Дэвид. Там версия PSExec (PAExec) использует команды WriteConsole для записи вывода, который я пытаюсь получить. Таким образом, этот вывод не записывается ни в stderr, ни в stdout, а напрямую в экранный буфер консоли. Очевидно, это ни о чем не говорит PSExec, так как они не одни и те же люди. Но это предполагает, что это потенциально моя проблема. Если это так, мне нужен другой способ отслеживать процесс подключения.


person NewMario64    schedule 16.02.2017    source источник
comment
Просто мысли вслух... что, если вместо запуска и мониторинга PSExec запустить CMD, а в CMD запустить PSExec. Разве вы не сможете получить вывод из CMD, который будет иметь вывод PSexec?   -  person blaze_125    schedule 09.03.2017
comment
@blaze_125 Как ни странно, я не вижу заметной разницы в поведении приложения.   -  person NewMario64    schedule 09.03.2017


Ответы (2)


Кажется, все упирается в скорость. Если процесс выполняется слишком быстро, события не возникают. Увидеть ниже.

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();
        }
    }
}
person blaze_125    schedule 09.03.2017
comment
К сожалению, ваше предложение не решило проблему. и вызывающее, и ведомое приложения являются графическими интерфейсами, поэтому добавление задержки оказало минимальное влияние. Я обновлю вопрос, чтобы отразить это. - person NewMario64; 09.03.2017
comment
Поскольку у него есть графический интерфейс, а в моем тестовом приложении его нет, что, если вы используете proc.WaitForInputIdle(); вместо ублюдочной задержки? - person blaze_125; 09.03.2017
comment
Результаты выглядят идентичными для меня, хотя, когда я убиваю подчиненное приложение, добавленное вами событие proc.exited также не вызывается. - person NewMario64; 09.03.2017

Мне потребовалось некоторое время, но я нашел решение, надеюсь, это так.

Единственный способ получить результат простым способом — повторить результат через CMD.

Я сделал это, чтобы удаленно получить информацию о доступных веб-сайтах, которые возвращали только текст PSExec и не возвращали текст, который возвращает AppCmd.exe (или psexesvc).

Есть 2 решения, которые я нашел для получения данных. (Строка экранирована, потому что есть небольшая игра с кавычками)

[Сложный способ] Используйте команду, чтобы создать текстовый файл на удаленном или локальном компьютере** 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\""

Надеюсь, это поможет, наслаждайтесь.

person Shlomi Bazel    schedule 19.12.2017
comment
Спасибо за публикацию решения, особенно после всего этого времени. И извините, что так долго не пробовал ваше решение. К сожалению, ни один из них не работает в моем конкретном случае. Поскольку подчиненное приложение является WInform, необходимо использовать параметр -i, который также показывает cmd подчиненному пользователю, если он является частью команды. Это дает ведомому пользователю доступ к более высоким разрешениям, чем они должны иметь. Также перенаправление, похоже, не захватывает вывод из PsExec. - person NewMario64; 08.01.2018