Terminal: cmd / powershell / pwsh unutar alata

↑ Nazad

Živ shell proces sa preusmerenim ulazom i izlazom.

Detalji koji su morali da se reše

ProblemRešenje
čćžšđ izlaze kao smeće u PowerShell-uNa startu [Console]::OutputEncoding = UTF8 + StandardOutputEncoding = UTF8
BOM na stdin-u pokvari prvu komandunew UTF8Encoding(false)
cmd prompt se lepi za sledeći red izlazaPROMPT=[$P]$_ → folder u svom redu
git pager i boje bez pravog terminalaGIT_PAGER=cat, NO_COLOR=1
PowerShell visi kad nešto traži unos-NonInteractive → greška umesto čekanja

Ograničenja

ShellSession.cs

using System.Diagnostics;
using System.Text;

namespace WinFormsApp_test
{
    /// <summary>
    ///  Živ cmd / powershell / pwsh proces sa preusmerenim ulazom i izlazom.
    ///  GUI → proces: Send() piše u stdin.  Proces → GUI: Output event (sa POZADINSKE niti!).
    /// </summary>
    public sealed class ShellSession : IDisposable
    {
        private readonly Process _proc;

        /// <summary>Svaka linija sa stdout/stderr. Diže se sa pozadinske niti.</summary>
        public event Action<string>? Output;

        /// <summary>Proces je završen (exit, kill). Diže se sa pozadinske niti.</summary>
        public event Action<int>? Exited;

        public string ShellName { get; }
        public bool IsRunning => !_proc.HasExited;

        private ShellSession(string shellName, Process proc)
        {
            ShellName = shellName;
            _proc = proc;
        }

        public static ShellSession Start(string shellName, string workingDirectory)
        {
            // PowerShell se prebacuje na UTF-8, da č/ć/ž/š/đ izađu ispravno.
            // cmd ne ume da čita UTF-8 sa preusmerenog ulaza (bug u cmd-u), pa on radi u OEM kodnoj
            // strani sistema (852/437). Ako je u Windows-u uključen "Beta: UTF-8", cmd naša slova
            // u komandama ne prima nikako → za to koristi powershell.
            var (file, args, initCommand, encoding) = shellName switch
            {
                "cmd" => ("cmd.exe", "/Q", (string?)null, OemEncoding()),
                "powershell" => ("powershell.exe", "-NoLogo -NoProfile -NonInteractive -Command -",
                                 "[Console]::OutputEncoding=[Text.Encoding]::UTF8; $OutputEncoding=[Text.Encoding]::UTF8",
                                 (Encoding)new UTF8Encoding(false)),
                "pwsh" => ("pwsh.exe", "-NoLogo -NoProfile -NonInteractive -Command -",
                           "[Console]::OutputEncoding=[Text.Encoding]::UTF8",
                           new UTF8Encoding(false)),
                _ => throw new ArgumentException($"Nepoznat shell: {shellName}"),
            };

            var psi = new ProcessStartInfo(file, args)
            {
                WorkingDirectory = workingDirectory,
                UseShellExecute = false,
                CreateNoWindow = true,
                RedirectStandardInput = true,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                StandardOutputEncoding = encoding,
                StandardErrorEncoding = encoding,
                StandardInputEncoding = encoding,   // UTF8Encoding(false) = bez BOM-a, inače prva komanda pukne
            };
            // git: bez boja i bez pager-a (nema pravog terminala)
            psi.Environment["GIT_PAGER"] = "cat";
            psi.Environment["NO_COLOR"] = "1";
            // cmd prompt ne završava novim redom, pa bi se zalepio za sledeći izlaz → [folder] u svom redu
            psi.Environment["PROMPT"] = "[$P]$_";

            var proc = new Process { StartInfo = psi, EnableRaisingEvents = true };
            var session = new ShellSession(shellName, proc);

            proc.OutputDataReceived += (_, e) => { if (e.Data != null) session.Output?.Invoke(e.Data); };
            proc.ErrorDataReceived += (_, e) => { if (e.Data != null) session.Output?.Invoke(e.Data); };
            proc.Exited += (_, _) => session.Exited?.Invoke(proc.ExitCode);

            proc.Start();
            proc.BeginOutputReadLine();
            proc.BeginErrorReadLine();
            proc.StandardInput.AutoFlush = true;
            if (initCommand != null) proc.StandardInput.WriteLine(initCommand);
            return session;
        }

        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        private static extern int GetOEMCP();

        private static Encoding OemEncoding()
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);   // 852, 437... nisu ugrađeni u .NET
            int cp = GetOEMCP();
            return cp == 65001 ? new UTF8Encoding(false) : Encoding.GetEncoding(cp);
        }

        /// <summary>GUI → proces: pošalji komandu kao da je otkucana i pritisnut Enter.</summary>
        public void Send(string command)
        {
            if (!IsRunning) throw new InvalidOperationException("Shell nije pokrenut.");
            _proc.StandardInput.WriteLine(command);
        }

        /// <summary>Ubija shell i sve što je on pokrenuo (npr. zaglavljen ping -t).</summary>
        public void Kill()
        {
            if (IsRunning) _proc.Kill(entireProcessTree: true);
        }

        public void Dispose()
        {
            try { Kill(); } catch (InvalidOperationException) { }
            _proc.Dispose();
        }
    }
}

Upotreba

_shell = ShellSession.Start("powershell", @"C:\Users\Kimi\source\repos");
_shell.Output += _termLog.Write;       // pozadina → red → uiTimer → TextBox
_shell.Send("git status");

Izvor: 06-terminal.md