I'm trying to set a low-level keyboard hook. I have DLL with the following code:
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace InputListener;
public class LowLevelKeyboardHook : IDisposable
{
const int GLOBAL_HOOK = 0;
readonly HOOKPROC _callback;
readonly IntPtr _hHook;
public IntPtr Handle => _hHook;
public LowLevelKeyboardHook(HOOKPROC hookCallback, bool throwOnFail = false)
{
_callback = hookCallback;
IntPtr hHookProcModule;
using (var currentModule = Process.GetCurrentProcess().MainModule)
hHookProcModule = GetModuleHandle(currentModule);
_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hHookProcModule, GLOBAL_HOOK);
var errorCode = GetLastError();
if (throwOnFail && hHookProcModule == IntPtr.Zero) throw new DllNotFoundException("The module where the callback defined could not be retrieved.");
if (throwOnFail && Handle == default) throw new Exception("The hook could not be set.");
}
static IntPtr GetModuleHandle(ProcessModule? module)
{
return GetModuleHandle(module?.ModuleName!); ;
}
IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0) _callback(nCode, wParam, lParam);
return CallNextHookEx(Handle, nCode, wParam, lParam);
}
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
extern static IntPtr GetModuleHandle(string lpModuleName);
[DllImport("Kernel32.dll")]
extern static int GetLastError();
[DllImport("User32.dll")]
extern static IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int hookType, HOOKPROC lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("User32.dll")]
extern static bool UnhookWindowsHookEx(IntPtr hhk);
const int WH_KEYBOARD_LL = 0xD;
public void Dispose()
{
GC.SuppressFinalize(this);
UnhookWindowsHookEx(Handle);
}
}
public delegate IntPtr HOOKPROC(int nCode, IntPtr wParam, IntPtr lParam);
After including this DLL to a console application and running the following code, LowLevelKeyboardProc never gets called but errorCode holds 0 and all handles are returned:
Task.Run(() => {
var hook = new LowLevelKeyboardHook(TestProc);
while (true) ;
});
while (true) ;
IntPtr TestProc(int nCode, IntPtr wParam, IntPtr lParam)
{
Console.WriteLine("Works");
return default;
}
Please, help me with this one. I've spent a lot of time trying to figure out the reason why the callback doesn't get called. I peeked into the code of other people and they do virtually the same things I do but my code doesn't seem to work. Currently, I think that the issue might well be in the loop that I use for testing the hook. When I run the program.