Controls cannot be accessed inside my function - c#

just a quick question, can you tell me why I can't access my controls like popup or textbox or the 'this' inside the function that has a comment, please check it out. I really need to know why asap. Thank you so much!
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
DispatcherTimer timer;
[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,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
public const int WH_KEYBOARD_LL = 13;
public const int WM_KEYDOWN = 0x0100;
public static LowLevelKeyboardProc _proc = HookCallback;
public static IntPtr _hookID = IntPtr.Zero;
public const uint VK_NUMLOCK = 0x90;
public const uint VK_CAPITAL = 0x14;
public MainWindow()
{
MouseDown += delegate { DragMove(); };
InitializeComponent();
_hookID = SetHook(_proc);
}
public void mainForm_Loaded(object sender, RoutedEventArgs e)
{
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(2000);
timer.Tick += timer_Tick;
}
public void timer_Tick(object sender, EventArgs e)
{
Popup1.IsOpen = false;
timer.Stop();
}
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);
}
}
public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (vkCode == VK_CAPITAL)
{
if (Console.CapsLock == true)
{
// I WANT TO ACCESS MY CONTROLS HERE (popup, textbox, etc... this);
}
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
protected override void OnClosed(EventArgs e)
{
UnhookWindowsHookEx(_hookID);
base.OnClosed(e);
}
}

It is a static method. "this" and the controls belong to the instance and can't be seen inside of a static method.
With the use of those .dlls, I can't provide a bulletproof method of accessing it. I would not advise resorting to hopes and dreams, but in the case where the MainWindow is the active window, you could do something like the following:
public static void StaticMainWindowMethod(string incomingMessage)
{
var activeWindow = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
if (activeWindow != null)
{
var mainWindow = activeWindow as MainWindow;
if (mainWindow != null)
{
mainWindow.InstanceMainWindowMethod(incomingMessage);
}
}
}
protected void InstanceMainWindowMethod(string passedFromStaticMessage)
{
this.MainTextBox.Text = passedFromStaticMessage;
}
The idea is that you need to get the instance of the window in order to get at its properties/controls. Depending on how your application is designed, you may be able to get at it through application level properties. Its reliability is really up to the design of the application.

First, add a static event to your window:
public static event EventHandler CapsLockEnabled;
Next, add a handler for this event:
public MainWindow()
{
MouseDown += delegate { DragMove(); };
InitializeComponent();
_hookID = SetHook(_proc);
CapsLockEnabled += (sender, e) => { Console.WriteLine("caps lock enabled"); };
}
Your event handler, because it's defined on an instance, has full access to all of the window's controls.
Finally, raise the event from your HookCallback method:
public static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (vkCode == VK_CAPITAL)
{
if (Console.CapsLock == true)
{
var handler = CapsLockEnabled;
if (handler != null)
{
handler(typeof(MainWindow), EventArgs.Empty);
}
}
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}

Related

Is there a way to do a lowlevelkeyboardproc or lowlevelmouseproc locally instead of globally?

