A while back I was researching a way to check if a machine was being used so that it could be used in some computationally expensive processing if the machine was idle. I ended up using some WMI commands, but in the course of researching I wrote some code to hook into the keyboard and mouse at a low level.
This code is based off of the GlobalMouseKeyHook project.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MinHook
{
class KBHook
{
public const int WM_KEYDOWN = 0x100;
[StructLayout(LayoutKind.Sequential)]
internal struct KeyboardHookStruct
{
public int VirtualKeyCode;
public int ScanCode;
public int Flags;
public int Time;
public int ExtraInfo;
}
public static int HookHandle;
public delegate int HookCallback(int nCode, int wParam, IntPtr lParam);
static void Main(string[] args)
{
HookHandle = SetWindowsHookEx(13, new HookCallback(CallBackFunction), Process.GetCurrentProcess().MainModule.BaseAddress, 0);
while (true)
{
Application.Run();
}
}
public static int CallBackFunction(int nCode, Int32 wParam, IntPtr lParam)
{
if (nCode == 0)
ProcessCallBack(wParam, lParam);
return CallNextHookEx(HookHandle, nCode, wParam, lParam);
}
public static void ProcessCallBack(Int32 wParam, IntPtr lParam)
{
if (wParam != WM_KEYDOWN)
return;
KeyboardHookStruct k = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
byte[] keyState = new byte[256];
byte[] inBuffer = new byte[2];
GetKeyboardState(keyState);
bool isSuccesfullyConverted = ToAscii(k.VirtualKeyCode, k.ScanCode, keyState, inBuffer, k.Flags) == 1;
var ch = (char)inBuffer[0];
Console.WriteLine(string.Format("VKC={0}, Char={1}", k.VirtualKeyCode, ch));
}
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern int SetWindowsHookEx(int idHook,HookCallback lpfn,IntPtr hMod,int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
internal static extern int CallNextHookEx(int idHook,int nCode,int wParam,IntPtr lParam);
[DllImport("user32")]
public static extern int ToAscii(int uVirtKey,int uScanCode,byte[] lpbKeyState,byte[] lpwTransKey,int fuState);
[DllImport("user32")]
public static extern int GetKeyboardState(byte[] pbKeyState);
}
}