Your LowLevelKeyboardProc
definition is wrong. Change from
type LowLevelKeyboardProc = delegate of (int * IntPtr * IntPtr) -> IntPtr
to
type LowLevelKeyboardProc = delegate of int * IntPtr * IntPtr -> IntPtr
or better yet
type LowLevelKeyboardProc = delegate of int * nativeint * nativeint -> nativeint
or better yet
[<StructLayout(LayoutKind.Sequential)>]
type KBDLLHOOKSTRUCT =
val vkCode : uint32
val scanCode : uint32
val flags : uint32
val time : uint32
val dwExtraInfo : nativeint
type LowLevelKeyboardProc =
delegate of int * nativeint * KBDLLHOOKSTRUCT -> nativeint
In all of the above cases, proc
will need to use curried form rather than tupled form.
Also note that you should add SetLastError = true
to all of the extern
ed functions whose documentation says to call GetLastError
upon failure (which is the case for GetModuleHandle
, SetWindowsHookEx
, and UnhookWindowsHookEx
). That way if any fail (and you should be checking the return values...), you can simply raise a Win32Exception
or call Marshal.GetLastWin32Error
to get proper diagnostics.
EDIT: Just for the sake of clarity, here are all the P/Invoke signatures I successfully tested locally:
[<Literal>]
let WH_KEYBOARD_LL = 13
[<StructLayout(LayoutKind.Sequential)>]
type KBDLLHOOKSTRUCT =
val vkCode : uint32
val scanCode : uint32
val flags : uint32
val time : uint32
val dwExtraInfo : nativeint
type LowLevelKeyboardProc = delegate of int * nativeint * KBDLLHOOKSTRUCT -> nativeint
[<DllImport("kernel32.dll")>]
extern uint32 GetCurrentThreadId()
[<DllImport("kernel32.dll", SetLastError = true)>]
extern nativeint GetModuleHandle(string lpModuleName)
[<DllImport("user32.dll", SetLastError = true)>]
extern bool UnhookWindowsHookEx(nativeint hhk)
[<DllImport("user32.dll", SetLastError = true)>]
extern nativeint SetWindowsHookEx(int idhook, LowLevelKeyboardProc proc, nativeint hMod, uint32 threadId)
Also note that this would work equally as well, if you prefer value semantics for KBDLLHOOKSTRUCT
:
[<Struct; StructLayout(LayoutKind.Sequential)>]
type KBDLLHOOKSTRUCT =
val vkCode : uint32
val scanCode : uint32
val flags : uint32
val time : uint32
val dwExtraInfo : nativeint
type LowLevelKeyboardProc = delegate of int * nativeint * byref<KBDLLHOOKSTRUCT> -> nativeint
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…