I've been having trouble with a certain problem. I've been disabling left and right mouse clicks for a particular application and my C# code looks like below...
My problem is that even though this works fine for disabling the mouse on a low level, isn't there a way in where I could disable it on a low level just for when the application is focused and therefor running?
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace GlobalLowLevelHooks
{
public class MouseHook
{
public bool enabled = false;
public delegate IntPtr MouseHookHandler(int nCode, IntPtr wParam, IntPtr lParam);
public MouseHookHandler hookHandler;
public delegate void MouseHookCallback(MSLLHOOKSTRUCT mouseStruct);
#region Events
public event MouseHookCallback LeftButtonDown;
public event MouseHookCallback LeftButtonUp;
public event MouseHookCallback RightButtonDown;
public event MouseHookCallback RightButtonUp;
public event MouseHookCallback MouseMove;
public event MouseHookCallback MouseWheel;
public event MouseHookCallback DoubleClick;
public event MouseHookCallback MiddleButtonDown;
public event MouseHookCallback MiddleButtonUp;
#endregion
public IntPtr hookID = IntPtr.Zero;
public void Enable()
{
hookHandler = HookFunc;
hookID = SetHook(hookHandler);
enabled = true;
}
public void Disable()
{
if (hookID == IntPtr.Zero)
return;
UnhookWindowsHookEx(hookID);
hookID = IntPtr.Zero;
enabled = false;
}
~MouseHook()
{
Enable();
}
private IntPtr SetHook(MouseHookHandler proc)
{
using (ProcessModule module = Process.GetCurrentProcess().MainModule)
return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(module.ModuleName), 0);
}
private IntPtr HookFunc(int nCode, IntPtr wParam, IntPtr lParam)
{
// parse system messages
if (nCode >= 0)
{
if (MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
{
return (IntPtr)1;
}
if (MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam)
{
return (IntPtr)1;
}
if (MouseMessages.WM_MBUTTONDOWN == (MouseMessages)wParam)
{
return (IntPtr)1;
}
if (MouseMessages.WM_LBUTTONDBLCLK == (MouseMessages)wParam)
{
return (IntPtr)1;
}
}
return CallNextHookEx(hookID, nCode, wParam, lParam);
}
#region WinAPI
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205,
WM_LBUTTONDBLCLK = 0x0203,
WM_MBUTTONDOWN = 0x0207,
WM_MBUTTONUP = 0x0208
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
public struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
MouseHookHandler 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)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
#endregion
}
}

Globally Detecting Key Presses and Mouse Clicks

I am attempting to create a program to globally detect key presses and mouse clicks. I have googled around and found solutions for both and got them working in the same program. But I would like to combine the two separate classes into one class that does both. I have fiddled around with it for a bit and can't figure out how to do it. Does anyone know how to solve this? Thanks in advance!
Mouse clicks class:
public static class MouseHook
{
public static event EventHandler imputAction = delegate { };
public static void Start()
{
_hookID = SetHook(_proc);
}
public static void stop()
{
UnhookWindowsHookEx(_hookID);
}
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
private static IntPtr SetHook(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_MOUSE_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
{
MyGlobals.clickType = 1;
imputAction(null, new EventArgs());
}
if (nCode >= 0 && MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam)
{
MyGlobals.clickType = 2;
imputAction(null, new EventArgs());
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_RBUTTONDOWN = 0x0204,
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
Keyboard class:
class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static event EventHandler imputAction = delegate { };
public static void Start()
{
_hookID = SetHook(_proc);
}
public static void stop()
{
UnhookWindowsHookEx(_hookID);
}
private 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);
}
}
private delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
MyGlobals.characters = Convert.ToString((Keys)vkCode);
MyGlobals.clickType = 3;
imputAction(null, new EventArgs());
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
I am starting these like this:
MouseHook.Start();
MouseHook.MouseAction += new EventHandler(Event);
InterceptKeys.Start();
InterceptKeys.MouseAction += new EventHandler(Event)
The function receiving both events:
private void Event(object sender, EventArgs e)
{
//do stuff
}
I am also trying to do scrolling, I got it to detect scrolling by adding these lines. But I can't figure out how to read the scroll direction or value and assign it to a variable.
WM_MOUSEWHEEL = 0x020A,
if (nCode >= 0 && MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam)
{
MyGlobals.imputType = 4;
// Here is where I need to set a variable to the direction or
// value of scrolling but I cant figure out how
imputAction(null, new EventArgs());
}

Keyword 'this' is not valid in a static property, static method, or static field initializer

