Win Api Как узнать повторный ли это эвент нажатия клавиши
Цитата: Viсtor от 05.10.2022, 14:23Всем привет. Сейчас пишу обработчик клавиатурного ввода.
[spoiler title="Исходный код"]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 }[/spoiler]
Мне необходимо определить была ли нажата клавиша до. Имеется ввиду отбросить события повторные, когда клавиша уже была зажата и вызвала повторный эвент.
Я пытался расшифровать в структуре TagKBDLLHOOKSTRUCT Flags (Скорей всего не правильно), но даже если правильно, вроде там нет информации о том была ли нажата клавиша. Это информация вроде можно извлечь из KeyboardProc при регистрации WH_KEYBOARD, а не WH_KEYBOARD_LL, но не пойму как указать окно(или что-то еще), события не приходят, и не понятно подходит ли это для моей задачи. Буду рад любой помощи. Спасибо.
Всем привет. Сейчас пишу обработчик клавиатурного ввода.
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 (Скорей всего не правильно), но даже если правильно, вроде там нет информации о том была ли нажата клавиша. Это информация вроде можно извлечь из KeyboardProc при регистрации WH_KEYBOARD, а не WH_KEYBOARD_LL, но не пойму как указать окно(или что-то еще), события не приходят, и не понятно подходит ли это для моей задачи. Буду рад любой помощи. Спасибо.