C# Keyboard Hook Problems

D

dsheiner

Hi,

I'm trying to implement a keyboard hook that will receive all keyboard
events within my application, and only my application, using C# .NET
3.5 and VS 2008.

I'm using the following code:

private delegate IntPtr KeyboardHookCallback(int nCode, IntPtr wParam,
IntPtr lParam);

private static KeyboardHookCallback _callback = new
KeyboardHookCallback(HookCallback);
private const int WH_KEYBOARD_LL = 13;

private static IntPtr SetHook()
{
return SetWindowsHookEx(WH_KEYBOARD_LL, _callback, IntPtr.Zero,
Thread.CurrentThread.ManagedThreadId);
}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true,
CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr SetWindowsHookEx(int idHook,
KeyboardHookCallback lpfn, IntPtr hMod, int threadId);

SetWindowsHookEx is returning 0, and Marshal.GetLastWin32Error() is
returning 87. I understand this means there's an invalid parameter,
but that's as far as I can get. I've tried using WH_KEYBOARD (2)
instead of WH_KEYBOARD_LL, and I've tried the deprecated
AppDomain.GetCurrentThreadId(), but this doesn't seem to make a
difference. Any idea where I've gone wrong?

Thanks,
Daniel Sheiner
 
K

Kerem Gümrükcü

Hi Daniel,

thats what a valid call looks like:

SetWindowsHookEx(WH_KEYBOARD_LL,
LowLevelKeyboardHookProc,
Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
0);

CallingConvention = CallingConvention.StdCall is not needed and set CharSet
to CharSet.Unicode if you strictly run on windows NT systems.

The callback delegate should look like that:

private delegate IntPtr LowLevelKeyboardProcDelegate(int nCode, int wParam,
IntPtr lParam);

Then it works fine,...

Regards

Kerem

--
 
D

dsheiner

Hi Kerem,

If I do it that way, SetWindowsHookEx still returns 0, and the Win32
error code is 1008.

-Daniel
 
R

Random

I'm trying to implement a keyboard hook that will receive all keyboard
events within my application, and only my application, using C# .NET
3.5 and VS 2008.

Lots of issues, here's some code that works:

[StructLayout(LayoutKind.Sequential)]
struct KeyboardHookStruct
{
public int VirtualKeyCode;
public int ScanCode;
public int Flags;
public int Time;
public IntPtr ExtraInfo;
}

delegate IntPtr LowLevelKeyboardProcDelegate(
int nCode, IntPtr wParam, IntPtr lParam);

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProcDelegate lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("Kernel32.dll", SetLastError = true)]
static extern IntPtr GetModuleHandle(IntPtr lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr CallNextHookEx(IntPtr hhk,
int nCode, IntPtr wParam, IntPtr lParam);

const int WH_KEYBOARD_LL = 13;

LowLevelKeyboardProcDelegate m_callback;
IntPtr m_hHook;

IntPtr LowLevelKeyboardHookProc(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
{
return CallNextHookEx(m_hHook, nCode, wParam, lParam);
}
else
{
KeyboardHookStruct khs = (KeyboardHookStruct)
Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
Debug.Print("Hook: Code: {0}, WParam: {1}, " +
"LParam: {2}, {3} {4} {5} {6}",
nCode, wParam, lParam,
khs.VirtualKeyCode, khs.ScanCode, khs.Flags, khs.Time);

return CallNextHookEx(m_hHook, nCode, wParam, lParam);
}
}

void Test()
{
m_callback = LowLevelKeyboardHookProc;

m_hHook = SetWindowsHookEx(WH_KEYBOARD_LL,
m_callback,
GetModuleHandle(IntPtr.Zero),
0);
}

void Untest()
{
UnhookWindowsHookEx(m_hHook);
}
 
D

dsheiner

Hi Random,

Thanks, that's progress. However, this is a global hook. I need a
hook that only intercepts key events from inside my own application.

-Daniel
 
R

Random

Thanks, that's progress.  However, this is a global hook.  I need a
hook that only intercepts key events from inside my own application.

Sorry, missed that in your original post. WH_KEYBOARD_LL only
supports global hooks. You'll have to use WH_KEYBOARD or some other
solution for a thread specific hook.
 
D

dsheiner

Here's my solution. It feels uncomfortably hackish, and if anyone has
a better way to do this, I'd be glad to hear it:

using System;
using System.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Hooks
{
public class HookInfo
{
private BitArray _bits;

public bool IsKeyDown
{
get
{
return !_bits[31];
}
}

public bool WasKeyDown
{
get
{
return _bits[30];
}
}

public bool IsExtendedKey
{
get
{
return _bits[24];
}
}

public bool AltIsDown
{
get
{
return _bits[29];
}
}

public int RepeatCount
{
get
{
return GetRange(0, 15);
}
}

public int ScanCode
{
get
{
return GetRange(16, 23);
}
}

public HookInfo(int value)
{
_bits = new BitArray(new int[] { value });
}

private int GetRange(int min, int max)
{
BitArray subSection = new BitArray(32);
for (int i = 0; i <= max - min; i++)
{
subSection.Set(i, _bits[min + i]);
}

int[] result = new int[1];
subSection.CopyTo(result, 0);
return result[0];
}
}

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SetHook();
}

private delegate IntPtr KeyboardHookCallback(int nCode, IntPtr
wParam, IntPtr lParam);

private static KeyboardHookCallback _callback = new
KeyboardHookCallback(HookCallback);
private static IntPtr _hookID = IntPtr.Zero;
private const int WH_KEYBOARD = 2;

private static void SetHook()
{
_hookID = SetWindowsHookEx(WH_KEYBOARD, _callback, 0,
GetCurrentThreadId());
}

private static IntPtr HookCallback(int nCode, IntPtr wParam,
IntPtr lParam)
{
if (nCode >= 0)
{
HookInfo hookInfo = new HookInfo(lParam.ToInt32());

Debug.Print(
string.Format(
"keycode: {0}, KeyDown: {1}, was KeyDown: {2},
extended: {3}, alt: {4}, repeat count: {5}, scan code: {6}",
wParam,
hookInfo.IsKeyDown,
hookInfo.WasKeyDown,
hookInfo.IsExtendedKey,
hookInfo.AltIsDown,
hookInfo.RepeatCount,
hookInfo.ScanCode));
}

return CallNextHookEx(_hookID, nCode, wParam, lParam);
}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError
= true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
KeyboardHookCallback lpfn, int hMod, int threadId);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError
= true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError
= true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int
nCode,
IntPtr wParam, IntPtr lParam);

[DllImport("kernel32.dll", CharSet = CharSet.Auto,
SetLastError = true)]
private static extern int GetCurrentThreadId();
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top