I'm using the following code:
public partial class ThisAddIn
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static IntPtr hookId = IntPtr.Zero;
private delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam);
private static HookProcedure procedure = HookCallback;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProcedure lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
hookId = SetHook(procedure);
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
UnhookWindowsHookEx(hookId);
}
private static IntPtr SetHook(HookProcedure procedure)
{
using (Process process = Process.GetCurrentProcess())
using (ProcessModule module = process.MainModule)
return SetWindowsHookEx(WH_KEYBOARD_LL, procedure, GetModuleHandle(module.ModuleName), 0);
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int pointerCode = Marshal.ReadInt32(lParam);
string pressedKey = ((Keys)pointerCode).ToString();
//Do some sort of processing on key press
var thread = new Thread(() =>
{ MessageBox.Show(pressedKey);
if (pressedKey.Equals("Space") || pressedKey.Equals("Tab"))
{
***Word.Range rng = this.Application.ActiveDocument.Words.Last;
rng.Select();
rng.Copy();
String input = Clipboard.GetText(TextDataFormat.Text);
}
});
thread.Start();
}
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
}
I'm getting the following error for the line marked with *** :
Error 1 Keyword 'this' is not valid in a static property, static
method, or static field initializer.
Please advice
The this keyword in the HookCallback static method is a problem:
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int pointerCode = Marshal.ReadInt32(lParam);
string pressedKey = ((Keys)pointerCode).ToString();
//Do some sort of processing on key press
var thread = new Thread(() =>
{ MessageBox.Show(pressedKey);
if (pressedKey.Equals("Space") || pressedKey.Equals("Tab"))
{
***Word.Range rng = this.Application.ActiveDocument.Words.Last;
rng.Select();
rng.Copy();
String input = Clipboard.GetText(TextDataFormat.Text);
}
});
thread.Start();
}
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
If Application.ActiveDocument is not available in a static context (ie it does not work without this in front of it), then you will need to pass in the values you need.

Windows 7 Chromium Embedded Framework - override mouse clicks

