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(); } }
public static bool IsKeyPressed(int key) { return((WinApi.GetAsyncKeyState(key) & 0x8000) != 0); }