Beispiel #1
0
        /// <summary>
        /// Starts blocking.
        /// </summary>
        /// <exception cref="ArgumentException"><i>what</i> is 0.</exception>
        /// <exception cref="InvalidOperationException">Already started.</exception>
        public void Start(BIEvents what)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(AInputBlocker));
            }
            if (_block != 0)
            {
                throw new InvalidOperationException();
            }
            if (!what.HasAny(BIEvents.All))
            {
                throw new ArgumentException();
            }

            _block     = what;
            _startTime = ATime.WinMilliseconds;

            _syncEvent    = Api.CreateEvent(false);
            _stopEvent    = Api.CreateEvent(false);
            _threadHandle = Api.OpenThread(Api.SYNCHRONIZE, false, _threadId = Api.GetCurrentThreadId());

            ThreadPool.QueueUserWorkItem(_this => (_this as AInputBlocker)._ThreadProc(), this);
            //SHOULDDO: what if thread pool is very busy? Eg if scripts use it incorrectly. Maybe better have own internal pool.

            Api.WaitForSingleObject(_syncEvent, Timeout.Infinite);
            GC.KeepAlive(this);
        }
Beispiel #2
0
        /// <summary>
        /// Gets process executable file name (like "notepad.exe") or full path.
        /// Returns null if fails.
        /// </summary>
        /// <param name="processId">Process id.</param>
        /// <param name="fullPath">
        /// Get full path.
        /// Note: Fails to get full path if the process belongs to another user session, unless current process is running as administrator; also fails to get full path of some system processes.
        /// </param>
        /// <param name="noSlowAPI">When the fast API QueryFullProcessImageName fails, don't try to use another much slower API WTSEnumerateProcesses. Not used if <i>fullPath</i> is true.</param>
        /// <remarks>
        /// This function is much slower than getting window name or class name.
        /// </remarks>
        /// <seealso cref="wnd.ProgramName"/>
        /// <seealso cref="wnd.ProgramPath"/>
        /// <seealso cref="wnd.ProcessId"/>
        public static string getName(int processId, bool fullPath = false, bool noSlowAPI = false)
        {
            if (processId == 0)
            {
                return(null);
            }
            string R = null;

            //var t = perf.mcs;
            //if(s_time != 0) print.it(t - s_time);
            //s_time = t;

            using var ph = Handle_.OpenProcess(processId);
            if (!ph.Is0)
            {
                //In non-admin process fails if the process is of another user session.
                //Also fails for some system processes: nvvsvc, nvxdsync, dwm. For dwm fails even in admin process.

                //getting native path is faster, but it gets like "\Device\HarddiskVolume5\Windows\System32\notepad.exe" and I don't know API to convert to normal
                if (_QueryFullProcessImageName(ph, !fullPath, out var s))
                {
                    R = s;
                    if (pathname.IsPossiblyDos_(R))
                    {
                        if (fullPath || _QueryFullProcessImageName(ph, false, out s))
                        {
                            R = pathname.ExpandDosPath_(s);
                            if (!fullPath)
                            {
                                R = _GetFileName(R);
                            }
                        }
                    }
                }
            }
            else if (!noSlowAPI && !fullPath)
            {
                //the slow way. Can get only names, not paths.
                using (var p = new AllProcesses_(false)) {
                    for (int i = 0; i < p.Count; i++)
                    {
                        if (p.Id(i) == processId)
                        {
                            R = p.Name(i, cannotOpen: true);
                            break;
                        }
                    }
                }
                //TEST: NtQueryInformationProcess, like in getCommandLine.
            }

            return(R);

            //Would be good to cache process names here. But process id can be reused quickly. Use GetNameCached_ instead.
            //	tested: a process id is reused after creating ~100 processes (and waiting until exits). It takes ~2 s.
            //	The window finder is optimized to call this once for each process and not for each window.
        }
Beispiel #3
0
 public _Preloaded(int index)
 {
     pipeName = $@"\\.\pipe\Au.Task-{Api.GetCurrentProcessId()}-{index}";
     hPipe    = Api.CreateNamedPipe(pipeName,
                                    Api.PIPE_ACCESS_OUTBOUND | Api.FILE_FLAG_OVERLAPPED, //use async pipe because editor would hang if task process exited without connecting. Same speed.
                                    Api.PIPE_TYPE_MESSAGE | Api.PIPE_REJECT_REMOTE_CLIENTS,
                                    1, 0, 0, 0, null);
     overlappedEvent = Api.CreateEvent(false);
 }