I need to ignore all right clicks within my application and Chromium Embedded Framework.
Now I had this working great on an old version which used the WebBrowser widget, but now after switching over to CEF browser, the KeyMessageFilter does not get the message when the CEF browser is in focus. The first related post seems to say that the CEF keeps hold of the event and does not pass it on to the application.
However, I don't understand the answer. It seems to be in Basic or something....
Here is my KeyMessageFilter code
public class KeyMessageFilter : IMessageFilter
{
private enum KeyMessages
{
WM_KEYFIRST = 0x100,
WM_KEYDOWN = 0x100,
WM_KEYUP = 0x101,
WM_CHAR = 0x102,
WM_SYSKEYDOWN = 0x0104,
WM_SYSKEYUP = 0x0105,
WM_SYSCHAR = 0x0106,
WM_MOUSEWHEEL = 0x20a
}
[DllImport("user32.dll")]
private static extern IntPtr GetParent(IntPtr hwnd);
// We check the events agains this control to only handle
// key event that happend inside this control.
Control _control;
public KeyMessageFilter()
{ }
public KeyMessageFilter(Control c)
{
_control = c;
}
public bool PreFilterMessage(ref Message m)
{
Console.WriteLine(m.Msg);
// Filter out WM_NCRBUTTONDOWN/UP/DBLCLK
if (m.Msg == 0xA4 || m.Msg == 0xA5 || m.Msg == 0xA6) return true;
// Filter out WM_RBUTTONDOWN/UP/DBLCLK
if (m.Msg == 0x204 || m.Msg == 0x205 || m.Msg == 0x206) return true;
if (m.Msg == (int)KeyMessages.WM_MOUSEWHEEL)
{
if (Control.ModifierKeys == Keys.Control)
{
return true;
}
}
if (m.Msg == (int)KeyMessages.WM_KEYDOWN)
{
if (_control != null)
{
IntPtr hwnd = m.HWnd;
IntPtr handle = _control.Handle;
while (hwnd != IntPtr.Zero && handle != hwnd)
{
hwnd = GetParent(hwnd);
}
if (hwnd == IntPtr.Zero) // Didn't found the window. We are not interested in the event.
return false;
}
Keys key = (Keys)m.WParam;
if (key.Equals(Keys.Tab))
{
return true;
}
if (Control.ModifierKeys == Keys.Control)
{
switch (key)
{
case Keys.Oemplus:
return true;
case Keys.OemMinus:
return true;
}
}
}
return false;
}
}
Related Posts
Ignoring keys in winAPi: Stack Overflow question Override mouse using Chromium embedded framework
Ignoring keys: Stack Overflow question How to detect the currently pressed key?
Okay here is how I did it.
I used a low level hook for the mouse in the Program.cs file.
I then used a Setting to say wether my App is focused or not this uses the Form.Activated / Deactivated events (other wise it would swallow all right clicks on the computer while the App is running, BAD EXPERIENCE).
Relevant Links:Global mouse event handler
Program.cs
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
Main(string[] args){
.......
_hookID = SetHook(_proc);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ContainerForm());
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_MOUSE_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 &&
(MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam || MouseMessages.WM_RBUTTONUP == (MouseMessages)wParam))
{
//If the app has focuse swallow event
if (Properties.Settings.Default.AppFocus) {
return new IntPtr(-1);
}
else
{
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private const int WH_MOUSE = 7;
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
Form.cs
private void onActivated(object sender, EventArgs e)
{
Properties.Settings.Default.AppFocus = true;
}
private void onDeactivated(object sender, EventArgs e)
{
Properties.Settings.Default.AppFocus = false;
}
This likely isn't the best way to do this, however I was unable to get the CEFBrowser object to ignore right clicks, just as I was unable to get the Form as a whole to ignore right clicks, or even the Application to ignore (non-low level) Mouse Events. This is the work around of someone who is completely new to Windows so don't take this as the best code ever but it is working for me.

Global keyboard hook works, but global shell hook fails

I have two pieces of code which are very similar in that they register global hooks.
Registering global keyboard hook:
public class KeyboardHook : IDisposable
{
#region Events
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public delegate void HookEventHandler(object sender, KeyboardHookEventArgs e);
public event HookEventHandler KeyDown;
public event HookEventHandler KeyUp;
#endregion
#region Constants
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
private LowLevelKeyboardProc _proc = null;
private static IntPtr _hookID = IntPtr.Zero;
#endregion
#region Imports
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
#endregion
#region Constructor
public KeyboardHook()
{
_proc = new LowLevelKeyboardProc(HookCallback);
_hookID = SetHook(_proc);
}
#endregion
#region Methods
private IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
//if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
{
int vkCode = Marshal.ReadInt32(lParam);
WinForms.Keys key = (WinForms.Keys)vkCode;
if (this.KeyDown != null)
this.KeyDown(this, new KeyboardHookEventArgs(vkCode));
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
#endregion
#region Destructor
public void Dispose()
{
UnhookWindowsHookEx(_hookID);
}
#endregion
}
Registering global shell hook:
public enum ShellEvents
{
HSHELL_WINDOWCREATED = 1,
HSHELL_WINDOWDESTROYED = 2,
HSHELL_ACTIVATESHELLWINDOW = 3,
HSHELL_WINDOWACTIVATED = 4,
HSHELL_GETMINRECT = 5,
HSHELL_REDRAW = 6,
HSHELL_TASKMAN = 7,
HSHELL_LANGUAGE = 8,
HSHELL_ACCESSIBILITYSTATE = 11
}
public class ShellHook
{
#region Events
private delegate IntPtr ShellProc(int nCode, IntPtr wParam, IntPtr lParam);
public delegate void HookEventHandler(object sender, ShellHookEventArgs e);
public event HookEventHandler WindowActivated;
#endregion
#region Constants
private const int WH_SHELL = 10;
private ShellProc _proc = null;
private static IntPtr _hookID = IntPtr.Zero;
#endregion
#region Imports
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
ShellProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
#endregion
#region Fields
#endregion
#region Constructor
public ShellHook()
{
_proc = new ShellProc(HookCallback);
_hookID = SetHook(_proc);
}
#endregion
#region Methods
private IntPtr SetHook(ShellProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_SHELL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam.Equals(ShellEvents.HSHELL_WINDOWACTIVATED))
{
string windowTitle = GetWindowTitle(wParam);
if (this.WindowActivated != null)
this.WindowActivated(this, new ShellHookEventArgs(windowTitle));
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private string GetWindowTitle(IntPtr hWnd)
{
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
if (GetWindowText(hWnd, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
#endregion
#region Destructor
public void Dispose()
{
UnhookWindowsHookEx(_hookID);
}
#endregion
}
For some reason, the keyboard hook works, but the shell hook fails (SetWindowsHookEx returns 0 and the callback is never reached).
Any ideas why?
I think I'll answer my own question. This quote from pinvoke.net explains why...
However, you cannot implement global hooks in Microsoft .NET Framework
except low level hooks. To install a global hook, a hook must have a
native dynamic-link library (DLL) export to inject itself in another
process that requires a valid, consistent function to call into. This
requires a DLL export, which .NET Framework does not support. Managed
code has no concept of a consistent value for a function pointer
because these function pointers are proxies that are built
dynamically.

Categories