private AttachedThreadInputScope() { s_current.Value?.Dispose(); s_current.Value = this; _foregroundWindow = WinApi.GetForegroundWindow(); _currentThreadId = WinApi.GetCurrentThreadId(); _foregroundThreadId = WinApi.GetWindowThreadProcessId(_foregroundWindow, out var _); if (_currentThreadId != _foregroundThreadId) { // attach to the foreground thread if (!WinApi.AttachThreadInput(_currentThreadId, _foregroundThreadId, true)) { // unable to attach, see if the window is a Win10 Console Window var className = new StringBuilder(capacity: 256); WinApi.GetClassName(_foregroundWindow, className, className.Capacity - 1); if (String.CompareOrdinal("ConsoleWindowClass", className.ToString()) != 0) { return; } // consider attached to a console window } } _attached = true; }
public static bool TryGetThirdPartyForgroundWindow(out IntPtr hwnd) { hwnd = IntPtr.Zero; var foregroundWindow = WinApi.GetForegroundWindow(); var currentThreadId = WinApi.GetCurrentThreadId(); var foregroundThread = WinApi.GetWindowThreadProcessId(foregroundWindow, out var _); if (currentThreadId == foregroundThread) { return(false); } else { var hwndRoot = WinApi.GetAncestor(foregroundWindow, WinApi.GA_ROOT); if (hwndRoot != IntPtr.Zero) { foregroundWindow = hwndRoot; } var className = new StringBuilder(capacity: 256); WinApi.GetClassName(foregroundWindow, className, className.Capacity - 1); if (className.ToString().CompareTo("Shell_TrayWnd") == 0) { return(false); } hwnd = foregroundWindow; return(true); } }
private AttachedThreadInputScope() { s_current.Value?.Dispose(); s_current.Value = this; _foregroundWindow = WinApi.GetForegroundWindow(); _currentThreadId = WinApi.GetCurrentThreadId(); _foregroundThreadId = WinApi.GetWindowThreadProcessId(_foregroundWindow, out var _); if (_currentThreadId != _foregroundThreadId) { // attach to the foreground thread if (!WinApi.AttachThreadInput(_foregroundThreadId, _currentThreadId, true)) { return; } } _attached = true; }
private async static ValueTask <(bool left, bool right)> GetLeftRightCtrlStateAsync(uint delay, CancellationToken token) { // this is used purely for debugging and so we utilize // native CreateTimerQueueTimer with WT_EXECUTEINPERSISTENTTHREAD // for keyboard polling here, because we don't want // to pollute ThreadPool threads with AttachThreadInput other user32 calls var tcs = new TaskCompletionSource <(bool, bool)>(TaskCreationOptions.RunContinuationsAsynchronously); using var rego = token.Register(() => tcs.SetCanceled(), useSynchronizationContext: false); WinApi.WaitOrTimerCallbackProc timerCallback = delegate { try { // attach the to the foreground thread to read the keyboard status var currentThread = WinApi.GetCurrentThreadId(); var foregroundWindow = WinApi.GetForegroundWindow(); var foregroundThread = WinApi.GetWindowThreadProcessId(foregroundWindow, out uint foregroundProcess); var attached = WinApi.AttachThreadInput(foregroundThread, currentThread, true); try { var leftCtrl = (WinApi.GetAsyncKeyState(WinApi.VK_LCONTROL) & 0x8000) != 0; var rightCtrl = (WinApi.GetAsyncKeyState(WinApi.VK_RCONTROL) & 0x8000) != 0; tcs.TrySetResult((leftCtrl, rightCtrl)); } finally { if (attached) { WinApi.AttachThreadInput(foregroundThread, currentThread, false); } } } catch (Exception ex) { tcs.TrySetException(ex); } }; var gcHandle = GCHandle.Alloc(timerCallback); IntPtr timerHandle = IntPtr.Zero; try { if (!WinApi.CreateTimerQueueTimer( out timerHandle, IntPtr.Zero, timerCallback, IntPtr.Zero, delay, 0, (UIntPtr)(WinApi.WT_EXECUTEINPERSISTENTTHREAD | WinApi.WT_EXECUTEONLYONCE))) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } return(await tcs.Task); } finally { if (timerHandle != IntPtr.Zero) { WinApi.DeleteTimerQueueTimer(IntPtr.Zero, timerHandle, IntPtr.Zero); } gcHandle.Free(); } }