void _ThreadProc() { AHookWin hk = null, hm = null; AHookAcc hwe = null; try { try { if (_block.Has(BIEvents.Keys)) { hk = AHookWin.Keyboard(_keyHookProc ??= _KeyHookProc); } if (_block.HasAny(BIEvents.MouseClicks | BIEvents.MouseMoving)) { hm = AHookWin.Mouse(_mouseHookProc ??= _MouseHookProc); } } catch (AuException e1) { ADebug.Print(e1); _block = 0; return; } //failed to hook //This prevents occassional inserting a foreign key after the first our-script-pressed key. //To reproduce, let our script send small series of chars in loop, and simultaneously a foreign script send other chars. ATime.DoEvents(); //AOutput.Write("started"); Api.SetEvent(_syncEvent); //the acc hook detects Ctrl+Alt+Del, Win+L, UAC consent, etc. SystemEvents.SessionSwitch only Win+L. try { hwe = new AHookAcc(AccEVENT.SYSTEM_DESKTOPSWITCH, 0, _winEventProc ??= _WinEventProc); } catch (AuException e1) { ADebug.Print(e1); } //failed to hook AWaitFor.Wait_(-1, WHFlags.DoEvents, _stopEvent, _threadHandle); if (_blockedKeys != null) { bool onlyUp = _discardBlockedKeys || ATime.WinMilliseconds - _startTime > c_maxResendTime; _blockedKeys.SendBlocked_(onlyUp); } //AOutput.Write("ended"); } finally { _blockedKeys = null; hk?.Dispose(); hm?.Dispose(); hwe?.Dispose(); Api.SetEvent(_syncEvent); } GC.KeepAlive(this); }
/// <summary> /// Waits until the target app gets (Paste) or sets (Copy) clipboard text. /// Throws AuException on timeout (3 s normally, 28 s if the target window is hung). /// </summary> /// <param name="ctrlKey">The variable that was used to send Ctrl+V or Ctrl+C. This function may call Release to avoid too long Ctrl down.</param> public void Wait(ref AKeys.Internal_.SendCopyPaste ctrlKey) { //AOutput.Write(Success); //on Paste often already true, because SendInput dispatches sent messages for (int n = 6; !Success;) //max 3 s (6*500 ms). If hung, max 28 s. { AWaitFor.Wait_(500, WHFlags.DoEvents, null, this); if (Success) { break; } //is hung? if (--n == 0) { throw new AuException(_paste ? "*paste" : "*copy"); } ctrlKey.Release(); _wFocus.SendTimeout(5000, 0, flags: 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"); }