I am writing an aplication that need to get the system input language, while the application window is not focused.
After searching Google I have found that the way to do this is to hook WM_INPUTLANGCHANGE message.
But I could not find a syntax example of the hook.
I have found the following code and tried to adapt it for my needs, but I have failed:
Edit:
I have replaced WM_KEYUP with WM_INPUTLANGCHANGE but it does not works.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace KeyHook
{
class LenHook
{
private const int WM_INPUTLANGCHANGE = 0x0051;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public LenHook()
{
_hookID = SetHook(_proc);
UnhookWindowsHookEx(_hookID);
System.Windows.Forms.Application.Run();
}
//Install hook
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (var curProcess = Process.GetCurrentProcess())
{
using (var curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WM_INPUTLANGCHANGE, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
}
//Do it when key press
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
MessageBox.Show(wParam.ToString());
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
}
This code from a project of mine works for me, it looks like we may have used the same example:
private static IntPtr _hookId = IntPtr.Zero;
private readonly External.LowLevelKeyboardProc _proc;
public FrmMain()
{
_proc = HookCallback;
_hookId = SetHook(_proc);
InitializeComponent();
}
private static IntPtr SetHook(External.LowLevelKeyboardProc proc)
{
using(var curProcess = Process.GetCurrentProcess())
{
using(var curModule = curProcess.MainModule)
{
return External.SetWindowsHookEx(External.WH_KEYBOARD_LL, proc, External.GetModuleHandle(curModule.ModuleName), 0);
}
}
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
// You can change this to WM_KEYDOWN
if (nCode >= 0 && wParam == (IntPtr)External.WM_KEYUP)
{
// Code you want to run when a button is pressed.
}
return External.CallNextHookEx(_hookId, nCode, wParam, lParam);
}
Also, this is my External class.
public static class External
{
public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public const int WH_KEYBOARD_LL = 13;
public const int WM_KEYDOWN = 0x0100;
public const int WM_KEYUP = 0x0101;
public const uint WM_GETTEXT = 0x0D;
public const uint WM_GETTEXTLENGTH = 0x0E;
public const uint EM_GETSEL = 0xB0;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint GetCurrentThreadId();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetFocus();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, uint Msg, out int wParam, out int lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetCaretPos(out Point lPoint);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
}
I have read that you'll have problems if you try to do these hooks from a console application, although your call to Application.Run() should fix that.
Related
I want to get acquainted with WinAPI in C#.
I need to write a global mouse hook in the console application. I found the code that does this job. But I have a console application, and I shouldn't use Application.Run () from Windows.Forms.
I need to forward messages about the coordinates of the mouse from winAPI directly to the console window without winForms. How can i do this?
class InterceptMouse
{
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_MOUSE_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && MouseMessages.WM_MOUSEMOVE == (MouseMessages)wParam)
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
[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 IntPtr GetModuleHandle(string lpModuleName);
}
Add system.windows.forms as a reference to your project.
using System.Windows.Forms;
It should be work then.
On my computer, and on few of other computers (laptops, if it important) the program works, but on all PCs in our classroom - it works only if the console application window is in focus.
Of course on the PC user does not have full rights, but that in fact should not interfere with low-level hooks? Moreover, I specifically created a user account with limited rights on my laptop and everything worked right.
On some forums I was told that it could be due to the bit width of programs. What do you think about it?
Here is the code of the keylogger class.
P.S. Functions GetSymbolENG and GetSymbolRUS is just a switch-case for the Russian layout and additional symbols.
P.S.S Sorry for my english.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
// dll import + keylogger
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace meinhack_
{
class KeyLogger
{
const int WH_KEYBOARD_LL = 13;
const int HC_ACTION = 0;
private const int WM_KEYDOWN = 0x0100;
private static IntPtr _hookID = IntPtr.Zero;
public static string hookedKeys = "";
public KeyLogger()
{
Console.Out.WriteLine("Hook created...");
}
private static IntPtr SetHook(KeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
public void SetHook()
{
Console.Out.WriteLine("Trying to set hook...");
_hookID = SetHook(Callback);
Application.Run();
Console.Out.WriteLine("Hook right...");
}
~KeyLogger()
{
UnhookWindowsHookEx(_hookID);
}
public delegate IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
KeyboardProc Callback = KeyboardHookCallback;
static IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
Console.Out.WriteLine("Trying to recognize layout...");
string text = GetKeyboardLayoutId();
Console.Out.WriteLine("Layout recognized...");
int vkCode = Marshal.ReadInt32(lParam);
Console.Out.WriteLine("Trying to get KeyState of CapsLock and Shift layout...");
bool capsLock = (((ushort)GetKeyState(0x14)) & 0xffff) != 0;
bool numLock = (((ushort)GetKeyState(0x90)) & 0xffff) != 0;
bool scrollLock = (((ushort)GetKeyState(0x91)) & 0xffff) != 0;
bool shift = (GetAsyncKeyState(Keys.LShiftKey) != 0 || GetAsyncKeyState(Keys.RShiftKey) != 0);
Console.Out.WriteLine("KeyState of CapsLock and Shift got...");
Console.Out.WriteLine("Trying to write key...");
if (text == "RUS") hookedKeys += GetSymbolRUS((Keys)vkCode, shift, capsLock).ToString();
else hookedKeys += GetSymbolENG((Keys)vkCode, shift, capsLock).ToString();
Console.Out.WriteLine("Key written...");
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
public string GetHookedKeys()
{
return hookedKeys;
}
public void ResetHookedKeys()
{
hookedKeys = "";
}
#region dllimport
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetKeyboardLayout(int WindowsThreadProcessID);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetWindowThreadProcessId(IntPtr handleWindow, out int lpdwProcessID);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
private static extern short GetKeyState(int keyCode);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
private static extern short GetAsyncKeyState(Keys key);
#endregion
#region recognizeLayout
private static InputLanguageCollection _InstalledInputLanguages;
private static int _ProcessId;
private static string _CurrentInputLanguage;
private static string GetKeyboardLayoutId()
{
_InstalledInputLanguages = InputLanguage.InstalledInputLanguages;
IntPtr hWnd = GetForegroundWindow();
int WinThreadProcId = GetWindowThreadProcessId(hWnd, out _ProcessId);
IntPtr KeybLayout = GetKeyboardLayout(WinThreadProcId);
for (int i = 0; i < _InstalledInputLanguages.Count; i++)
{
if (KeybLayout == _InstalledInputLanguages[i].Handle)
{
_CurrentInputLanguage = _InstalledInputLanguages[i].Culture.ThreeLetterWindowsLanguageName.ToString();
}
}
return _CurrentInputLanguage;
}
#endregion
}
}
I'm about to write something like a screenshot programm. For now I used the PrintWindow function with the handle of the active window -> GetForgroundWindow and to start it I set a hook on the keyboard. When I capture normal windows on the desktop it's ok, besides there is no hardware accelerated effects, but in a game the hook doesn't seem to work. (I know, that the PrintWindow won't do anything here, I'm implementing a SlimDX class at the time) Is that some sort of a problem with the game-side modified message loop? And if yes, is there a way to make it work :D I would appreciate a C# way, cause I'm bad at programming UI with C++ ^^
I hope it is just some sort of bug...
Greetz Coldi
EDIT:
public class KeyboardHook
{
#region Imports
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[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 IntPtr GetModuleHandle(string lpModuleName);
#endregion
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x0100;
static LowLevelKeyboardProc m_proc = HookCallback;
static IntPtr m_hookID = IntPtr.Zero;
static Screenshot m_screen;
public static void Hook(Screenshot screenshotManager)
{
m_screen = screenshotManager;
m_hookID = SetHook(m_proc);
}
public static void Unhook()
{
UnhookWindowsHookEx(m_hookID);
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
#if DEBUG
Debug.WriteLine(((System.Windows.Forms.Keys)vkCode).ToString());
#endif
if ((System.Windows.Forms.Keys)vkCode == m_screen.CaptureKey)
{
m_screen.CaptureSimple();
}
}
return CallNextHookEx(m_hookID, nCode, wParam, lParam);
}
}
So on desktop it works pretty fine, but when I start a DirectX application, the keyhook doesn't get any pressed key (HookCallback isn't handeled)
Ive seen many solutions online but none does exactly what I want. What is the best/simplest way to get any keys pressed in a given process (not my console applicaton) while my application is running in background. I dont need the modifiers or anything.
If you don't particularly care which process the keys are being pressed in the easiest method would be to call GetAsyncKeyState. It's rather limited though as it does not hook the keyboard and requires you to call it continuously. The best approach in my opinion is to hook the keyboard.
Using SetWindowsHookEx you can actually explicitly specify the identifier of the thread with which the hook procedure is to be associated so you can hook keys for a specific process (see dwThreadId).
Here's a class that you can use (originally found on a Micrsoft blog but I cannot seem to find the authors name at the moment!)
public delegate IntPtr KeyboardProcess(int nCode, IntPtr wParam, IntPtr lParam);
public sealed class KeyboardHook
{
public static event EventHandler<KeyPressedEventArgs> KeyPressed;
private const int WH_KEYBOARD = 13;
private const int WM_KEYDOWN = 0x0100;
private static KeyboardProcess keyboardProc = HookCallback;
private static IntPtr hookID = IntPtr.Zero;
public static void CreateHook()
{
hookID = SetHook(keyboardProc);
}
public static void DisposeHook()
{
UnhookWindowsHookEx(hookID);
}
private static IntPtr SetHook(KeyboardProcess keyboardProc)
{
using (Process currentProcess = Process.GetCurrentProcess())
using (ProcessModule currentProcessModule = currentProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD, keyboardProc, GetModuleHandle(currentProcessModule.ModuleName), 0);
}
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (KeyPressed != null)
KeyPressed(null, new KeyPressedEventArgs((Keys)vkCode));
}
return CallNextHookEx(hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProcess lpfn, IntPtr hMod, uint dwThreadId);
[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 IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
}
public class KeyPressedEventArgs : EventArgs
{
public Keys KeyCode { get; set; }
public KeyPressedEventArgs(Keys Key)
{
KeyCode = Key;
}
}
Implementation via Console Application:
class Program
{
static void Main(string[] args)
{
KeyboardHook.CreateHook();
KeyboardHook.KeyPressed += KeyboardHook_KeyPressed;
Application.Run();
KeyboardHook.DisposeHook();
}
static void KeyboardHook_KeyPressed(object sender, KeyPressedEventArgs e)
{
Console.WriteLine(e.KeyCode.ToString());
}
}
What you're looking for is called a global keyboard hook. You can find more information and examples on MSDN.
Oh, so you're looking for "Autofire" in old-school gaming terms?
Instead of writing your own keyboard hook app (unless you're doing it for fun/the thrill of it/the exercise) you might want to look at AutoIt or AutoHotkey, which are both pretty good for keyboard/mouse automation.
See this thread for instance... http://www.autohotkey.com/board/topic/40598-autofire-keyboard/
I have found a way to hook only for a process.
You may need it.
int ProcessId = GetProcessesByName("Your_app_here").FirstOrDefault().Id;
private IntPtr SetHook(KeyboardHookHandler proc)
{
return SetWindowsHookEx(13, proc, GetModuleHandle(Process.GetProcessById(ProcessId).MainModule.ModuleName), GetWindowThreadProcessId(GetModuleHandle(Process.GetProcessById(ProcessId).MainModule.ModuleName), out int MainThreadId));
}
Remember to import these methods.
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, KeyboardHookHandler lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
I am using SetWindowsHookEx() to create a keyboard hook. The creation seems to be successful but the procedure which is registered never gets called. Is there something I am doing wrong?
#region Windows API Functions Declarations
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern bool UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
#endregion
=
private void CreateHook()
{
int id_hook = (int)HookType.WH_KEYBOARD_LL;
HookProc lpfn = new HookProc(this.KeyboardHookProc);
using (ProcessModule curModule = Process.GetCurrentProcess().MainModule)
hHook = SetWindowsHookEx(id_hook, lpfn, GetModuleHandle(curModule.ModuleName), 0);
if (hHook == 0)
throw new Exception("could not start monitoring mouse events");
}
=
private int KeyboardHookProc(int code, IntPtr wParam, IntPtr lParam)
{
if (code >= 0)
Console.WriteLine((Keys)wParam.ToInt32());
return CallNextHookEx(0, code, wParam, lParam);
}
=
Your P/Invoke declarations are wrong, you are using int where IntPtr is required and mixing up idHook and hHook. After editing your code, this worked:
IntPtr hHook;
private delegate IntPtr HookProc(int nCode, IntPtr wp, IntPtr lp);
HookProc lpfn;
private IntPtr KeyboardHookProc(int code, IntPtr wParam, IntPtr lParam) {
if (code >= 0)
Console.WriteLine((Keys)wParam.ToInt32());
return CallNextHookEx(hHook, code, wParam, lParam);
}
private void CreateHook() {
int id_hook = 13;
lpfn = new HookProc(this.KeyboardHookProc);
using (ProcessModule curModule = Process.GetCurrentProcess().MainModule)
hHook = SetWindowsHookEx(id_hook, lpfn, GetModuleHandle(curModule.ModuleName), 0);
if (hHook == IntPtr.Zero)
throw new Exception("could not start monitoring mouse events");
}
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern bool UnhookWindowsHookEx(IntPtr hHook);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CallNextHookEx(IntPtr hHook, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);