当 UWP 桌面应用程序失去焦点时,不会调用全局低级鼠标挂钩
Global low-level mouse hook doesn't get called when UWP desktop app is out of focus
我正在创建一个桌面 UWP 应用程序,我需要设置一个全局低级鼠标挂钩来检测它并在它移动到屏幕的特定位置时更改它的位置。
当我的应用程序 window 处于焦点并且值被正确记录到输出 window 时它工作正常(意味着挂钩工作正常)。
此 UWP 应用不会在商店中上架,只能在 Windows 10 桌面 (1903+) 上使用。
我试过在不同的线程上调用 SetWindowsHookEx
(没有做任何事情)。
尝试在调用 SetWindowsHookEx
时也传递线程 ID 无济于事。
还尝试使用以下受限功能来防止应用程序在未聚焦时挂起:extendedExecutionUnconstrained
和 extendedBackgroundTaskTime
以及 here 所示的 PreventFromSuspending
方法。
我尝试的另一件事是在应用程序清单上将 uiAccess
设置为 true,这也没有用。
即使应用程序不在前台,全局挂钩也应该工作,但它只在它具有活动 window 焦点时工作。
#region Structures
[StructLayout(LayoutKind.Sequential)]
/* MSLLHOOKSTRUCT */
public struct NativeMouseLowLevelHook
{
public override string ToString()
{
return $"{nameof(Point)}: {Point}, {nameof(MouseData)}: {MouseData}, {nameof(Flags)}: {Flags}";
}
public NativePoint Point;
public int MouseData;
public int Flags;
public int Time;
public UIntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public class NativePoint
{
public override string ToString()
{
return $"{nameof(X)}: {X}, {nameof(Y)}: {Y}";
}
public int X;
public int Y;
}
#endregion
#region Hook
public class ManagedMouseHook : SafeHandleZeroOrMinusOneIsInvalid
{
public static List<ManagedMouseHook> KnownHooks { get; set; } = new List<ManagedMouseHook>();
public HookProc HookImpl { get; set; }
public ManagedMouseHook() : base(true)
{
Hook();
}
private void Hook()
{
HookImpl = NativeHookCallback;
KnownHooks.Add(this);
using (var curProcess = Process.GetCurrentProcess())
using (var curModule = curProcess.MainModule)
{
DoHook(curModule);
}
}
private void DoHook(ProcessModule curModule)
{
SetHandle(SetWindowsHookEx(14 /*WH_MOUSE_LL*/, HookImpl, GetModuleHandle(curModule.ModuleName), 0));
}
private bool UnHook()
{
var result = UnhookWindowsHookEx(DangerousGetHandle());
KnownHooks.Remove(this);
HookImpl = null;
return result;
}
/* LowLevelMouseProc */
private IntPtr NativeHookCallback(int code, IntPtr wparam, IntPtr lparam)
{
if (code >= 0)
{
var info = (NativeMouseLowLevelHook) Marshal.PtrToStructure(lparam,
typeof(NativeMouseLowLevelHook));
Debug.WriteLine(info); //Output example: Point: X: 408, Y: 535, MouseData: 0, Flags: 0
return new IntPtr(-1);
}
return CallNextHookEx(IntPtr.Zero, code, wparam, lparam);
}
protected override bool ReleaseHandle()
{
return UnHook();
}
}
#endregion
#region Interop
public delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int hookType, HookProc lpfn, IntPtr hMod, ulong dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll")]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam,
IntPtr lParam);
#endregion
然后,开始使用它:
public ManagedMouseHook MouseHook { get; set; }
MouseHook/*property*/ = new ManagedMouseHook();
并解开:
MouseHook/*property*/.Close();
UWP 应用 运行 在沙盒中,所以这听起来并不奇怪。如果您认为应用程序可以接收此类输入是一个安全问题,并且由于 'Security' 被列为 UWP 的第一大特征,因此这种行为是预期的。
在评论的帮助下,我决定最好的方法是将我的 UWP 应用程序用作前端,并使用 win32 应用程序本身的挂钩功能。
我正在创建一个桌面 UWP 应用程序,我需要设置一个全局低级鼠标挂钩来检测它并在它移动到屏幕的特定位置时更改它的位置。
当我的应用程序 window 处于焦点并且值被正确记录到输出 window 时它工作正常(意味着挂钩工作正常)。
此 UWP 应用不会在商店中上架,只能在 Windows 10 桌面 (1903+) 上使用。
我试过在不同的线程上调用 SetWindowsHookEx
(没有做任何事情)。
尝试在调用 SetWindowsHookEx
时也传递线程 ID 无济于事。
还尝试使用以下受限功能来防止应用程序在未聚焦时挂起:extendedExecutionUnconstrained
和 extendedBackgroundTaskTime
以及 here 所示的 PreventFromSuspending
方法。
我尝试的另一件事是在应用程序清单上将 uiAccess
设置为 true,这也没有用。
即使应用程序不在前台,全局挂钩也应该工作,但它只在它具有活动 window 焦点时工作。
#region Structures
[StructLayout(LayoutKind.Sequential)]
/* MSLLHOOKSTRUCT */
public struct NativeMouseLowLevelHook
{
public override string ToString()
{
return $"{nameof(Point)}: {Point}, {nameof(MouseData)}: {MouseData}, {nameof(Flags)}: {Flags}";
}
public NativePoint Point;
public int MouseData;
public int Flags;
public int Time;
public UIntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public class NativePoint
{
public override string ToString()
{
return $"{nameof(X)}: {X}, {nameof(Y)}: {Y}";
}
public int X;
public int Y;
}
#endregion
#region Hook
public class ManagedMouseHook : SafeHandleZeroOrMinusOneIsInvalid
{
public static List<ManagedMouseHook> KnownHooks { get; set; } = new List<ManagedMouseHook>();
public HookProc HookImpl { get; set; }
public ManagedMouseHook() : base(true)
{
Hook();
}
private void Hook()
{
HookImpl = NativeHookCallback;
KnownHooks.Add(this);
using (var curProcess = Process.GetCurrentProcess())
using (var curModule = curProcess.MainModule)
{
DoHook(curModule);
}
}
private void DoHook(ProcessModule curModule)
{
SetHandle(SetWindowsHookEx(14 /*WH_MOUSE_LL*/, HookImpl, GetModuleHandle(curModule.ModuleName), 0));
}
private bool UnHook()
{
var result = UnhookWindowsHookEx(DangerousGetHandle());
KnownHooks.Remove(this);
HookImpl = null;
return result;
}
/* LowLevelMouseProc */
private IntPtr NativeHookCallback(int code, IntPtr wparam, IntPtr lparam)
{
if (code >= 0)
{
var info = (NativeMouseLowLevelHook) Marshal.PtrToStructure(lparam,
typeof(NativeMouseLowLevelHook));
Debug.WriteLine(info); //Output example: Point: X: 408, Y: 535, MouseData: 0, Flags: 0
return new IntPtr(-1);
}
return CallNextHookEx(IntPtr.Zero, code, wparam, lparam);
}
protected override bool ReleaseHandle()
{
return UnHook();
}
}
#endregion
#region Interop
public delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int hookType, HookProc lpfn, IntPtr hMod, ulong dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll")]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam,
IntPtr lParam);
#endregion
然后,开始使用它:
public ManagedMouseHook MouseHook { get; set; }
MouseHook/*property*/ = new ManagedMouseHook();
并解开:
MouseHook/*property*/.Close();
UWP 应用 运行 在沙盒中,所以这听起来并不奇怪。如果您认为应用程序可以接收此类输入是一个安全问题,并且由于 'Security' 被列为 UWP 的第一大特征,因此这种行为是预期的。
在评论的帮助下,我决定最好的方法是将我的 UWP 应用程序用作前端,并使用 win32 应用程序本身的挂钩功能。