/// <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); }
/// <summary> /// Stops blocking. /// Plays back blocked keys if need. See <see cref="ResendBlockedKeys"/>. /// Does nothing if currently is not blocking. /// </summary> /// <param name="discardBlockedKeys">Do not play back blocked key-down events recorded because of <see cref="ResendBlockedKeys"/>.</param> public void Stop(bool discardBlockedKeys = false) { if (_block == 0) { return; } _block = 0; _discardBlockedKeys = discardBlockedKeys; Api.SetEvent(_stopEvent); Api.WaitForSingleObject(_syncEvent, Timeout.Infinite); _CloseHandles(); }
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); }
void _ThreadProc() { WindowsHook hk = null, hm = null; WinEventHook hwe = null; try { try { if (_block.Has(BIEvents.Keys)) { hk = WindowsHook.Keyboard(_keyHookProc ??= _KeyHookProc); } if (_block.HasAny(BIEvents.MouseClicks | BIEvents.MouseMoving)) { hm = WindowsHook.Mouse(_mouseHookProc ??= _MouseHookProc); } } catch (AuException e1) { Debug_.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. wait.doEvents(); //print.it("started"); Api.SetEvent(_syncEvent); //the hook detects Ctrl+Alt+Del, Win+L, UAC consent, etc. SystemEvents.SessionSwitch only Win+L. try { hwe = new WinEventHook(EEvent.SYSTEM_DESKTOPSWITCH, 0, _winEventProc ??= _WinEventProc); } catch (AuException e1) { Debug_.Print(e1); } //failed to hook wait.Wait_(-1, WHFlags.DoEvents, _stopEvent, _threadHandle); if (_blockedKeys != null) { bool onlyUp = _discardBlockedKeys || Environment.TickCount64 - _startTime > c_maxResendTime; _blockedKeys.SendBlocked_(onlyUp); } //print.it("ended"); } finally { _blockedKeys = null; hk?.Dispose(); hm?.Dispose(); hwe?.Dispose(); Api.SetEvent(_syncEvent); } GC.KeepAlive(this); }
/// <summary> /// This constructor calls <see cref="Start"/>. /// </summary> /// <exception cref="ArgumentException"><i>what</i> is 0.</exception> public AInputBlocker(BIEvents what) { Start(what); }