Exemplo n.º 1
0
        static unsafe int _RunConsole(Action <string> outAction, StringBuilder outStr, string exe, string args, string curDir, Encoding encoding, bool needLines)
        {
            exe = _NormalizeFile(true, exe, out _, out _);
            //args = pathname.expand(args); //rejected

            encoding ??= Console.OutputEncoding;             //fast. Default is an internal type System.Text.OSEncoding that wraps API GetConsoleOutputCP.
            var decoder = encoding.GetDecoder();             //ensures we'll not get partial multibyte chars (UTF8 etc) at buffer end/start

            var ps = new ProcessStarter_(exe, args, curDir, rawExe: true);

            Handle_ hProcess = default;
            var     sa       = new Api.SECURITY_ATTRIBUTES(null)
            {
                bInheritHandle = 1
            };

            if (!Api.CreatePipe(out Handle_ hOutRead, out Handle_ hOutWrite, sa, 0))
            {
                throw new AuException(0);
            }

            byte *        b  = null;    //buffer before decoding
            char *        c  = null;    //buffer after decoding
            StringBuilder sb = null;    //holds part of line when buffer does not end with newline

            try {
                Api.SetHandleInformation(hOutRead, 1, 0);                 //remove HANDLE_FLAG_INHERIT

                ps.si.dwFlags   |= Api.STARTF_USESTDHANDLES | Api.STARTF_USESHOWWINDOW;
                ps.si.hStdOutput = hOutWrite;
                ps.si.hStdError  = hOutWrite;
                ps.flags        |= Api.CREATE_NEW_CONSOLE;

                if (!ps.StartL(out var pi, inheritHandles: true))
                {
                    throw new AuException(0);
                }
                hOutWrite.Dispose();                 //important: must be here
                pi.hThread.Dispose();
                hProcess = pi.hProcess;

                //native console API allows any buffer size when writing, but wrappers usually use small buffer, eg .NET 4-5 KB, msvcrt 5 KB, C++ not tested
                const int bSize = 8000, cSize = bSize + 10;
                b = MemoryUtil.Alloc(bSize);

                for (bool skipN = false; ;)
                {
                    if (Api.ReadFile(hOutRead, b, bSize, out int nr))
                    {
                        if (nr == 0)
                        {
                            continue;
                        }
                    }
                    else
                    {
                        if (lastError.code != Api.ERROR_BROKEN_PIPE)
                        {
                            throw new AuException(0);
                        }
                        //process ended
                        if (sb != null && sb.Length > 0)
                        {
                            outAction(sb.ToString());
                        }
                        break;
                    }

                    if (c == null)
                    {
                        c = MemoryUtil.Alloc <char>(cSize);
                    }
                    int nc = decoder.GetChars(b, nr, c, cSize, false);

                    if (needLines)
                    {
                        var k = new Span <char>(c, nc);
                        if (skipN)
                        {
                            skipN = false;
                            if (c[0] == '\n' && nc > 0)
                            {
                                k = k[1..];                                                     //\r\n split in 2 buffers
Exemplo n.º 2
0
        static unsafe int _RunConsole(Action <string> outAction, StringBuilder outStr, string exe, string args, string curDir, Encoding encoding)
        {
            exe = _NormalizeFile(true, exe, out _, out _);
            //args = APath.ExpandEnvVar(args); //rejected

            var ps = new ProcessStarter_(exe, args, curDir, rawExe: true);

            Handle_ hProcess = default;
            var     sa       = new Api.SECURITY_ATTRIBUTES(null)
            {
                bInheritHandle = 1
            };

            if (!Api.CreatePipe(out Handle_ hOutRead, out Handle_ hOutWrite, sa, 0))
            {
                throw new AuException(0);
            }

            byte *b = null; char *c = null;

            try {
                Api.SetHandleInformation(hOutRead, 1, 0);                 //remove HANDLE_FLAG_INHERIT

                ps.si.dwFlags   |= Api.STARTF_USESTDHANDLES | Api.STARTF_USESHOWWINDOW;
                ps.si.hStdOutput = hOutWrite;
                ps.si.hStdError  = hOutWrite;
                ps.flags        |= Api.CREATE_NEW_CONSOLE;

                if (!ps.StartLL(out var pi, inheritHandles: true))
                {
                    throw new AuException(0);
                }
                hOutWrite.Dispose();                 //important: must be here
                pi.hThread.Dispose();
                hProcess = pi.hProcess;

                //variables for 'prevent getting partial lines'
                bool needLines = outStr == null /*&& !flags.Has(RCFlags.RawText)*/;
                int  offs = 0; bool skipN = false;

                int bSize = 8000;
                b = (byte *)AMemory.Alloc(bSize);

                for (bool ended = false; !ended;)
                {
                    if (bSize - offs < 1000)                      //part of 'prevent getting partial lines' code
                    {
                        b = (byte *)AMemory.ReAlloc(b, bSize *= 2);
                        AMemory.Free(c); c = null;
                    }

                    if (Api.ReadFile(hOutRead, b + offs, bSize - offs, out int nr))
                    {
                        if (nr == 0)
                        {
                            continue;
                        }
                        nr += offs;
                    }
                    else
                    {
                        if (ALastError.Code != Api.ERROR_BROKEN_PIPE)
                        {
                            throw new AuException(0);
                        }
                        //process ended
                        if (offs == 0)
                        {
                            break;
                        }
                        nr    = offs;
                        offs  = 0;
                        ended = true;
                    }

                    //prevent getting partial lines. They can be created by the console program, or by the above code when buffer too small.
                    int moveFrom = 0;
                    if (needLines)
                    {
                        if (skipN)                          //if was split between \r and \n, remove \n now
                        {
                            skipN = false;
                            if (b[0] == '\n')
                            {
                                Api.memmove(b, b + 1, --nr);
                            }
                            if (nr == 0)
                            {
                                continue;
                            }
                        }
                        int i;
                        for (i = nr; i > 0; i--)
                        {
                            var k = b[i - 1]; if (k == '\n' || k == '\r')
                            {
                                break;
                            }
                        }
                        if (i == nr)                          //ends with \n or \r
                        {
                            offs = 0;
                            if (b[--nr] == '\r')
                            {
                                skipN = true;
                            }
                            else if (nr > 0 && b[nr - 1] == '\r')
                            {
                                nr--;
                            }
                        }
                        else if (i > 0)                            //contains \n or \r
                        {
                            moveFrom = i;
                            offs     = nr - i;
                            if (b[--i] == '\n' && i > 0 && b[i - 1] == '\r')
                            {
                                i--;
                            }
                            nr = i;
                        }
                        else if (!ended)
                        {
                            offs = nr;
                            continue;
                        }
                    }

                    if (c == null)
                    {
                        c = (char *)AMemory.Alloc(bSize * 2);
                    }
                    if (encoding == null)
                    {
                        if ((encoding = s_oemEncoding) == null)
                        {
                            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                            var oemCP = Api.GetOEMCP();
                            try { encoding = Encoding.GetEncoding(oemCP); }
                            catch { encoding = Encoding.GetEncoding(437); }
                            s_oemEncoding = encoding;
                        }
                    }
                    int nc = encoding.GetChars(b, nr, c, bSize);

                    if (moveFrom > 0)
                    {
                        Api.memmove(b, b + moveFrom, offs);                                  //part of 'prevent getting partial lines' code
                    }
                    var s = new string(c, 0, nc);
                    if (needLines)
                    {
                        if (s.FindAny("\r\n") < 0)
                        {
                            outAction(s);
                        }
                        else
                        {
                            foreach (var k in s.Segments(SegSep.Line))
                            {
                                outAction(s[k.start..k.end]);