我有非常类似于在Windows托盘应用程序中运行的this question的代码,即使使用我得到相同行为问题的确切代码也是如此.在经典的Windows应用程序(例如Firefox,Chrome,Windows资源管理器等)上,它们都能很好地工作.但是,当鼠标焦点移到Edge,Calendar或Mail等UWP应用程序时,滚动会变得抖动,几十次滚动执行后,我的应用程序挂起并且甚至无法从任务管理器中终止(权限被拒绝),这种行为是非常可重现的.
我将粘贴问题中的代码:
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace EnableMacScrolling
{
class InterceptMouse
{
const int INPUT_MOUSE = 0;
const int MOUSEEVENTF_WHEEL = 0x0800;
const int WH_MOUSE_LL = 14;
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
if (_hookID == null)
{
MessageBox.Show("SetWindowsHookEx Failed");
return;
}
Application.Run();
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_MOUSEWHEEL == (MouseMessages)wParam)
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
Console.WriteLine(hookStruct.mouseData);
if (hookStruct.flags != -1) //prevents recursive call to self
{
INPUT input;
input = new INPUT();
input.type = INPUT_MOUSE;
input.mi.dx = 0;
input.mi.dy = 0;
input.mi.dwFlags = MOUSEEVENTF_WHEEL;
input.mi.time = 0;
input.mi.dwExtraInfo = 0;
input.mi.mouseData = -(hookStruct.mouseData >> 16);
try
{
SendInput(1, ref input, Marshal.SizeOf(input));
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
return (IntPtr)1;
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private enum MouseMessages
{
WM_MOUSEWHEEL = 0x020A
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public int mouseData;
public int flags;
public int time;
public IntPtr dwExtraInfo;
}
public struct INPUT
{
public int type;
public MOUSEINPUT mi;
}
[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public uint dwFlags;
public int time;
public int 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);
[DllImport("User32.dll", SetLastError = true)]
public static extern int SendInput(int nInputs, ref INPUT pInputs, int cbSize);
} }
我可能正在这里处理Windows中的错误吗?是否有任何指示可以帮助我找出正在发生的事情?
更新:
我已经在C语言中创建了一个测试Win32应用程序,可以更轻松地重现/演示该问题.问题在于SendCommand,当任何经典应用程序都处于焦点状态时执行该命令都可以正常工作.但是,当在UWP应用程序聚焦时执行该命令时,甚至Windows启动/启动菜单也会导致调用应用程序(我的应用程序)挂起并卡住,直到重新启动Windows.
一个有效的解决方法/解决方案是在处理挂钩回调的线程中的另一个线程上执行SendCommand调用.立即启动执行SendCommand的线程并从hook回调返回,将产生所需的行为,并且不会引起任何问题.
解决方法:
if (hookStruct.flags != -1) //prevents recursive call to self
这很重要,是的.但是,该声明如何可能发挥作用尚不清楚.该字段的期望值为0、1或3.从不-1.您可能被机器上活动的另一个鼠标钩误导了.
现在,WinRT应用程序是否在前台确实很重要.因为随后涉及消息代理并且字段值更改,所以打开了LLMHF_LOWER_IL_INJECTED bit. WinRT应用程序在沙盒中运行,而沙盒的完整性级别较低.因此该字段将不会为-1,并且您的SendInput()调用会再次触发鼠标钩子.持续进行下去,显示在堆栈用完时结束了.
因此,第一个可能的解决方法是按预期使用该字段,将语句更改为:
if ((hookStruct.flags & 1) == 0)
如果该推定的鼠标钩损坏了该字段,则将不起作用,然后考虑在您的类中使用静态布尔字段来破坏递归.在调用SendInput()之前将其设置为true,然后将其设置为false.
但是我想我知道您为什么要这样做,我也一直是触控板倒退的受害者.有一种更简单的方法,只需修改FlipFlopWheel setting.