/// <summary> /// Starts process using API CreateProcess or CreateProcessAsUser, without the feedback hourglass cursor. /// </summary> /// <param name="need">Which field to set in <b>Result</b>.</param> /// <param name="inheritUiaccess">If this process has UAC integrity level uiAccess, let the new process inherit it.</param> /// <exception cref="AuException">Failed.</exception> internal Result Start(Result.Need need = 0, bool inheritUiaccess = false) { 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 = StartL(out var pi, inheritUiaccess); if (resetSuspendedFlag) { flags &= ~Api.CREATE_SUSPENDED; } if (!ok) { throw new AuException(0, $"*start process '{_exe}'"); } return(new Result(pi, need, suspended)); }
/// <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="uacInfo.isAdmin"/> or <see cref="uacInfo.IntegrityLevel"/>. /// </remarks> internal Result StartUserIL(Result.Need need = 0) { if (s_userToken == null) { Debug.Assert(uacInfo.isAdmin); //else cannot set privilege if (!SecurityUtil.SetPrivilege("SeIncreaseQuotaPrivilege", true)) { goto ge; } //perf.first(); #if false //works, but slow, eg 60 ms, even if we don't create task everytime var s = $"\"{folders.ThisAppBS}{(osVersion.is32BitProcess ? "32" : "64")}\\AuCpp.dll\",Cpp_RunDll"; WinTaskScheduler.CreateTaskToRunProgramOnDemand("Au", "rundll32", false, folders.System + "rundll32.exe", s); //WinTaskScheduler.CreateTaskToRunProgramOnDemand("Au", "rundll32", false, folders.System + "notepad.exe"); //slow too //perf.next(); int pid = WinTaskScheduler.RunTask("Au", "rundll32"); //perf.next(); //print.it(pid); var hUserProcess = Handle_.OpenProcess(pid); //print.it((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 (!wait.forCondition(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 //perf.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"); }