Beispiel #4
0
            public bool Init()
            {
                var tid = Api.GetCurrentThreadId();

                pipeName = @"\\.\pipe\Au.CL-" + tid.ToString();                                    //will send this string to the task
                _hPipe   = Api.CreateNamedPipe(pipeName,
                                               Api.PIPE_ACCESS_INBOUND | Api.FILE_FLAG_OVERLAPPED, //use async pipe because also need to wait for task process exit
                                               Api.PIPE_TYPE_MESSAGE | Api.PIPE_READMODE_MESSAGE | Api.PIPE_REJECT_REMOTE_CLIENTS,
                                               1, 0, 0, 0, Api.SECURITY_ATTRIBUTES.ForPipes);
                return(!_hPipe.Is0);
            }
Beispiel #5
0
        void _Alloc(int pid, AWnd w, int nBytes)
        {
            string     err;
            const uint fl = Api.PROCESS_VM_OPERATION | Api.PROCESS_VM_READ | Api.PROCESS_VM_WRITE;

            _hproc = w.Is0 ? Handle_.OpenProcess(pid, fl) : Handle_.OpenProcess(w, fl);
            if (_hproc.Is0)
            {
                err = "Failed to open process handle."; goto ge;
            }

            if (nBytes != 0)
            {
                Mem = Api.VirtualAllocEx(_HprocHR, default, nBytes);
Beispiel #6
0
 /// <summary>
 /// Gets DPI awareness of a window.
 /// </summary>
 /// <returns>Returns <b>Awareness.Invalid</b> if failed.</returns>
 /// <param name="w">A top-level window or control. Can belong to any process.</param>
 /// <remarks>
 /// Works best on Windows 10 1607 and later; uses API <msdn>GetWindowDpiAwarenessContext</msdn>.
 /// On Windows 8.1 returns <b>Awareness.PerMonitor</b> if <i>w</i> is of this process; else uses API <msdn>GetProcessDpiAwareness</msdn>, which is slower and less reliable.
 /// On Windows 7 and 8.0 always returns <b>System</b>, because there are no Windows API.
 /// </remarks>
 public static Awareness WindowDpiAwareness(wnd w)
 {
     if (osVersion.minWin10_1607)
     {
         return(Api.GetAwarenessFromDpiAwarenessContext(Api.GetWindowDpiAwarenessContext(w)));
     }
     else if (osVersion.minWin8_1)
     {
         if (w.IsOfThisProcess)
         {
             return(Awareness.PerMonitor);
         }
         using var hp = Handle_.OpenProcess(w);
         return((!hp.Is0 && 0 == Api.GetProcessDpiAwareness(hp, out var a)) ? a : Awareness.Invalid);
     }
     else
     {
         return(Awareness.System);
         //could use, IsWindowVirtualized (except if of this process), but slow and unreliable.
     }
 }
Beispiel #7
0
        /// <summary>
        /// Runs a message loop.
        /// </summary>
        public unsafe void Loop()
        {
            bool isForms = 0 != (1 & Assembly_.IsLoadedFormsWpf());

            using (isForms ? new EnsureWindowsFormsSynchronizationContext_(true) : null) {
                _loopEndEvent = Api.CreateEvent(true);
                try {
                    _DoEvents();

                    for (; ;)
                    {
                        IntPtr ev = _loopEndEvent;
                        int    k  = Api.MsgWaitForMultipleObjectsEx(1, &ev, 1000, Api.QS_ALLINPUT, Api.MWMO_INPUTAVAILABLE);
                        if (k == Api.WAIT_TIMEOUT)
                        {
                            continue;                                               //previously timeout was used to support Thread.Abort. It is disabled in Core, but maybe still safer with a timeout.
                        }
                        _DoEvents();
                        if (k == Api.WAIT_OBJECT_0 || k == Api.WAIT_FAILED)
                        {
                            break;                                                                        //note: this is after DoEvents because may be posted messages when stopping loop. Although it seems that MsgWaitForMultipleObjects returns events after all messages.
                        }
                        if (Api.PeekMessage(out var _, default, Api.WM_QUIT, Api.WM_QUIT, Api.PM_NOREMOVE))
Beispiel #8
0
        /// <summary>
        /// Runs/opens a program, document, directory (folder), URL, new email, Control Panel item etc.
        /// The returned <see cref="RResult"/> variable contains some process info - process id etc.
        /// </summary>
        /// <param name="file">
        /// Examples:
        /// - <c>@"C:\file.txt"</c>
        /// - <c>folders.Documents</c>
        /// - <c>folders.System + "notepad.exe"</c>
        /// - <c>@"%folders.System%\notepad.exe"</c>
        /// - <c>@"%TMP%\file.txt"</c>
        /// - <c>"notepad.exe"</c>
        /// - <c>@"..\folder\x.exe"</c>
        /// - <c>"http://a.b.c/d"</c>
        /// - <c>"file:///path"</c>
        /// - <c>"mailto:[email protected]"</c>
        /// - <c>":: ITEMIDLIST"</c>
        /// - <c>@"shell:::{CLSID}"</c>
        /// - <c>@"shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"</c>.
        /// More info in Remarks.
        /// </param>
        /// <param name="args">
        /// Command line arguments.
        /// This function expands environment variables if starts with <c>"%"</c> or <c>"\"%"</c>.
        /// </param>
        /// <param name="flags"></param>
        /// <param name="dirEtc">
        /// Allows to specify more parameters: current directory, verb, etc.
        /// If string, it sets initial current directory for the new process. If "", gets it from <i>file</i>. More info: <see cref="ROptions.CurrentDirectory"/>.
        /// </param>
        /// <exception cref="ArgumentException">Used both <b>ROptions.Verb</b> and <b>RFlags.Admin</b> and this process isn't admin.</exception>
        /// <exception cref="AuException">Failed. For example, the file does not exist.</exception>
        /// <remarks>
        /// It works like when you double-click a file icon. It may start new process or not. For example it may just activate window if the program is already running.
        /// Uses API <msdn>ShellExecuteEx</msdn>.
        /// Similar to <see cref="Process.Start(string, string)"/>.
        ///
        /// The <i>file</i> parameter can be:
        /// - Full path of a file or directory. Examples: <c>@"C:\file.txt"</c>, <c>folders.Documents</c>, <c>folders.System + "notepad.exe"</c>, <c>@"%folders.System%\notepad.exe"</c>.
        /// - Filename of a file or directory, like <c>"notepad.exe"</c>. The function calls <see cref="filesystem.searchPath"/>.
        /// - Path relative to <see cref="folders.ThisApp"/>. Examples: <c>"x.exe"</c>, <c>@"subfolder\x.exe"</c>, <c>@".\subfolder\x.exe"</c>, <c>@"..\another folder\x.exe"</c>.
        /// - URL. Examples: <c>"https://www.example.com"</c>, <c>"file:///path"</c>.
        /// - Email, like <c>"mailto:[email protected]"</c>. Subject, body etc also can be specified, and Google knows how.
        /// - Shell object's ITEMIDLIST like <c>":: ITEMIDLIST"</c>. See <see cref="Pidl.ToHexString"/>, <see cref="folders.shell"/>. Can be used to open virtual folders and items like Control Panel.
        /// - Shell object's parsing name, like <c>@"shell:::{CLSID}"</c> or <c>@"::{CLSID}"</c>. See <see cref="Pidl.ToShellString"/>. Can be used to open virtual folders and items like Control Panel.
        /// - To run a Windows Store App, use <c>@"shell:AppsFolder\WinStoreAppId"</c> format. Examples: <c>@"shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"</c>, <c>@"shell:AppsFolder\windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel"</c>. To discover the string use hotkey Ctrl+Shift+Q or function <see cref="WndUtil.GetWindowsStoreAppId"/> or Google.
        ///
        /// Supports environment variables, like <c>@"%TMP%\file.txt"</c>. See <see cref="pathname.expand"/>.
        /// </remarks>
        /// <seealso cref="wnd.find"/>
        /// <seealso cref="wnd.findOrRun"/>
        /// <seealso cref="wnd.runAndFind"/>
        /// <example>
        /// Run Notepad and wait for an active Notepad window.
        /// <code><![CDATA[
        /// run.it("notepad.exe");
        /// 1.s();
        /// wnd w = wnd.wait(10, true, "*- Notepad", "Notepad");
        /// ]]></code>
        /// Run Notepad or activate a Notepad window.
        /// <code><![CDATA[
        /// wnd w = wnd.findOrRun("*- Notepad", run: () => run.it("notepad.exe"));
        /// ]]></code>
        /// Run File Explorer and wait for new folder window. Ignores matching windows that already existed.
        /// <code><![CDATA[
        /// var w = wnd.runAndFind(
        ///     () => run.it(@"explorer.exe"),
        ///     10, cn: "CabinetWClass");
        /// ]]></code>
        /// </example>
        public static RResult it(string file, string args = null, RFlags flags = 0, ROptions dirEtc = null)
        {
            Api.SHELLEXECUTEINFO x = default;
            x.cbSize = Api.SizeOf(x);
            x.fMask  = Api.SEE_MASK_NOZONECHECKS | Api.SEE_MASK_NOASYNC | Api.SEE_MASK_CONNECTNETDRV | Api.SEE_MASK_UNICODE;
            x.nShow  = Api.SW_SHOWNORMAL;

            bool curDirFromFile = false;
            var  more           = dirEtc;

            if (more != null)
            {
                x.lpVerb = more.Verb;
                if (x.lpVerb != null)
                {
                    x.fMask |= Api.SEE_MASK_INVOKEIDLIST;                                   //makes slower. But verbs are rarely used.
                }
                if (more.CurrentDirectory is string cd)
                {
                    if (cd.Length == 0)
                    {
                        curDirFromFile = true;
                    }
                    else
                    {
                        cd = pathname.expand(cd);
                    }
                    x.lpDirectory = cd;
                }

                if (!more.OwnerWindow.IsEmpty)
                {
                    x.hwnd = more.OwnerWindow.Hwnd.Window;
                }

                switch (more.WindowState)
                {
                case ProcessWindowStyle.Hidden: x.nShow = Api.SW_HIDE; break;

                case ProcessWindowStyle.Minimized: x.nShow = Api.SW_SHOWMINIMIZED; break;

                case ProcessWindowStyle.Maximized: x.nShow = Api.SW_SHOWMAXIMIZED; break;
                }

                x.fMask &= ~more.FlagsRemove;
                x.fMask |= more.FlagsAdd;
            }

            if (flags.Has(RFlags.Admin))
            {
                if (x.lpVerb == null || x.lpVerb.Eqi("runas"))
                {
                    x.lpVerb = "runas";
                }
                else if (!uacInfo.isAdmin)
                {
                    throw new ArgumentException("Cannot use Verb with flag Admin, unless this process is admin");
                }
            }

            file = _NormalizeFile(false, file, out bool isFullPath, out bool isShellPath);
            Pidl pidl = null;

            if (isShellPath)                  //":: ITEMIDLIST" or "::{CLSID}..." (we convert it too because the API does not support many)
            {
                pidl = Pidl.FromString(file); //does not throw
                if (pidl != null)
                {
                    x.lpIDList = pidl.UnsafePtr;
                    x.fMask   |= Api.SEE_MASK_INVOKEIDLIST;
                }
                else
                {
                    x.lpFile = file;
                }
            }
            else
            {
                x.lpFile = file;

                if (curDirFromFile && isFullPath)
                {
                    x.lpDirectory = pathname.getDirectory(file);
                }
            }
            x.lpDirectory ??= Directory.GetCurrentDirectory();
            if (!args.NE())
            {
                x.lpParameters = pathname.expand(args);
            }

            if (0 == (flags & RFlags.ShowErrorUI))
            {
                x.fMask |= Api.SEE_MASK_FLAG_NO_UI;
            }
            if (0 == (flags & RFlags.WaitForExit))
            {
                x.fMask |= Api.SEE_MASK_NO_CONSOLE;
            }
            if (0 != (flags & RFlags.MostUsed))
            {
                x.fMask |= Api.SEE_MASK_FLAG_LOG_USAGE;
            }
            x.fMask |= Api.SEE_MASK_NOCLOSEPROCESS;

            WndUtil.EnableActivate(-1);

            bool waitForExit = 0 != (flags & RFlags.WaitForExit);
            bool needHandle  = flags.Has(RFlags.NeedProcessHandle);

            bool ok = false; int pid = 0, errorCode = 0;
            bool asUser = !flags.HasAny(RFlags.Admin | RFlags.InheritAdmin) && uacInfo.isAdmin;             //info: new process does not inherit uiAccess

            if (asUser)
            {
                ok = Cpp.Cpp_ShellExec(x, out pid, out int injectError, out int execError);
                if (!ok)
                {
                    if (injectError != 0)
                    {
                        print.warning("Failed to run as non-admin.");
                        asUser = false;
                    }
                    else
                    {
                        errorCode = execError;
                    }
                }
            }
            if (!asUser)
            {
                ok = Api.ShellExecuteEx(ref x);
                if (!ok)
                {
                    errorCode = lastError.code;
                }
            }
            pidl?.Dispose();
            if (!ok)
            {
                throw new AuException(errorCode, $"*run '{file}'");
            }

            var         R  = new RResult();
            WaitHandle_ ph = null;

            if (needHandle || waitForExit)
            {
                if (pid != 0)
                {
                    x.hProcess = Handle_.OpenProcess(pid, Api.PROCESS_ALL_ACCESS);
                }
                if (!x.hProcess.Is0)
                {
                    ph = new WaitHandle_(x.hProcess, true);
                }
            }

            if (!waitForExit)
            {
                if (pid != 0)
                {
                    R.ProcessId = pid;
                }
                else if (!x.hProcess.Is0)
                {
                    R.ProcessId = process.processIdFromHandle(x.hProcess);
                }
            }

            try {
                Api.AllowSetForegroundWindow();

                if (x.lpVerb != null && Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
                {
                    Thread.CurrentThread.Join(50);                     //need min 5-10 for file Properties. And not Sleep.
                }
                if (ph != null)
                {
                    if (waitForExit)
                    {
                        ph.WaitOne();
                        if (Api.GetExitCodeProcess(x.hProcess, out var exitCode))
                        {
                            R.ProcessExitCode = exitCode;
                        }
                    }
                    if (needHandle)
                    {
                        R.ProcessHandle = ph;
                    }
                }
            }
            finally {
                if (R.ProcessHandle == null)
                {
                    if (ph != null)
                    {
                        ph.Dispose();
                    }
                    else
                    {
                        x.hProcess.Dispose();
                    }
                }
            }

            return(R);

            //tested: works well in MTA thread.
            //rejected: in QM2, run also has a 'window' parameter. However it just makes limited, unclear etc, and therefore rarely used. Instead use wnd.findOrRun or Find/Run/Wait like in the examples.
            //rejected: in QM2, run also has 'autodelay'. Better don't add such hidden things. Let the script decide what to do.
        }
Beispiel #9
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
Beispiel #10
0
            /// <summary>
            /// Adds the action to the queue and notifies the thread to execute it.
            /// If the thread is busy, returns false; if ifRunning!=0, the action possibly will run later.
            /// </summary>
            public bool RunAction(Action actionWrapper, ActionTrigger trigger)
            {
                if (_disposed)
                {
                    return(false);
                }
                if (_q == null)
                {
                    _q     = new Queue <_Action>();
                    _event = Api.CreateEvent(false);
                    try {
                        AThread.Start(() => {
                            try {
                                while (!_disposed && 0 == Api.WaitForSingleObject(_event, -1))
                                {
                                    while (!_disposed)
                                    {
                                        _Action x;
                                        lock (_q) {
                                            g1:
                                            if (_q.Count == 0)
                                            {
                                                _running = false; break;
                                            }
                                            x = _q.Dequeue();
                                            if (x.time != 0 && ATime.PerfMilliseconds > x.time)
                                            {
                                                goto g1;
                                            }
                                            _running = true;
                                        }
                                        x.actionWrapper();
                                    }
                                }
                            }
                            finally {
                                _event.Dispose();
                                _q = null; _running = false;                                 //restart if aborted
                                //AOutput.Write("thread ended");
                            }
                        });
                    }
                    catch (OutOfMemoryException) {                    //too many threads, probably 32-bit process
                        _event.Dispose();
                        _OutOfMemory();
                    }
                }

                bool R = true;

                lock (_q) {
                    int ifRunningWaitMS = trigger.options.ifRunning;
                    if (_running)
                    {
                        if (ifRunningWaitMS == 0)
                        {
                            if (!trigger.options.noWarning)
                            {
                                AOutput.Write("Warning: can't run the trigger action because an action is running in this thread. To run simultaneously or wait, use one of Triggers.Options.RunActionInX functions. To disable this warning: Triggers.Options.RunActionInThread(0, 0, noWarning: true);. Trigger: " + trigger);
                            }
                            return(false);
                        }
                        R = false;
                    }
                    else
                    {
                        _running = true;
                        //if(ifRunningWaitMS > 0 && ifRunningWaitMS < 1000000000) ifRunningWaitMS += 1000;
                    }
                    _q.Enqueue(new _Action {
                        actionWrapper = actionWrapper, time = ifRunningWaitMS <= 0 ? 0 : ATime.PerfMilliseconds + ifRunningWaitMS
                    });
                }
                Api.SetEvent(_event);
                return(R);
            }
Beispiel #11
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]);
Beispiel #12
0
        /// <summary>
        /// Starts UAC Medium integrity level (IL) process from this admin process.
        /// </summary>
        /// <param name="need">Which field to set in <b>Result</b>.</param>
        /// <exception cref="AuException">Failed.</exception>
        /// <remarks>
        /// Actually the process will have the same IL and user session as the shell process (normally explorer).
        /// Fails if there is no shell process (API GetShellWindow fails) for more than 2 s from calling this func.
        /// Asserts and fails if this is not admin/system process. Caller should at first call <see cref="AUac.IsAdmin"/> or <see cref="AUac.IntegrityLevel"/>.
        /// </remarks>
        internal Result StartUserIL(Result.Need need = 0)
        {
            if (s_userToken == null)
            {
                Debug.Assert(AUac.IsAdmin);                 //else cannot set privilege
                if (!ASecurity.SetPrivilege("SeIncreaseQuotaPrivilege", true))
                {
                    goto ge;
                }

                //APerf.First();
#if false //works, but slow, eg 60 ms, even if we don't create task everytime
                var s = $"\"{AFolders.ThisAppBS}{(AVersion.Is32BitProcess ? "32" : "64")}\\AuCpp.dll\",Cpp_RunDll";
                WinTaskScheduler.CreateTaskToRunProgramOnDemand("Au", "rundll32", false, AFolders.System + "rundll32.exe", s);
                //WinTaskScheduler.CreateTaskToRunProgramOnDemand("Au", "rundll32", false, AFolders.System + "notepad.exe"); //slow too
                //APerf.Next();
                int pid = WinTaskScheduler.RunTask("Au", "rundll32");
                //APerf.Next();
                //AOutput.Write(pid);
                var hUserProcess = Handle_.OpenProcess(pid);
                //AOutput.Write((IntPtr)hUserProcess);
                if (hUserProcess.Is0)
                {
                    goto ge;
                }
#else
                bool retry = false;
g1:
                var w = Api.GetShellWindow();
                if (w.Is0)                  //if Explorer process killed or crashed, wait until it restarts
                {
                    if (!AWaitFor.Condition(2, () => !Api.GetShellWindow().Is0))
                    {
                        throw new AuException($"*start process '{_exe}' as user. There is no shell process.");
                    }
                    500.ms();
                    w = Api.GetShellWindow();
                }

                var hUserProcess = Handle_.OpenProcess(w);
                if (hUserProcess.Is0)
                {
                    if (retry)
                    {
                        goto ge;
                    }
                    retry = true; 500.ms(); goto g1;
                }

                //two other ways:
                //1. Enum processes and find one that has Medium IL. Unreliable, eg its token may be modified.
                //2. Start a service process. Let it start a Medium IL process like in QM2. Because LocalSystem can get token with WTSQueryUserToken.
                //tested: does not work with GetTokenInformation(TokenLinkedToken). Even if would work, in non-admin session it is wrong token.
#endif
                //APerf.NW();

                using (hUserProcess) {
                    if (Api.OpenProcessToken(hUserProcess, Api.TOKEN_DUPLICATE, out Handle_ hShellToken))
                    {
                        using (hShellToken) {
                            const uint access = Api.TOKEN_QUERY | Api.TOKEN_ASSIGN_PRIMARY | Api.TOKEN_DUPLICATE | Api.TOKEN_ADJUST_DEFAULT | Api.TOKEN_ADJUST_SESSIONID;
                            if (Api.DuplicateTokenEx(hShellToken, access, null, Api.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, Api.TOKEN_TYPE.TokenPrimary, out var userToken))
                            {
                                s_userToken = new SafeAccessTokenHandle(userToken);
                            }
                        }
                    }
                }
                if (s_userToken == null)
                {
                    goto ge;
                }
            }

            bool suspended = need == Result.Need.NetProcess && !_NetProcessObject.IsFast, resetSuspendedFlag = false;
            if (suspended && 0 == (flags & Api.CREATE_SUSPENDED))
            {
                flags |= Api.CREATE_SUSPENDED; resetSuspendedFlag = true;
            }
            bool ok = Api.CreateProcessWithTokenW(s_userToken.DangerousGetHandle(), 0, null, cl, flags, envVar, curDir, si, out var pi);
            if (resetSuspendedFlag)
            {
                flags &= ~Api.CREATE_SUSPENDED;
            }
            if (!ok)
            {
                goto ge;
            }
            return(new Result(pi, need, suspended));

            ge : throw new AuException(0, $"*start process '{_exe}' as user");
        }