Code:
using WPARAM = System.IntPtr;
using LPARAM = System.IntPtr;
public struct KeyHookEventArgs
{
public Keys PressedKey { get; set; }
public string TranslatedCharacters { get; set; }
}
public class KeyHook : IDisposable
{
public enum HookType
{
WH_KEYBOARD_LL = 13,
WH_GETMESSAGE = 3
}
public enum WindowsMessage : uint
{
KEYDOWN = 0x100,
SYSKEYDOWN = 0x0104,
KEYUP = 0x0101,
SYSKEYUP = 0x0105
}
public enum MapVirtualKeyMapType : uint
{
VK_TO_VSC = 0x00,
VSC_TO_VK = 0x01,
VK_TO_CHAR = 0x02,
VSC_TO_VK_EX = 0x03,
VK_TO_VSC_EX = 0x04
}
public enum VirtualKey
{
SHIFT = 0x10,
LSHIFT = 0xA0,
CONTROL = 0x11
}
private IntPtr _HookID = IntPtr.Zero;
private bool _shiftPressed = false;
public Func<KeyHookEventArgs, bool> OnKeyPressed = (KeyHookEventArgs eventArgs) => false;
public KeyHook(Func<KeyHookEventArgs, bool> onKeyPressed)
{
OnKeyPressed = onKeyPressed;
}
public void Install()
{
_HookID = SetWindowsHookEx((int)HookType.WH_KEYBOARD_LL, HookCallback, IntPtr.Zero, 0);
}
public void Uninstall()
{
UnhookWindowsHookEx(_HookID);
}
private delegate IntPtr LowLevelKeyboardProc(
int nCode, WPARAM wParam, LPARAM lParam);
private IntPtr HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
uint processID;
var windowThreadID = GetWindowThreadProcessId(GetForegroundWindow(), out processID);
var threadID = GetCurrentThreadId();
/* Kann zum filtern benutzt werden
using (var process = Process.GetCurrentProcess())
{
if (process.Id != processID)
return CallNextHookEx(_HookID, nCode, wParam, lParam);
}*/
if (nCode >= 0)
{
var virtualKey = (uint)Marshal.ReadInt32(lParam);
if ((int) wParam == (int) WindowsMessage.KEYDOWN || (int) wParam == (int) WindowsMessage.SYSKEYDOWN)
{
AttachThreadInput(threadID, windowThreadID, true);
var keyboardState = new byte[256];
GetKeyboardState(keyboardState);
if (_shiftPressed)
keyboardState[(int) VirtualKey.SHIFT] |= 0x80;
else
keyboardState[(int) VirtualKey.SHIFT] |= 0x00;
if (virtualKey == (int) VirtualKey.LSHIFT)
_shiftPressed = true;
var charBuffer = new char[16];
var translatedCharacterCount = ToUnicode(virtualKey,
MapVirtualKey(virtualKey, (uint) MapVirtualKeyMapType.VK_TO_VSC),
keyboardState, charBuffer, charBuffer.Length, 0);
AttachThreadInput(threadID, windowThreadID, false);
var forwardEvent = OnKeyPressed(new KeyHookEventArgs()
{
PressedKey = (Keys) virtualKey,
TranslatedCharacters = new string(charBuffer, 0, translatedCharacterCount)
});
if (forwardEvent)
return lParam;
}
else if ((int) wParam == (int) WindowsMessage.KEYUP)
{
if (virtualKey == (int)VirtualKey.LSHIFT)
_shiftPressed = false;
}
}
return CallNextHookEx(_HookID, nCode, wParam, lParam);
}
public void Dispose()
{
Uninstall();
}
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern int ToUnicode(uint virtualKey, uint scanCode, byte[] keyStates, [MarshalAs(UnmanagedType.LPArray)] [Out] char[] chars, int charMaxCount, uint flags);
[DllImport("user32.dll")]
static extern uint MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("User32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("User32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentThreadId();
[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,
WPARAM wParam, LPARAM lParam);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
Ihr braucht euch nicht im Zielprozess befinden, da der Hook global ist und jede Anwendung die auf dem selben Desktop läuft, gehooked wird.
Nutzung:
Code:
using (var keyHook = new KeyHook((KeyHookEventArgs eventArgs) =>
{
// return false wenn der Key an die Applikation selbst weitergeleitet werden soll,
// return true wenn der Key nicht weitergeleitet werden soll
}))
{
keyHook.Install();
tagMSG msg;
bool res = true;
while (res)
{
res = Convert.ToBoolean(GetMessage(out msg, IntPtr.Zero, 0, 0));
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
}