IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Diskuze: C# Process.StandardOutput bez zavření StandardInput

V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.

Aktivity
Avatar
Dog
Člen
Avatar
Dog:12.6.2017 0:46

Ahoj,
mám problém s komunikací s konzolou jiné aplikace, potřebuji s ní komunikovat "živě" bez zavírání, protože na sebe jednotlivé příkazy navazují s trvají dlouho, potřebuji např.:

-> open 'cesta k souboru'
-> getOption
<- List<string> options
//na základě options volit další kroky s tím, že některé mohou zabrat mnoho času

Takže pro tyhle účely nemohu použít klasické:

_consoleProcess.StandardInput.WriteLine(command);
_consoleProcess.StandardInput.Flush();
//Tady je problém, protože se stream zavře a já začínám zase odzačátku
_consoleProcess.StandardInput.Close();
string[] output = _consoleProcess.StandardOutput.ReadToEnd();
_consoleProcess.WaitForExit();

Protože by se zavřel StreamWriter (_consoleProces­s.StandardInput).

    public class ConsoleCommunication
    {
        public string FilePath { get; set; }
        public string WorkingDirectory { get; set; }
        public bool OpenWindow { get; set; }

        private Process _consoleProcess;

        public ConsoleCommunication(string filePath, string workingDirectory, bool openWindow)
        {
            FilePath = filePath;
            WorkingDirectory = workingDirectory;
            OpenWindow = openWindow;
        }

        public void OpenConnection()
        {
            Process consoleProcess = new Process
            {
                StartInfo =
                {
                    FileName = FilePath,
                    WorkingDirectory = WorkingDirectory,
                    RedirectStandardInput = true,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = OpenWindow,
                    UseShellExecute = false,
                }
            };
            _consoleProcess = consoleProcess;
            _consoleProcess.Start();
        }

        public List<string> SendCommand(string command)
        {
            StreamReader sr = _consoleProcess.StandardOutput;
            StreamWriter sw = _consoleProcess.StandardInput;

            sw.WriteLine(command);
            sw.Flush();
            List<string> output = new List<string>();

            while (sr.Peek() >= -1)
            {
                output.Add(sr.ReadLine());
            }
            return output;
        }
}

Tady když volám SendCommand, tak přečte všechno, ale po posledním až je prázdnej buffer tak zamrzne, když dám do sr.Peek() > -1, tak nezamrzne, ale nepřečte všechen výstup a tím pádem je nepoužitelný.

Díky za každou radu... :)

Editováno 12.6.2017 0:48
 
Odpovědět
12.6.2017 0:46
Avatar
Dog
Člen
Avatar
Dog:12.6.2017 19:46

Nebo jakákoli alternativní metoda by pomohla, nikde na internetu jsem nic o tom nenašel a nechce se mi věřit, že by to nikdo nikdy neřešil (jak posílat série příkazů a na základě výsledků posílat další příkazy) :( Stačilo by jen znovu otevřít StreamReader a StreamWriter toho procesu, za předpokladu, že bych ho zavřel sw.Close() (když to zavřu pomocí Close(), tak funguje samozřejmě správně, ale u dalšího volání SendCommand samozřejmě napíše: System.ObjectDis­posedException: Do zavřeného objektu TextWriter nelze zapisovat.), nebo to prostě nějákým způsobem nechat otevřený, ale zatím jsem nepřišel na způsob, jak to udělat aby to poslalo všechny příkazy z StreamWriteru sw, bez jeho zavření... tím pádem to bohužel nefunguje správně

 
Nahoru Odpovědět
12.6.2017 19:46
Avatar
Odpovídá na Dog
sadlomaslox25:12.6.2017 21:27

to zalezi jestli je na druhe strane tvoje aplikace. pokud ano tak nejjednoduzsi je vytvorit si nejaky protokol a nejjednoduzsi protokol za me je pripravit si veskere data co chci odeslat, prevest je do neceho, co nema radky (JSON, BASE64) a poslat pomoci WriteLine a na druhe strane to nacist pres ReadLine a zrekonstruovat.

pokud je na druhe strane nejaka systemova aplikace tak doporucuju komunikovat bezstavove tzn. kazdy request = nove vytvoreni procesu, provedeni prikazu, nacteni vystupu a zavreni procesu.

pokud ale chces komunikovat stavove, tak zbyva cist stream pres ReadBlock + timeout nebo se chytat nejakeho specialniho symbolu nakonci vystupu (moc nedoporucuju a -1 je pouze kdyz konci cela aplikace), nebo implementovat paralelne cteni a zapis.

 
Nahoru Odpovědět
12.6.2017 21:27
Avatar
Shade
Člen
Avatar
Shade:12.6.2017 23:44

Něco jsem zkusil a tohle vzniklo:

static void Main(string[] args)
        {
            ProcessStartInfo info = new ProcessStartInfo();

            info.RedirectStandardInput = true;
            info.RedirectStandardOutput = true;
            info.RedirectStandardError = true;

            info.UseShellExecute = false;
            info.FileName = "cmd.exe";
            info.CreateNoWindow = true;

            Process proc = new Process();
            proc.OutputDataReceived += (s, e) => Console.WriteLine(e.Data); ;  //async output
            proc.StartInfo = info;
            proc.Start();
            proc.BeginOutputReadLine();


            using (StreamWriter writer = proc.StandardInput)
            {
                string command = "";
                while(command != "exit")
                {
                    command = Console.ReadLine();
                    writer.WriteLine(command);
                }
            }
        }
Nahoru Odpovědět
12.6.2017 23:44
Talk is cheap. Show me the code.
Avatar
Dog
Člen
Avatar
Dog:18.6.2017 9:45

Jsem ted na dovolene, takze tu byly nejake problemy s pripojenim... Funguje to teď, ale nevím jestli je to nějak moc thread save :)

