I hope that you can help me, I have an application that took every key that I press, like a KeyLogger (yeah, I know, is kind a Malware but I used to another thing, good things, very good things. Not for spy someone). When I press the number keys between the letter key and the F's keys (I mean, the keys above the letters, that ones that you may use if you keyboard doesn't have numeric keypad)it write like this :
0123456789
But when I use the NumericPad (that one on the right side of the keyboard) it show like this:
ABCDEFGHIJ
How can I transform the letter to number?
PS: I research about this and I found that I may need to use UnicodeEncodig.Unicode but I don't know how to use it.
This is the class that capture the keys:
using System;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class GlobalKeyboardHook
{
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int code, int wParam, ref keyBoardHookStruct lParam);
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, LLKeyboardHook callback, IntPtr hInstance, uint theardID);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
public delegate int LLKeyboardHook(int Code, int wParam, ref keyBoardHookStruct lParam);
public struct keyBoardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x0100;
const int WM_KEYUP = 0x0101;
const int WM_SYSKEYDOWN = 0x0104;
const int WM_SYSKEYUP = 0x0105;
LLKeyboardHook llkh;
public List<Keys> HookedKeys = new List<Keys>();
IntPtr Hook = IntPtr.Zero;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
// This is the Constructor. This is the code that runs every time you create a new GlobalKeyboardHook object
public GlobalKeyboardHook()
{
llkh = new LLKeyboardHook(HookProc);
// This starts the hook. You can leave this as comment and you have to start it manually (the thing I do in the tutorial, with the button)
// Or delete the comment mark and your hook will start automatically when your program starts (because a new GlobalKeyboardHook object is created)
// That's why there are duplicates, because you start it twice! I'm sorry, I haven't noticed this...
hook(); <-- Choose!
}
~GlobalKeyboardHook()
{ unhook(); }
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
Hook = SetWindowsHookEx(WH_KEYBOARD_LL, llkh, hInstance, 0);
}
public void unhook()
{
UnhookWindowsHookEx(Hook);
}
public int HookProc(int Code, int wParam, ref keyBoardHookStruct lParam)
{
if (Code >= 0)
{
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key))
{
KeyEventArgs kArg = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
KeyDown(this, kArg);
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
KeyUp(this, kArg);
if (kArg.Handled)
return 1;
}
}
return CallNextHookEx(Hook, Code, wParam, ref lParam);
}
}
Second Edit, Here is where I use gHook_KeyDown:
GlobalKeyboardHook gHook;
private void Form1_Load(object sender, EventArgs e)
{
gHook = new GlobalKeyboardHook();
gHook.KeyDown += new KeyEventHandler(gHook_KeyDown);
foreach(Keys key in Enum.GetValues(typeof(Keys)))
ghook.HookedKeys.Add(key);
}
public void gHook_KeyDown(object sender, KeyEventArgs e)
{
textBox1.Text += ((char)e.KeyValue).ToString();
}
Related
I have a problem that when executing this code in my winform application, it runs normally but when running on the console it gets an error message, it seems that KeyEventHandler is only used in winform, yes What can be replaced in the console application. Here is my hook function:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
namespace Websevice_Core_Console
{
class Y2KeyboardHook
{
#region Win32 API Functions and Constants
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
KeyboardHookDelegate lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x101;
#endregion
private KeyboardHookDelegate _hookProc;
private IntPtr _hookHandle = IntPtr.Zero;
public delegate IntPtr KeyboardHookDelegate(int nCode, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
public struct KeyboardHookStruct
{
public int VirtualKeyCode;
public int ScanCode;
public int Flags;
public int Time;
public int ExtraInfo;
}
#region Keyboard Events
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
#endregion
// destructor
~Y2KeyboardHook()
{
Uninstall();
}
public void Install()
{
_hookProc = KeyboardHookProc;
_hookHandle = SetupHook(_hookProc);
if (_hookHandle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
private IntPtr SetupHook(KeyboardHookDelegate hookProc)
{
IntPtr hInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
return SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
private IntPtr KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
KeyboardHookStruct kbStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
if (wParam == (IntPtr)WM_KEYDOWN)
{
if (KeyDown != null)
KeyDown(null, new KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
}
else if (wParam == (IntPtr)WM_KEYUP)
{
if (KeyUp != null)
KeyUp(null, new KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
}
}
return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
}
public void Uninstall()
{
UnhookWindowsHookEx(_hookHandle);
}
}
}
Here is how I use it:
_y2KeyboardHook.KeyDown += (sender, e) =>// ghi nhan thong tin tu ban phim
{
if (e.KeyCode.ToString() != "")
{
Console.WriteLine("Hello "+KeyCode);
}
}
I got an error message on 2 lines
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
Can anyone help me, thanks a lot!
Given the code you posted and the vague "I got an error message" (which is not an actual question nor a useful problem statement…always be specific), it seems likely you are not referencing System.Windows.Forms.dll. On the other hand, if you aren't referencing the Winforms assembly, it's not clear why you don't also get an error where you use KeyEventArgs, since that's defined in the same assembly.
That said, assuming the error you're getting for your use of KeyEventHandler is simply that it's not defined, you can define the same type for your own use:
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
I.e., just copy the delegate type declaration from the documentation.
Now, all that said, if you aren't referencing the Winforms assembly, and you are getting an error message as expected with the KeyEventArgs type as well, as would be expected, you might want to consider not copying the Winforms implementation of this approach, and just declaring your own event args type with your own event handler. The only thing you're returning is the virtual key code, so you might as well define your own types for that:
class KeyboardHookEventArgs : EventArgs
{
public int VirtualKeyCode { get; }
public KeyboardHookEventArgs(int virtualKeyCode)
{
VirtualKeyCode = virtualKeyCode;
}
}
…then the events:
public event EventHandler<KeyboardHookEventArgs> KeyDown;
public event EventHandler<KeyboardHookEventArgs> KeyUp;
Where you create the args object like new KeyboardHookEventArgs(kbStruct.VirtualKeyCode) instead.
Note that your event-raising implementation is not thread-safe. It would be more safe, and more idiomatic in C#, to use the null-conditional operator, e.g.:
if (wParam == (IntPtr)WM_KEYDOWN)
{
KeyDown?.Invoke(this, new KeyboardHookEventArgs(kbStruct.VirtualKeyCode));
}
You should also pass a correct value for sender, i.e. this as I've shown above. If you want for the event to have no sender, then it (and the rest of the class) should just be static.
I'm using a global keyboard hook class. This class allows to check if keyboard key pressed anywhere. And after some time I'm having an error:
**CallbackOnCollectedDelegate was detected**
A callback was made on a garbage collected delegate of type 'Browser!Utilities.globalKeyboardHook+keyboardHookProc::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
Here is globalkeyboardHook class:
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
public struct keyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
public List<Keys> HookedKeys = new List<Keys>();
IntPtr hhook = IntPtr.Zero;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
public globalKeyboardHook()
{
hook();
}
~globalKeyboardHook()
{
unhook();
}
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
public void unhook()
{
UnhookWindowsHookEx(hhook);
}
public int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
{
if (code >= 0)
{
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key))
{
KeyEventArgs kea = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
{
KeyDown(this, kea);
}
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
{
KeyUp(this, kea);
}
if (kea.Handled)
return 1;
}
}
return CallNextHookEx(hhook, code, wParam, ref lParam);
}
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
#endregion
Any ideas how to fix it? The program works well, but after some time the program freezes ant I get this error.
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
There's your problem. You are relying on C# syntax sugar to have it automatically create a delegate object to hookProc. Actual code generation look like this:
keyboardHookProc $temp = new keyboardHookProc(hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, $temp, hInstance, 0);
There's only one reference to the delegate object, $temp. But it is local variable and disappears as soon as your hook() method stops executing and returns. The garbage collector is otherwise powerless to see that Windows has a 'reference' to it as well, it cannot probe unmanaged code for references. So the next time the garbage collector runs, the delegate object gets destroyed. And that's a kaboom when Windows makes the hook callback. The built-in MDA detects the problem and generates the helpful diagnostic before the program crashes with an AccessViolation.
You will need to create an additional reference to the delegate object that survives long enough. You could use GCHandle for example. Or easier, just store a reference yourself so the garbage collector can always see the reference. Add a field to your class. Making it static is a sure-fire way to ensure the object can't be collected:
private static keyboardHookProc callbackDelegate;
public void hook()
{
if (callbackDelegate != null) throw new InvalidOperationException("Can't hook more than once");
IntPtr hInstance = LoadLibrary("User32");
callbackDelegate = new keyboardHookProc(hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
if (hhook == IntPtr.Zero) throw new Win32Exception();
}
public void unhook()
{
if (callbackDelegate == null) return;
bool ok = UnhookWindowsHookEx(hhook);
if (!ok) throw new Win32Exception();
callbackDelegate = null;
}
No need to pinvoke FreeLibrary, user32.dll is always loaded until your program terminates.
It didn't take too long to get it done!
Here's an all good working implementation with latest fix (definitions & implementations) following Hans Passant's answer and a GitHub project.
//file Win32Api.cs
using System;
using System.Runtime.InteropServices;
using YourProjectNamespace.Hooks;
namespace YourProjectNamespace
{
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
/// <summary>
/// Pcursor info
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct PCURSORINFO
{
public Int32 Size;
public Int32 Flags;
public IntPtr Cursor;
public POINTAPI ScreenPos;
}
/// <summary>
/// Point
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct POINTAPI
{
public int x;
public int y;
}
/// <summary>
/// keyboard hook struct
/// </summary>
public struct keyboardHookStruct
{
public int dwExtraInfo;
public int flags;
public int scanCode;
public int time;
public int vkCode;
}
/// <summary>
/// Wrapper for windows 32 calls.
/// </summary>
public class Win32Api
{
public const Int32 CURSOR_SHOWING = 0x00000001;
public const int WH_KEYBOARD_LL = 13;
public const int WM_KEYDOWN = 0x100;
public const int WM_KEYUP = 0x101;
public const int WM_SYSKEYDOWN = 0x104;
public const int WM_SYSKEYUP = 0x105;
[DllImport("user32.dll")]
public static extern bool GetCursorInfo(out PCURSORINFO cinfo);
[DllImport("user32.dll")]
public static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);
[DllImport("winmm.dll")]
private static extern int mciSendString(string MciComando, string MciRetorno, int MciRetornoLeng, int CallBack);
[DllImport("user32")]
private static extern int GetKeyboardState(byte[] pbKeyState);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern short GetKeyState(int vKey);
[DllImport("user32")]
public static extern int ToAscii(
int uVirtKey,
int uScanCode,
byte[] lpbKeyState,
byte[] lpwTransKey,
int fuState);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(
IntPtr idHook,
int nCode,
int wParam,
ref keyboardHookStruct lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern int UnhookWindowsHookEx(IntPtr idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(
int idHook,
keyboardHookProc lpfn,
IntPtr hMod,
int dwThreadId);
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
//Constants
} // class Win32
**File GlobalKeyboardHook.cs**
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using log4net;
namespace ScreenRecorder.Hooks
{
public class GlobalKeyboardHook
{
private static readonly ILog log = LogManager.GetLogger(typeof (GlobalKeyboardHook).Name);
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
private static keyboardHookProc callbackDelegate;
public List<Keys> HookedKeys = new List<Keys>();
private IntPtr keyboardHook = IntPtr.Zero;
/// <summary>
public GlobalKeyboardHook()
{
Hook();
}
~GlobalKeyboardHook() {
Unhook();
}
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
public int HookProc(int nCode, int wParam, ref keyboardHookStruct lParam)
{
if (nCode >= 0)
{
var key = (Keys) lParam.vkCode;
if (HookedKeys.Contains(key))
{
var kArgs = new KeyEventArgs(key);
if ((wParam == Win32Api.WM_KEYDOWN || wParam == Win32Api.WM_SYSKEYDOWN) && (KeyDown != null))
{
KeyDown(this, kArgs);
}
else if ((wParam == Win32Api.WM_KEYUP || wParam == Win32Api.WM_SYSKEYUP) && (KeyUp != null))
{
KeyUp(this, kArgs);
}
if (kArgs.Handled)
return 1;
}
}
return Win32Api.CallNextHookEx(keyboardHook, nCode, wParam, ref lParam);
}
public void Hook()
{
// Create an instance of HookProc.
//if (callbackDelegate != null) throw new InvalidOperationException("Multiple hooks are not allowed!");
IntPtr hInstance = Win32Api.LoadLibrary("User32");
callbackDelegate = new keyboardHookProc(HookProc);
//install hook
keyboardHook = Win32Api.SetWindowsHookEx( Win32Api.WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
//If SetWindowsHookEx fails.
if (keyboardHook == IntPtr.Zero)
{
//Returns the error code returned by the last unmanaged function called using platform invoke that has the DllImportAttribute.SetLastError flag set.
var errorCode = Marshal.GetLastWin32Error();
log.Error("Unable to install keyboard hook.", new Win32Exception(errorCode));
}
}
/// <summary>
/// Unsubscribe for keyboard hook
/// </summary>
public void Unhook()
{
if (callbackDelegate == null) return;
if (keyboardHook != IntPtr.Zero)
{
//uninstall hook
var retKeyboard = Win32Api.UnhookWindowsHookEx(keyboardHook);
//reset invalid handle
keyboardHook = IntPtr.Zero;
//if failed and exception must be thrown
if (retKeyboard == 0)
{
//Returns the error code returned by the last unmanaged function called using platform invoke that has the DllImportAttribute.SetLastError flag set.
var errorCode = Marshal.GetLastWin32Error();
//Initializes and throws a new instance of the Win32Exception class with the specified error.
log.Error("Error while uninstalling keyboard hook", new Win32Exception(errorCode));
}
}
callbackDelegate = null;
}
}
}
I'm creating a software that must work when user is playing game in full screen mode. When user press a hotkey it will do something. My current hotkey implementation works only in windows. I need both KeyUp && KeyDown in full screen, i'm use TheDarkJoker94's class. Please advice.
using System;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class GlobalKeyboardHook
{
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int code, int wParam, ref keyBoardHookStruct lParam);
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, LLKeyboardHook callback, IntPtr hInstance, uint theardID);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
public delegate int LLKeyboardHook(int Code, int wParam, ref keyBoardHookStruct lParam);
public struct keyBoardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x0100;
const int WM_KEYUP = 0x0101;
const int WM_SYSKEYDOWN = 0x0104;
const int WM_SYSKEYUP = 0x0105;
LLKeyboardHook llkh;
public List<Keys> HookedKeys = new List<Keys>();
IntPtr Hook = IntPtr.Zero;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
// This is the Constructor. This is the code that runs every time you create a new GlobalKeyboardHook object
public GlobalKeyboardHook()
{
llkh = new LLKeyboardHook(HookProc);
// This starts the hook. You can leave this as comment and you have to start it manually (the thing I do in the tutorial, with the button)
// Or delete the comment mark and your hook will start automatically when your program starts (because a new GlobalKeyboardHook object is created)
// That's why there are duplicates, because you start it twice! I'm sorry, I haven't noticed this...
// hook(); <-- Choose!
}
~GlobalKeyboardHook()
{ unhook(); }
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
Hook = SetWindowsHookEx(WH_KEYBOARD_LL, llkh, hInstance, 0);
}
public void unhook()
{
UnhookWindowsHookEx(Hook);
}
public int HookProc(int Code, int wParam, ref keyBoardHookStruct lParam)
{
if (Code >= 0)
{
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key))
{
KeyEventArgs kArg = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
KeyDown(this, kArg);
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
KeyUp(this, kArg);
if (kArg.Handled)
return 1;
}
}
return CallNextHookEx(Hook, Code, wParam, ref lParam);
}
}
Here is keyboard hook code:
class globalKeyboardHook
{
#region Constant, Structure and Delegate Definitions
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
public static keyboardHookProc callbackDelegate;
public struct keyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
#endregion
#region Instance Variables
public List<Keys> HookedKeys = new List<Keys>();
private static IntPtr hhook = IntPtr.Zero;
#endregion
#region Events
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
#endregion
#region Constructors and Destructors
public globalKeyboardHook()
{
hook();
GC.KeepAlive(callbackDelegate);
}
~globalKeyboardHook()
{
unhook();
}
#endregion
#region Public Methods
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
callbackDelegate = new keyboardHookProc(hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
if (hhook == IntPtr.Zero) throw new Win32Exception();
}
public void unhook()
{
bool ok = UnhookWindowsHookEx(hhook);
if (!ok) throw new Win32Exception();
callbackDelegate = null;
}
public int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
{
if (code >= 0)
{
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key))
{
KeyEventArgs kea = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
{
KeyDown(this, kea);
}
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
{
KeyUp(this, kea);
}
if (kea.Handled)
return 1;
}
}
return CallNextHookEx(hhook, code, wParam, ref lParam); // Exception on this line
}
#endregion
#region DLL imports
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
#endregion
}
When I initialize it in Child Form constructor first nothing happens, you can press all you want but nothing gets to event:
private globalKeyboardHook kh = new globalKeyboardHook();
public FrmMainForm(FrmVideo owner)
{
InitializeComponent();
_videoForm = owner;
try
{
_videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (_videoDevices.Count == 0)
{
throw new ApplicationException();
}
foreach (FilterInfo device in _videoDevices)
{
camerasCombo.Items.Add(device.Name);
}
camerasCombo.SelectedIndex = 1;
}
catch (ApplicationException)
{
camerasCombo.Items.Add("No local capture devices");
_videoDevices = null;
}
kh.KeyUp += new KeyEventHandler(kh_KeyUp);
kh.hook();
}
void kh_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F12)
_videoForm.TakeOver = !_videoForm.TakeOver;
if (e.KeyCode == Keys.Escape)
Application.Exit();
}
Until I call piece of Aforge code for capturing from webcam, this starts streaming on Parent form:
var videoSource = new VideoCaptureDevice(_videoDevices[camerasCombo.SelectedIndex].MonikerString)
{
DesiredFrameSize = new Size(Globals.FrameWidth, Globals.FrameHeight),
DesiredFrameRate = 12
};
if (videoSource != null)
{
_videoForm.Device = videoSource;
_videoForm.Start();
button1.Enabled = false;
}
After that on any keypress I get:
CallbackOnCollectedDelegate was detected on return next hook, any ideas?
Oh, and I am unhooking on dispose of form:
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
kh.unhook();
base.Dispose(disposing);
}
This requires psychic debugging and infer something you didn't document. The oddity is that the MDA is triggered when you call CallNextHookEx(). Which is somewhat strange, the callback to your hook procedure actually worked. That delegate object wasn't collected. It is the next hook procedure call that failed.
There's a simple explanation for that: you called SetWindowsHookEx() more than once. Now using a static variable to store the delegate object will seriously bite, as static variables usually do, it is capable of storing only one delegate object. The second time you call hook(), it will overwrite the delegate of the first hook and it is thus no longer prevented from getting garbage collected. Which indeed triggers the MDA on the CallNextHookEx() since that will call the hook procedure for the first hook.
So your hook() method needs to be improved to this:
public void hook()
{
if (callbackDelegate != null)
throw new InvalidOperationException("Cannot hook more than once");
// etc..
}
It is actually not illegal to hook more than once, Windows doesn't mind. Just don't declare the variable static.
I made an application that needs to handle specific keyboard presses even when the window is not active, now I have this and it works great; however the handled keys are still being propagated to windows (not sure if my app is handling them first, so I may be backwards about that).
Is there any way to have it so my app can handle the key presses I want but not have the keys be sent to the current application/windows.
EX) I have my app open in the background monitoring the number pad for presses, every time I press a number key on the number pad I want to add that number to a text box for display purposes. Now I have chrome open and have the cursor in the address bar, I want to be able to press the number keys while having my app handle them but not having them show up in chromes address bar.
Thanks.
This is basically a very simplistic key logger.
EDIT)
Keyboard Hook
#endregion
using System;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class GlobalKeyboardHook
{
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int code, int wParam, ref keyBoardHookStruct lParam);
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, LLKeyboardHook callback, IntPtr hInstance, uint theardID);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
public delegate int LLKeyboardHook(int Code, int wParam, ref keyBoardHookStruct lParam);
public struct keyBoardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x0100;
const int WM_KEYUP = 0x0101;
const int WM_SYSKEYDOWN = 0x0104;
const int WM_SYSKEYUP = 0x0105;
LLKeyboardHook llkh;
public List<Keys> HookedKeys = new List<Keys>();
IntPtr Hook = IntPtr.Zero;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
public GlobalKeyboardHook()
{
llkh = new LLKeyboardHook(HookProc);
hook();
}
~GlobalKeyboardHook()
{ unhook(); }
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
Hook = SetWindowsHookEx(WH_KEYBOARD_LL, llkh, hInstance, 0);
}
public void unhook()
{
UnhookWindowsHookEx(Hook);
}
public int HookProc(int Code, int wParam, ref keyBoardHookStruct lParam)
{
if (Code >= 0)
{
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key))
{
KeyEventArgs kArg = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
KeyDown(this, kArg);
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
KeyUp(this, kArg);
if (kArg.Handled)
return 1;
}
}
return CallNextHookEx(Hook, Code, wParam, ref lParam);
}
}
Usage of GlobalKeyboardHook
GlobalKeyboardHook gHook;
private void Form1_Load(object sender, EventArgs e)
{
gHook = new GlobalKeyboardHook(); // Create a new GlobalKeyboardHook
// Declare a KeyDown Event
gHook.KeyDown += new KeyEventHandler(gHook_KeyDown);
// Add the keys you want to hook to the HookedKeys list
foreach (Keys key in Enum.GetValues(typeof(Keys)))
gHook.HookedKeys.Add(key);
}
// Handle the KeyDown Event
public void gHook_KeyDown(object sender, KeyEventArgs e)
{
textBox1.Text += ((char)e.KeyValue).ToString();
}
private void button1_Click(object sender, EventArgs e)
{
gHook.hook();
}
private void button2_Click(object sender, EventArgs e)
{
gHook.unhook();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
gHook.unhook();
}
In your hookProc, you must not call return CallNextHookEx(Hook, Code, wParam, ref lParam); if you dont want the WM_KEYS to propagate.
If you do call CallNextHookEx, the messages will be propagated.
Btw, you are aware that you are using a Global hook, and not a thread specific hook ? So you are capturing ALL key presses, and not only the ones relative to your app.