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
            public bool WaitAndRead(WaitHandle hProcess, Action <string> results)
            {
                bool  R = false;
                char *b = null; const int bLen = 7900;
                var   ev = new ManualResetEvent(false);

                try {
                    var ha = new WaitHandle[2] {
                        ev, hProcess
                    };
                    for (bool useSB = false; ; useSB = results == null)
                    {
                        var o = new Api.OVERLAPPED {
                            hEvent = ev.SafeWaitHandle.DangerousGetHandle()
                        };
                        if (!Api.ConnectNamedPipe(_hPipe, &o))
                        {
                            int e = lastError.code;
                            if (e != Api.ERROR_PIPE_CONNECTED)
                            {
                                if (e != Api.ERROR_IO_PENDING)
                                {
                                    break;
                                }
                                int wr = WaitHandle.WaitAny(ha);
                                if (wr != 0)
                                {
                                    Api.CancelIo(_hPipe); R = true; break;
                                }                                                                                       //task ended
                                if (!Api.GetOverlappedResult(_hPipe, ref o, out _, false))
                                {
                                    Api.DisconnectNamedPipe(_hPipe); break;
                                }
                            }
                        }

                        if (b == null)
                        {
                            b = (char *)MemoryUtil.Alloc(bLen);
                        }
                        bool readOK;
                        while (((readOK = Api.ReadFile(_hPipe, b, bLen, out int n, null)) || (lastError.code == Api.ERROR_MORE_DATA)) && n > 0)
                        {
                            n /= 2;
                            if (!readOK)
                            {
                                useSB = true;
                            }
                            if (useSB)                               //rare
                            {
                                _sb ??= new StringBuilder(bLen);
                                if (results == null && _s != null)
                                {
                                    _sb.Append(_s);
                                }
                                _s = null;
                                _sb.Append(b, n);
                            }
                            else
                            {
                                _s = new string(b, 0, n);
                            }
                            if (readOK)
                            {
                                if (results != null)
                                {
                                    results(ResultString);
                                    _sb?.Clear();
                                }
                                break;
                            }
                            //note: MSDN says must use OVERLAPPED with ReadFile too, but works without it.
                        }
                        Api.DisconnectNamedPipe(_hPipe);
                        if (!readOK)
                        {
                            break;
                        }
                    }
                }
                finally {
                    ev.Dispose();
                    MemoryUtil.Free(b);
                }
                return(R);
            }