Win Api Как узнать повторный ли это эвент нажатия клавиши
Пишу сейчас обработчик клавиатурного ввода. Сейчас имеется такой код.
public class LowLevlHook : CriticalFinalizerObject, IDisposable
{
private bool _disposed = false;
public void Dispose()
{
if (_disposed is true) return;
UninstallHook();
_disposed = true;
GC.SuppressFinalize(this);
}
private delegate IntPtr KeyboardHookHandler(int nCode, WMEvent wParam, TagKBDLLHOOKSTRUCT lParam);
private KeyboardHookHandler? hookHandler;
private IntPtr hookID = IntPtr.Zero;
public void InstallHook()
{
CheckDisposed();
hookHandler = HookFunc;
hookID = SetHook(hookHandler);
}
~LowLevlHook()
{
try
{
if (_disposed is true) return;
UnhookWindowsHookEx(hookID);
Marshal.FreeHGlobal(hookID);
}
catch { }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CheckDisposed() { if (_disposed is true) throw new ObjectDisposedException("You cannot use an instance of a class after it has been disposed of."); }
public void UninstallHook() { CheckDisposed(); UnhookWindowsHookEx(hookID); }
private const int WH_KEYBOARD_LL = 13;
private const int WH_KEYBOARD = 2;
private IntPtr SetHook(KeyboardHookHandler proc) =>
SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandleW(Process.GetCurrentProcess().MainModule is not ProcessModule module2 ?
throw new NullReferenceException() : module2.ModuleName ?? throw new NullReferenceException()), 0);
internal delegate void KeyboardHookCallback(VKeys key, SettingHook setting);
internal event KeyboardHookCallback? KeyDown;
internal SettingHook Settings = new SettingHook();
private IntPtr HookFunc(int nCode, WMEvent wParam, TagKBDLLHOOKSTRUCT lParam)
{
if (nCode is 0)
{
if (wParam is WMEvent.WM_KEYDOWN || wParam is WMEvent.WM_SYSKEYDOWN)
{
// Необходимо отбросить повторные эвенты когда клавиша зажата
KeyDown?.Invoke(lParam.Vkcode, Settings);
if (Settings.Break is true)
{
Settings.Break = false;
return (System.IntPtr)1;
}
}
}
return CallNextHookEx(hookID, nCode, wParam, lParam);
}
enum WMEvent: uint
{
WM_KEYDOWN = 256,
WM_SYSKEYDOWN = 260,
WM_KEYUP = 257,
WM_SYSKEYUP = 261
}
[StructLayout(LayoutKind.Sequential)]
struct TagKBDLLHOOKSTRUCT
{
internal readonly VKeys Vkcode;
internal readonly uint ScanCode;
internal readonly uint Flags;
internal readonly uint Time; // Милисикунды между сообщениями. Обнуляются при переполнинии.
internal readonly UIntPtr DwExtraInfo; //??
}
#region WinAPI
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(int idHook, KeyboardHookHandler lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll")]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, WMEvent wParam, TagKBDLLHOOKSTRUCT lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr GetModuleHandleW([MarshalAs(UnmanagedType.LPWStr)] string lpModuleName);
#endregion
}
Мне необходимо определить была ли нажата клавиша до. Имеется ввиду отбросить ивенты повторные, когда клавиша уже была зажата и вызвала повторный эвент.
Я пытался расшифровать в структуре TagKBDLLHOOKSTRUCT Flags (Скорей всего не правильно), но даже если правильно, вроде там нет информации о том была ли нажата клавиша. Это информация вроде как есть в WH_KEYBOARD, но не пойму как указать окно (или что-то еще), эвенты не приходят, и не понятно подходит ли это для моей задачи.
Обновление:
Мне удается привязать WH_KEYBOARD только к потоку в последним аргументе метода CallNextHookEx(), и это работает только если окно имеет фокус.