GlobalMouseHook.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. using System;
  2. using System.ComponentModel;
  3. using System.Diagnostics;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Runtime.InteropServices;
  6. using System.Windows;
  7. namespace PixiEditor.Helpers
  8. {
  9. // see https://stackoverflow.com/questions/22659925/how-to-capture-mouseup-event-outside-the-wpf-window
  10. [ExcludeFromCodeCoverage]
  11. public static class GlobalMouseHook
  12. {
  13. private delegate int HookProc(int nCode, int wParam, IntPtr lParam);
  14. private static int _mouseHookHandle;
  15. private static HookProc _mouseDelegate;
  16. private static event MouseUpEventHandler MouseUp;
  17. public static event MouseUpEventHandler OnMouseUp
  18. {
  19. add
  20. {
  21. Subscribe();
  22. MouseUp += value;
  23. }
  24. remove
  25. {
  26. MouseUp -= value;
  27. Unsubscribe();
  28. }
  29. }
  30. public static void RaiseMouseUp()
  31. {
  32. MouseUp?.Invoke(default, default);
  33. }
  34. private static void Unsubscribe()
  35. {
  36. if (_mouseHookHandle != 0)
  37. {
  38. int result = UnhookWindowsHookEx(_mouseHookHandle);
  39. _mouseHookHandle = 0;
  40. _mouseDelegate = null;
  41. if (result == 0)
  42. {
  43. int errorCode = Marshal.GetLastWin32Error();
  44. throw new Win32Exception(errorCode);
  45. }
  46. }
  47. }
  48. private static void Subscribe()
  49. {
  50. if (_mouseHookHandle == 0)
  51. {
  52. _mouseDelegate = MouseHookProc;
  53. _mouseHookHandle = SetWindowsHookEx(
  54. WH_MOUSE_LL,
  55. _mouseDelegate,
  56. GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName),
  57. 0);
  58. if (_mouseHookHandle == 0)
  59. {
  60. int errorCode = Marshal.GetLastWin32Error();
  61. throw new Win32Exception(errorCode);
  62. }
  63. }
  64. }
  65. private static int MouseHookProc(int nCode, int wParam, IntPtr lParam)
  66. {
  67. if (nCode >= 0)
  68. {
  69. MSLLHOOKSTRUCT mouseHookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
  70. if (wParam == WM_LBUTTONUP)
  71. {
  72. if (MouseUp != null)
  73. {
  74. MouseUp.Invoke(null, new Point(mouseHookStruct.pt.x, mouseHookStruct.pt.y));
  75. }
  76. }
  77. }
  78. return CallNextHookEx(_mouseHookHandle, nCode, wParam, lParam);
  79. }
  80. private const int WH_MOUSE_LL = 14;
  81. private const int WM_LBUTTONUP = 0x0202;
  82. [StructLayout(LayoutKind.Sequential)]
  83. private struct POINT
  84. {
  85. public int x;
  86. public int y;
  87. }
  88. [StructLayout(LayoutKind.Sequential)]
  89. private struct MSLLHOOKSTRUCT
  90. {
  91. public POINT pt;
  92. public uint mouseData;
  93. public uint flags;
  94. public uint time;
  95. public IntPtr dwExtraInfo;
  96. }
  97. [DllImport("user32.dll",
  98. CharSet = CharSet.Auto,
  99. CallingConvention = CallingConvention.StdCall,
  100. SetLastError = true)]
  101. private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
  102. [DllImport(
  103. "user32.dll",
  104. CharSet = CharSet.Auto,
  105. CallingConvention = CallingConvention.StdCall,
  106. SetLastError = true)]
  107. private static extern int UnhookWindowsHookEx(int idHook);
  108. [DllImport(
  109. "user32.dll",
  110. CharSet = CharSet.Auto,
  111. CallingConvention = CallingConvention.StdCall)]
  112. private static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
  113. [DllImport("kernel32.dll")]
  114. private static extern IntPtr GetModuleHandle(string name);
  115. }
  116. public delegate void MouseUpEventHandler(object sender, Point p);
  117. }