| File: System\Windows\Forms\SendKeys\SendKeys.SendKeysHookProc.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System.Windows.Forms; public partial class SendKeys { /// <summary> /// This class is our callback for the journaling hook we install. /// </summary> private static class SendKeysHookProc { // There appears to be a timing issue where setting and removing and then setting these hooks via // SetWindowsHookEx / UnhookWindowsHookEx can cause messages to be left in the queue and sent after the // re-hookup happens. This puts us in a bad state as we get an HC_SKIP before an HC_GETNEXT. So in that // case, we just ignore the HC_SKIP calls until we get an HC_GETNEXT. We also sleep a bit in the Unhook. private static bool s_gotNextEvent; #pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])] #pragma warning restore CS3016 public static unsafe LRESULT Callback(int nCode, WPARAM wparam, LPARAM lparam) { EVENTMSG* eventmsg = (EVENTMSG*)(nint)lparam; if (PInvoke.GetAsyncKeyState((int)Keys.Pause) != 0) { s_stopHook = true; } switch ((uint)nCode) { case PInvoke.HC_SKIP: if (s_gotNextEvent) { if (s_events.Count > 0) { s_events.Dequeue(); } s_stopHook = s_events.Count == 0; break; } break; case PInvoke.HC_GETNEXT: s_gotNextEvent = true; Debug.Assert( s_events.Count > 0 && !s_stopHook, "HC_GETNEXT when queue is empty!"); SKEvent @event = s_events.Peek(); eventmsg->message = (uint)@event.WM; eventmsg->paramL = @event.ParamL; eventmsg->paramH = @event.ParamH; eventmsg->hwnd = @event.HWND; eventmsg->time = PInvoke.GetTickCount(); break; default: if (nCode < 0) { PInvoke.CallNextHookEx(s_hhook, nCode, wparam, lparam); } break; } if (s_stopHook) { UninstallJournalingHook(); s_gotNextEvent = false; } return (LRESULT)0; } } }