I have .NET6 Background Service application from which I want to listen to keyboard and when Print Screen button is pressed, to run some code from that background service.
I have found multiple answers to listening for key press but haven't managed to make any work. All of them use User32.dll
and methods that I tried using are RegisterHotkey
and SetWindowsHookEx
.
Closes I think I came is with SetWindowsHookEx
since when I run it and try opening new tab in chrome with Control + T
I see it lags for few milliseconds, but for some reason callback handler in my code is not hit.
Here is the that code I managed to get to kind a work:
namespace LSShot;
using System.Drawing;
using System.Runtime.InteropServices;
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
#region Import Methods
[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("User32.dll")]
public static extern void ReleaseDC(IntPtr hwnd, IntPtr dc);
[DllImport("User32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("User32.dll")]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("User32.dll")]
public static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
public static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
#endregion
#region Structures
public struct keyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
#endregion
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
public IntPtr hhook = IntPtr.Zero;
private static keyboardHookProc callbackDelegate;
const int WH_KEYBOARD_LL = 13;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
Hook();
}
~Worker()
{
Unhook();
}
private void Hook()
{
if(callbackDelegate != null)
throw new InvalidOperationException("Can't hook more than once!");
IntPtr hInstance = LoadLibrary("User32");
callbackDelegate = new keyboardHookProc(hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
if (hhook == IntPtr.Zero)
throw new Exception("IntPtr.Zero");
}
private void Unhook()
{
}
private int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
{
_logger.LogInformation(code.ToString());
return CallNextHookEx(hhook, code, wParam, ref lParam);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
}
}
}