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.
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 am trying to use global hooks to detect input events (a mouse click, keyboard keys) and use the detection to trigger another event, such as storing the mouse coordinates. The code I've found does work, but I cannot call on another method in the same form because the hookProc is set to static.
public static IntPtr hookProc(int code, IntPtr wParam, IntPtr lParam)
{
if (code >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (vkCode.ToString() == "162") //162 is ASCI CTRL
{
MessageBox.Show("CTRL");
Form1 Trigger = new Form1(); // Supposed to allow calls to Form1, specifically textBox1
Trigger.textBox1.Text = "3"; // Will not change the Text value
}
return (IntPtr)1;
}
else
return CallNextHookEx(hhook, code, (int)wParam, lParam);
}
I know the code works because the CTRL MessageBox will show. I know textBox1.Text = "3"; because I can use the code elsewhere in an instance without issue. I searched and found I need to create a new instance of form1 in order to call on methods found outside the static hookProc. My code does not give me any build errors but does not do anything in regards to textBox1. Even if I wanted to call on another method when CTRL is detected, I cannot link to anything outside hookProc. Help?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Demo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
SetHook(); // Set the hook
}
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
UnHook(); // Remove the hook
}
// GLOBAL HOOK
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, int wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
const int WH_KEYBOARD_LL = 13; // Number of global LowLevel- hook on the keyboard
const int WM_KEYDOWN = 0x100; // Messages pressing
private LowLevelKeyboardProc _proc = hookProc;
private static IntPtr hhook = IntPtr.Zero;
public void SetHook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, _proc, hInstance, 0);
}
public static void UnHook()
{
UnhookWindowsHookEx(hhook);
}
public static IntPtr hookProc(int code, IntPtr wParam, IntPtr lParam)
{
if (code >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (vkCode.ToString() == "162") //162 is ASCI CTRL
{
MessageBox.Show("CTRL");
Form1 Trigger = new Form1(); // Allows called to Form1, specifically textBox3
Trigger.TriggeredEvent(); //Visual confirmation of CTRL has been detected
Trigger.textBox1.Text = "3";
}
return (IntPtr)1;
}
else
return CallNextHookEx(hhook, code, (int)wParam, lParam);
}
}
}
As you would have guessed, the issue is that you are creating a new instance of Form1 and attempting to update the TextBox Value in the new instance. What you require is the same instance which is currently open.
Assuming you have only a single instance of Form1, you can use Application.OpenForms to get collection of all Open Forms and use Enumerable.OfType to filter the Form1 Type.
For Example
var formInstance = Application.OpenForms.OfType<Form1>().Single();
formInstance.TriggeredEvent();
formInstance.textBox1.Text = "3";
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();
}
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.