I have had the same problem trying to add a hook to nox player (android emulator). I have tried the same code with another emulator and it works perfectly.
After this, i'm sure that nox includes a anti hook system ... probably same for your program. Nox looks freeze or exits after SetWindowsHookExA and not events captured at all.
EDITED. I know why after some hours checking.... 64 bits app needs 64 bits dlls, and 32 bits apps need 32 bits dlls.
from: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexa
SetWindowsHookEx can be used to inject a DLL into another process. A
32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL
cannot be injected into a 32-bit process. If an application requires
the use of hooks in other processes, it is required that a 32-bit
application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit
processes, and a 64-bit application call SetWindowsHookEx to inject a
64-bit DLL into 64-bit processes. The 32-bit and 64-bit DLLs must have
different names.
Because hooks run in the context of an application, they must match
the "bitness" of the application. If a 32-bit application installs a
global hook on 64-bit Windows, the 32-bit hook is injected into each
32-bit process (the usual security boundaries apply). In a 64-bit
process, the threads are still marked as "hooked." However, because a
32-bit application must run the hook code, the system executes the
hook in the hooking app's context; specifically, on the thread that
called SetWindowsHookEx. This means that the hooking application must
continue to pump messages or it might block the normal functioning of
the 64-bit processes. If a 64-bit application installs a global hook
on 64-bit Windows, the 64-bit hook is injected into each 64-bit
process, while all 32-bit processes use a callback to the hooking
application.
To hook all applications on the desktop of a 64-bit Windows
installation, install a 32-bit global hook and a 64-bit global hook,
each from appropriate processes, and be sure to keep pumping messages
in the hooking application to avoid blocking normal functioning. If
you already have a 32-bit global hooking application and it doesn't
need to run in each application's context, you may not need to create
a 64-bit version.
https://learn.microsoft.com/en-us/windows/win32/winprog64/process-interoperability
You can run Win32-based applications on 64-bit Windows using an
emulation layer. Windows 10 on ARM includes an x86-on-ARM64 emulation
layer. For more information, see Running 32-bit Applications.
On 64-bit Windows, a 64-bit process cannot load a 32-bit dynamic-link
library (DLL). Additionally, a 32-bit process cannot load a 64-bit
DLL. However, 64-bit Windows supports remote procedure calls (RPC)
between 64-bit and 32-bit processes (both on the same computer and
across computers). On 64-bit Windows, an out-of-process 32-bit COM
server can communicate with a 64-bit client, and an out-of-process
64-bit COM server can communicate with a 32-bit client. Therefore, if
you have a 32-bit DLL that is not COM-aware, you can wrap it in an
out-of-process COM server and use COM to marshal calls to and from a
64-bit process.