// *********************************
// Message from Original Author:
//
// 2008 Jose Menendez Poo
// Please give me credit if you use this code. It's all I ask.
// Contact me for more info: menendezpoo@gmail.com
// *********************************
//
// Original project from http://ribbon.codeplex.com/
// Continue to support and maintain by http://officeribbon.codeplex.com/
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Permissions;
namespace System.Windows.Forms.RibbonHelpers
{
internal class GlobalHook
: IDisposable
{
#region Subclasses
///
/// Types of available hooks
///
public enum HookTypes
{
///
/// Installs a mouse hook
///
Mouse,
///
/// Installs a keyboard hook
///
Keyboard
}
#endregion
#region Fields
private HookProcCallBack _HookProc;
private IntPtr _handle;
private HookTypes _hookType;
#endregion
#region Events
///
/// Occours when the hook captures a mouse click
///
public event MouseEventHandler MouseClick;
///
/// Occours when the hook captures a mouse double click
///
public event MouseEventHandler MouseDoubleClick;
///
/// Occours when the hook captures the mouse wheel
///
public event MouseEventHandler MouseWheel;
///
/// Occours when the hook captures the press of a mouse button
///
public event MouseEventHandler MouseDown;
///
/// Occours when the hook captures the release of a mouse button
///
public event MouseEventHandler MouseUp;
///
/// Occours when the hook captures the mouse moving over the screen
///
public event MouseEventHandler MouseMove;
///
/// Occours when a key is pressed
///
public event KeyEventHandler KeyDown;
///
/// Occours when a key is released
///
public event KeyEventHandler KeyUp;
///
/// Occours when a key is pressed
///
public event KeyPressEventHandler KeyPress;
#endregion
#region Delegates
///
/// Delegate used to recieve HookProc
///
///
///
///
///
internal delegate IntPtr HookProcCallBack(int nCode, IntPtr wParam, IntPtr lParam);
#endregion
#region Ctor
///
/// Creates a new Hook of the specified type
///
///
public GlobalHook(HookTypes hookType)
{
_hookType = hookType;
InstallHook();
}
~GlobalHook()
{
Dispose(false);
}
#endregion
#region Properties
/////
///// Gets the type of this hook
/////
//public HookTypes HookType { get { return _hookType; } }
/////
///// Gets the handle of the hook
/////
//public IntPtr Handle { get { return _handle; } }
#endregion
#region Event Triggers
///
/// Raises the event
///
///
protected virtual void OnMouseClick(MouseEventArgs e)
{
if (MouseClick != null)
{
MouseClick(this, e);
}
}
///
/// Raises the event
///
///
protected virtual void OnMouseDoubleClick(MouseEventArgs e)
{
if (MouseDoubleClick != null)
{
MouseDoubleClick(this, e);
}
}
///
/// Raises the event
///
///
protected virtual void OnMouseWheel(MouseEventArgs e)
{
if (MouseWheel != null)
{
MouseWheel(this, e);
}
}
///
/// Raises the event
///
///
protected virtual void OnMouseDown(MouseEventArgs e)
{
if (MouseDown != null)
{
MouseDown(this, e);
}
}
///
/// Raises the event
///
///
protected virtual void OnMouseUp(MouseEventArgs e)
{
if (MouseUp != null)
{
MouseUp(this, e);
}
}
///
/// Raises the event
///
///
protected virtual void OnMouseMove(MouseEventArgs e)
{
if (MouseMove != null)
{
MouseMove(this, e);
}
}
///
/// Raises the event
///
/// Event Data
protected virtual void OnKeyDown(KeyEventArgs e)
{
if (KeyDown != null)
{
KeyDown(this, e);
}
}
///
/// Raises the event
///
/// Event Data
protected virtual void OnKeyUp(KeyEventArgs e)
{
if (KeyUp != null)
{
KeyUp(this, e);
}
}
///
/// Raises the event
///
/// Event Data
protected virtual void OnKeyPress(KeyPressEventArgs e)
{
if (KeyPress != null)
{
KeyPress(this, e);
}
}
#endregion
#region Methods
///
/// Recieves the actual unsafe mouse hook procedure
///
///
///
///
///
private IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam)
{
if (code < 0)
{
return WinApi.CallNextHookEx(_handle, code, wParam, lParam);
}
switch (_hookType)
{
case HookTypes.Mouse:
return MouseProc(code, wParam, lParam);
case HookTypes.Keyboard:
return KeyboardProc(code, wParam, lParam);
default:
throw new ArgumentException("HookType not supported");
}
}
///
/// Recieves the actual unsafe keyboard hook procedure
///
///
///
///
///
private IntPtr KeyboardProc(int code, IntPtr wParam, IntPtr lParam)
{
WinApi.KeyboardLLHookStruct hookStruct = (WinApi.KeyboardLLHookStruct)Marshal.PtrToStructure(lParam, typeof(WinApi.KeyboardLLHookStruct));
int msg = wParam.ToInt32();
bool handled = false;
if (msg == WinApi.WM_KEYDOWN || msg == WinApi.WM_SYSKEYDOWN)
{
KeyEventArgs e = new KeyEventArgs((Keys)hookStruct.vkCode);
OnKeyDown(e);
handled = e.Handled;
}
else if (msg == WinApi.WM_KEYUP || msg == WinApi.WM_SYSKEYUP)
{
KeyEventArgs e = new KeyEventArgs((Keys)hookStruct.vkCode);
OnKeyUp(e);
handled = e.Handled;
}
if (msg == WinApi.WM_KEYDOWN && KeyPress != null)
{
byte[] keyState = new byte[256];
byte[] buffer = new byte[2];
WinApi.GetKeyboardState(keyState);
int conversion = WinApi.ToAscii(hookStruct.vkCode, hookStruct.scanCode, keyState, buffer, hookStruct.flags);
if (conversion == 1 || conversion == 2)
{
bool shift = (WinApi.GetKeyState(WinApi.VK_SHIFT) & 0x80) == 0x80;
bool capital = WinApi.GetKeyState(WinApi.VK_CAPITAL) != 0;
char c = (char)buffer[0];
if ((shift ^ capital) && Char.IsLetter(c))
{
c = Char.ToUpper(c);
}
KeyPressEventArgs e = new KeyPressEventArgs(c);
OnKeyPress(e);
handled |= e.Handled;
}
}
return handled ? (IntPtr)1 : WinApi.CallNextHookEx(_handle, code, wParam, lParam);
}
///
/// Processes Mouse Procedures
///
///
///
///
///
private IntPtr MouseProc(int code, IntPtr wParam, IntPtr lParam)
{
WinApi.MouseLLHookStruct hookStruct = (WinApi.MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(WinApi.MouseLLHookStruct));
int msg = wParam.ToInt32();
int x = hookStruct.pt.x;
int y = hookStruct.pt.y;
int delta = (short)((hookStruct.mouseData >> 16) & 0xffff);
if (msg == WinApi.WM_MOUSEWHEEL)
{
OnMouseWheel(new MouseEventArgs(MouseButtons.None, 0, x, y, delta));
}
else if (msg == WinApi.WM_MOUSEMOVE)
{
OnMouseMove(new MouseEventArgs(MouseButtons.None, 0, x, y, delta));
}
else if (msg == WinApi.WM_LBUTTONDBLCLK)
{
OnMouseDoubleClick(new MouseEventArgs(MouseButtons.Left, 0, x, y, delta));
}
else if (msg == WinApi.WM_LBUTTONDOWN)
{
OnMouseDown(new MouseEventArgs(MouseButtons.Left, 0, x, y, delta));
}
else if (msg == WinApi.WM_LBUTTONUP)
{
OnMouseUp(new MouseEventArgs(MouseButtons.Left, 0, x, y, delta));
OnMouseClick(new MouseEventArgs(MouseButtons.Left, 0, x, y, delta));
}
else if (msg == WinApi.WM_MBUTTONDBLCLK)
{
OnMouseDoubleClick(new MouseEventArgs(MouseButtons.Middle, 0, x, y, delta));
}
else if (msg == WinApi.WM_MBUTTONDOWN)
{
OnMouseDown(new MouseEventArgs(MouseButtons.Middle, 0, x, y, delta));
}
else if (msg == WinApi.WM_MBUTTONUP)
{
OnMouseUp(new MouseEventArgs(MouseButtons.Middle, 0, x, y, delta));
}
else if (msg == WinApi.WM_RBUTTONDBLCLK)
{
OnMouseDoubleClick(new MouseEventArgs(MouseButtons.Right, 0, x, y, delta));
}
else if (msg == WinApi.WM_RBUTTONDOWN)
{
OnMouseDown(new MouseEventArgs(MouseButtons.Right, 0, x, y, delta));
}
else if (msg == WinApi.WM_RBUTTONUP)
{
OnMouseUp(new MouseEventArgs(MouseButtons.Right, 0, x, y, delta));
}
else if (msg == WinApi.WM_XBUTTONDBLCLK)
{
OnMouseDoubleClick(new MouseEventArgs(MouseButtons.XButton1, 0, x, y, delta));
}
else if (msg == WinApi.WM_XBUTTONDOWN)
{
OnMouseDown(new MouseEventArgs(MouseButtons.XButton1, 0, x, y, delta));
}
else if (msg == WinApi.WM_XBUTTONUP)
{
OnMouseUp(new MouseEventArgs(MouseButtons.XButton1, 0, x, y, delta));
}
return WinApi.CallNextHookEx(_handle, code, wParam, lParam);
}
///
/// Installs the actual unsafe hook
///
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
private void InstallHook()
{
// Error check
if (_handle != IntPtr.Zero)
throw new InvalidOperationException("Hook is already installed");
#region htype
int htype = 0;
switch (_hookType)
{
case HookTypes.Mouse:
htype = WinApi.WH_MOUSE_LL;
break;
case HookTypes.Keyboard:
htype = WinApi.WH_KEYBOARD_LL;
break;
default:
throw new ArgumentException("HookType is not supported");
}
#endregion
// Delegate to recieve message
_HookProc = HookProc;
// Hook
// Ed Obeda suggestion for .net 4.0
//_hHook = WinApi.SetWindowsHookEx(htype, _HookProc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
_handle = WinApi.SetWindowsHookEx(htype, _HookProc, Process.GetCurrentProcess().MainModule.BaseAddress, 0);
int lastWin32Error = Marshal.GetLastWin32Error();
// Error check
if (_handle == IntPtr.Zero) throw new Win32Exception(lastWin32Error);
}
///
/// Unhooks the hook
///
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
private void Unhook()
{
if (_handle != IntPtr.Zero)
{
//bool ret = WinApi.UnhookWindowsHookEx(Handle);
//if (ret == false)
// throw new Win32Exception(Marshal.GetLastWin32Error());
//_hHook = 0;
try
{
//Fix submitted by Simon Dallmair to handle win32 error when closing the form in vista
if (!WinApi.UnhookWindowsHookEx(_handle))
{
int lastWin32Error = Marshal.GetLastWin32Error();
Win32Exception ex = new Win32Exception(lastWin32Error);
if (ex.NativeErrorCode != 0)
throw ex;
}
_handle = IntPtr.Zero;
}
catch (Exception)
{
}
}
}
#endregion
#region IDisposable Members
///
/// Dispose
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Clean up any resources being used.
///
/// true if managed resources should be disposed; otherwise, false.
protected virtual void Dispose(bool disposing)
{
if (_handle != IntPtr.Zero)
{
Unhook();
}
}
#endregion
}
}