CallbackOnCollectedDelegate was detected even with static delegate - c#

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.

Related

Calling a managed delegate from native code [duplicate]

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;
}
}
}

How to use Unicode with the Numeric KeyPad?

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();
}

How to implement global hotkeys that work in full screen mode

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);
}
}

Background keyboard shortcuts that do not propagate to windows

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.

c# WebBrowser Flash fullscreen - ESC won't exit fullscreen

I have a swf inside a html page. If I open it with IE or FF and put it into full screen I can exit full screen with the ESC button.
Now if i try this with my WinForms app and WebBrowser (or ShockWave Flash Object) it's not working??
Any ideas?
I know it's late but Ctrl + W does the trick for me
As outlined here there is a problem with Windows Forms' ActiveX container support. The keyboard message does not get passed to the Flash player.
The solution I've come up with (based on code from here) is to write a keyboard hook to catch the Escape key and to pass a message to the Flash window to close if it's active.
First, there's the KeyHook class which provides the plumbing to listen for the Escape key.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Your.Utility
{
public class KeyBordHook
{
private const int WM_KEYDOWN = 0x100;
private const int WM_KEYUP = 0x101;
private const int WM_SYSKEYDOWN = 0x104;
private const int WM_SYSKEYUP = 0x105;
//Global event
public event KeyEventHandler OnKeyDownEvent;
public event KeyEventHandler OnKeyUpEvent;
public event KeyPressEventHandler OnKeyPressEvent;
private static int hKeyboardHook = 0;
private const int WH_KEYBOARD_LL = 13; //keyboard hook constant
private HookProc KeyboardHookProcedure; // declare keyhook event type
//declare keyhook struct
[StructLayout(LayoutKind.Sequential)]
public class KeyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern bool UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
[DllImport("user32")]
private static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState, byte[] lpwTransKey, int fuState);
[DllImport("user32")]
private static extern int GetKeyboardState(byte[] pbKeyState);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
private delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
private List<Keys> preKeys = new List<Keys>();
public KeyBordHook()
{
Start();
}
~KeyBordHook()
{
Stop();
}
public void Start()
{
//install keyboard hook
if (hKeyboardHook == 0)
{
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
//hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
Process curProcess = Process.GetCurrentProcess();
ProcessModule curModule = curProcess.MainModule;
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(curModule.ModuleName), 0);
if (hKeyboardHook == 0)
{
Stop();
throw new Exception("SetWindowsHookEx ist failed.");
}
}
}
public void Stop()
{
bool retKeyboard = true;
if (hKeyboardHook != 0)
{
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
}
//if unhook failed
if (!(retKeyboard)) throw new Exception("UnhookWindowsHookEx failed.");
}
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if ((nCode >= 0) && (OnKeyDownEvent != null || OnKeyUpEvent != null || OnKeyPressEvent != null))
{
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
if ((OnKeyDownEvent != null || OnKeyPressEvent != null) && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
if (IsCtrlAltShiftKeys(keyData) && preKeys.IndexOf(keyData) == -1)
{
preKeys.Add(keyData);
}
}
if (OnKeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(GetDownKeys(keyData));
OnKeyDownEvent(this, e);
}
if (OnKeyPressEvent != null && wParam == WM_KEYDOWN)
{
byte[] keyState = new byte[256];
GetKeyboardState(keyState);
byte[] inBuffer = new byte[2];
if (ToAscii(MyKeyboardHookStruct.vkCode,
MyKeyboardHookStruct.scanCode,
keyState,
inBuffer,
MyKeyboardHookStruct.flags) == 1)
{
KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
OnKeyPressEvent(this, e);
}
}
if ((OnKeyDownEvent != null || OnKeyPressEvent != null) && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
if (IsCtrlAltShiftKeys(keyData))
{
for (int i = preKeys.Count - 1; i >= 0; i--)
{
if (preKeys[i] == keyData)
{
preKeys.RemoveAt(i);
}
}
}
}
if (OnKeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(GetDownKeys(keyData));
OnKeyUpEvent(this, e);
}
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
private Keys GetDownKeys(Keys key)
{
Keys rtnKey = Keys.None;
foreach (Keys keyTemp in preKeys)
{
switch (keyTemp)
{
case Keys.LControlKey:
case Keys.RControlKey:
rtnKey = rtnKey | Keys.Control;
break;
case Keys.LMenu:
case Keys.RMenu:
rtnKey = rtnKey | Keys.Alt;
break;
case Keys.LShiftKey:
case Keys.RShiftKey:
rtnKey = rtnKey | Keys.Shift;
break;
default:
break;
}
}
rtnKey = rtnKey | key;
return rtnKey;
}
private Boolean IsCtrlAltShiftKeys(Keys key)
{
switch (key)
{
case Keys.LControlKey:
case Keys.RControlKey:
case Keys.LMenu:
case Keys.RMenu:
case Keys.LShiftKey:
case Keys.RShiftKey:
return true;
default:
return false;
}
}
}
}
Next, you need to create an instance of the KeyHook class in your Windows Form ...
// inside Form class ...
private readonly KeyBordHook _keyBordHook = new KeyBordHook();
private void InitKeyHook()
{
_keyBordHook.OnKeyPressEvent += new KeyPressEventHandler(_KeyBordHook_OnKeyPressEvent);
_keyBordHook.Start();
}
void _KeyBordHook_OnKeyPressEvent(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == Convert.ToChar(Keys.Escape))
WindowHelper.CloseWindowIfActive("Flash");
}
public void StartKeyListening()
{
InitKeyHook();
}
public void StopKeyListening()
{
_keyBordHook.Stop();
}
And, the code to actually close the Flash Window...
public sealed class WindowHelper
{
[DllImport("USER32.dll", SetLastError = true)]
private static extern IntPtr GetForegroundWindow();
[DllImport("USER32.dll", SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
static uint WM_CLOSE = 0x10;
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("USER32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
private static bool CloseWindow(IntPtr hWnd)
{
bool returnValue = PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
if (!returnValue)
throw new Win32Exception(Marshal.GetLastWin32Error());
return true;
}
public static void CloseWindowIfActive(string windowTitle)
{
const int nChars = 256;
StringBuilder buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, buff, nChars) <= 0) return;
if (buff.ToString().ToLower().IndexOf(windowTitle.ToLower()) > -1)
CloseWindow(handle);
}
}
I'm sure there's improvements to be made in all of the above but so far, this solution appears to work for me.
Note that this code allows you to record all key strokes across all apps so it's definitely overkill and may not be desirable to integrate into a customer bound application!
Did you try F11? That's the default for IE.

Categories

Resources