1

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)
        {
        }
    }
}

Aleksa Ristic
  • 2,394
  • 3
  • 23
  • 54
  • I've done similar things in the past with `SetWindowsHookEx`. I used [this answer](https://stackoverflow.com/a/604417/10601203) to do it and got it working. Although when I've tried this more recently, it's caused severe lag when pressing any keyboard key. Not sure why that started happening. – Jesse Dec 15 '22 at 01:08
  • Where did you get this code from? Just comparing it to an old copy I have (for VSTO), I wire up the `SetWindowsHookEx` differently https://stackoverflow.com/a/10257266/495455 – Jeremy Thompson Dec 15 '22 at 01:09
  • @JeremyThompson I got it from multiple answers combined and trying it out. – Aleksa Ristic Dec 15 '22 at 01:10
  • @Jesse proposed the same underlying fix, take his code as it's from a well respected coder. – Jeremy Thompson Dec 15 '22 at 01:11

0 Answers0