/// <summary>
/// Class for live comunication with console
/// </summary>
public class ConsoleCommunication
{
    private readonly List<string> _outputList = new List<string>();
    private readonly string _respondToErrorCommand;
    private readonly string _errorCommand;
    private Process _consoleProcess;
    private string _inputCommand;
    private bool _semafor;

    /// <summary>
    /// Class for live comunication with console
    /// </summary>
    /// <param name="filePath">Whole path to the console exe</param>
    /// <param name="workingDirectory">Whole path to the working directory of that exe file</param>
    /// <param name="openWindow">Open console window</param>
    /// <param name="errorCommand">Command which console returns like error</param>
    /// <param name="respondToErrorCommand">Whole text of that error</param>
    public ConsoleCommunication(string filePath, string workingDirectory, bool openWindow, string errorCommand,
        string respondToErrorCommand)
    {
        _errorCommand = errorCommand;
        _respondToErrorCommand = respondToErrorCommand;
        FilePath = filePath;
        WorkingDirectory = workingDirectory;
        OpenWindow = openWindow;
    }

    public string FilePath { get; }
    public string WorkingDirectory { get; }
    public bool OpenWindow { get; }

    /// <summary>
    /// Open console connection
    /// </summary>
    public async void OpenConnection()
    {
        var consoleProcess = new Process
        {
            StartInfo =
            {
                FileName = FilePath,
                WorkingDirectory = WorkingDirectory,
                RedirectStandardInput = true,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = OpenWindow,
                UseShellExecute = false
            }
        };
        _consoleProcess = consoleProcess;
        _consoleProcess.OutputDataReceived += (s, e) =>
        {
            _outputList.Add(e.Data);
        };
        _consoleProcess.Start();
        _consoleProcess.BeginOutputReadLine();


        await Task.Run(() =>
        {
            using (var writer = _consoleProcess.StandardInput)
            {
                while (_inputCommand != "exit")
                {
                    if (!_semafor) continue;
                    writer.WriteLine(_inputCommand);
                    writer.WriteLine(_errorCommand);
                    _semafor = false;
                }
            }
        });
    }

    /// <summary>
    /// Send command into the process, returns outputs
    /// </summary>
    /// <param name="command"></param>
    public List<string> SendCommand(string command)
    {
        CheckExceptions();
        _semafor = true;
        _inputCommand = command;

        while (!_outputList.Contains(_respondToErrorCommand))
        {
        }
        string oboustrannazpetnavazba;
        List<string> listTemp = new List<string>(_outputList);
        _outputList.Clear();
        return listTemp;
    }

    /// <summary>
    /// Close connection without closing process
    /// </summary>
    public void CloseConnection()
    {
        _semafor = true;
        _inputCommand = "exit";
    }

    /// <summary>
    /// Close entire process
    /// </summary>
    public void CloseProcess()
    {
        _consoleProcess.Close();
    }

    private void CheckExceptions()
    {
        if (_consoleProcess == null)
        {
            throw new Exception("Process wasn't opened.");
        }
    }
}
 
Nahoru Odpovědět
18.6.2017 9:45
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 5 zpráv z 5.