// ********************************* // 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 } }