void _Schedule() { var s = _FormatCL(3); if (s == null) { return; } var user = Environment.UserName; string folder = @"Au\" + user /*App.Model.WorkspaceName*/, name = App.Model.CurrentFile.DisplayName; try { if (!WinTaskScheduler.TaskExists(folder, name)) { WinTaskScheduler.CreateTaskWithoutTriggers(folder, name, uacInfo.isAdmin ? UacIL.High : UacIL.Medium, process.thisExePath, s, author: user); } WinTaskScheduler.EditTask(folder, name); } catch (UnauthorizedAccessException) when(!uacInfo.isAdmin) { dialog.showError("Failed", "Restart this program as administrator.", owner: this); } catch (Exception e1) { dialog.showError("Failed", e1.ToStringWithoutStack(), owner: this); } //never mind: non-admin process can't create folders and tasks. // But somehow can do it in the QM2 tasks folder. // Now I don't know how to set folder security permissions. }
static bool _RestartAsAdmin(string[] args) { if (Debugger.IsAttached) { return(false); //very fast } try { //int pid = WinTaskScheduler.RunTask("Au", AFolders.ThisAppBS.Eqi(@"Q:\app\Au\_\") ? "_Au.Editor" : "Au.Editor", //run Q:\app\Au\_\Au.CL.exe or <installed path>\Au.CL.exe true, args); //Api.AllowSetForegroundWindow(pid); //fails and has no sense, because it's Au.CL.exe running as SYSTEM } catch (Exception ex) { //probably this program is not installed (no scheduled task) AOutput.QM2.Write(ex); return(false); } return(true); }
/// <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"); }