SetWindowsHookEx с WH_MOUSE_LL замедляет работу мыши на несколько секунд

Я использую следующий код для получения сообщений мыши в текущем процессе.

using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
    return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
}

По какой-то причине, когда этот код запускается, мышь замедляется на несколько секунд, а затем возвращается в нормальное состояние.

Есть идеи?
Спасибо.

ИЗМЕНИТЬ – метод ловушки

private static IntPtr mouseEvent(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
    {
        MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));     
        LastLeftClick = new ClickInfo { Time = DateTime.Now, X = hookStruct.pt.x, Y = hookStruct.pt.y };
    }
    return CallNextHookEx(hookID, nCode, wParam, lParam);
}

public class ClickInfo
{
    public int X { get; set; }
    public int Y { get; set; }
    public DateTime Time { get; set; }
}

person Itay Karo    schedule 12.07.2010    source источник
comment
это также происходит в режиме выпуска?   -  person Patrick Klug    schedule 20.07.2010
comment
@Patrick Klug: Также в режиме выпуска.   -  person Itay Karo    schedule 20.07.2010
comment
WH_MOUSE_LL требует, чтобы Windows перенаправляла все события мыши в ваше приложение, ждала их обработки, а затем продолжала работу в обычном режиме. Сообщения от мыши — особенно если у вас мышь с высоким разрешением — приходят быстро и яростно: они быстро накапливаются, если их не отправить быстро. И при первом вызове вашего обратного вызова он, вероятно, не будет отвечать быстро: ваш процесс должен сначала выполнить свою собственную обработку, а затем среда выполнения должна выполнить JIT ваш обратный вызов и вызов P/Invoke для CallNextHookEx().   -  person Shog9    schedule 21.07.2010
comment
Две вещи, которые нужно проверить: 1) наблюдайте за производительностью вашего процесса во время установки хука (вы можете организовать автоматическую установку хука в период покоя, чтобы упростить эту задачу). Если вы недостаточно быстро прокачиваете сообщения, вам не потребуется много времени, чтобы вызвать отставание. 2) попробуйте предварительно настроить обратный вызов, вызвав System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod().   -  person Shog9    schedule 21.07.2010
comment
@ Shog9: Pre-Jitting не помог. Попробую проверить работоспособность, когда найду время. Спасибо за вашу помощь :)   -  person Itay Karo    schedule 21.07.2010
comment
Убедитесь, что вы начали свой цикл сообщений. Сначала я не понял, что мне нужно вызвать Application.Run() (чтобы запустить цикл сообщений), и моя мышь тоже отставала из-за этого. Не знаю, почему именно так, но я слишком ленив, чтобы понять это сейчас, когда это больше не проблема. :D   -  person user2347921    schedule 14.01.2021


Ответы (6)


Как выглядит ваша процедура подключения?

Если в вашем процессе есть только один поток пользовательского интерфейса, вместо этого используйте фильтр сообщений: http://msdn.microsoft.com/en-us/library/system.windows.forms.application.addmessagefilter.aspx

person Tergiver    schedule 12.07.2010
comment
Какая разница, как выглядит процедура, если проблема не в процессе события мыши, а в самой регистрации? - person Itay Karo; 12.07.2010
comment
Если вы не следите за тем, чтобы вообще не двигать мышь во время регистрации (она вызывается в ответ на нажатие кнопки?), и у вас есть стабильная мышь (некоторые мыши, особенно мыши без шариков, шумные), ваша процедура ловушки может вызываться немедленно. - person Tergiver; 13.07.2010
comment
Я предполагаю, что хук вызывается немедленно, но замедление происходит только при регистрации, а не при других вызовах хука. - person Itay Karo; 13.07.2010
comment
Верно.. так что делает ваша процедура ловушки? Делает ли он статические вызовы, которые могут вызывать статический ctor в первый раз? - person Tergiver; 13.07.2010

У меня была такая же проблема (только это проект С++, а не С#), и я решил ее, изменив хук с WH_MOUSE_LL на WH_MOUSE (с низкого уровня на нормальный уровень). Для сообщений WM_LBUTTONUP и WM_RBUTTONUP работает нормально.

Что меня забавляет, так это то, что код с WH_MOUSE_LL работал нормально в то время, когда я его писал (мышь не зависает и т. д.). Похоже, какое-то обновление безопасности для Windows изменило поведение хуков мыши, и ранее хороший код стал проблемой.

person Bobrovsky    schedule 19.07.2010

Извините, что продолжаю после столь долгого времени, но я решил проблему, создав отдельный поток, который обрабатывает хук (я не добавил все в код, так как он также переводит сообщения, но основная идея должна быть ясна):

    public Form1()
    {
        InitializeComponent();

        Thread thread = new Thread(HookThread);
        thread.IsBackground = true;
        thread.Start();
    }

    private void HookThread()
    {
        _hookControl = new Control();
        IntPtr handle = _hookControl.Handle;

        _hookProc = new HookProc(HookFunction);
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule)
        {
            _hook = SetWindowsHookEx(HookType.WH_MOUSE_LL, _hookProc, GetModuleHandle(curModule.ModuleName), 0);// (uint)AppDomain.GetCurrentThreadId());
        }

        Application.Run();

        UnhookWindowsHookEx(_hook);
        _hook = IntPtr.Zero;
    }

    private IntPtr HookFunction(int code, IntPtr wParam, IntPtr lParam)
    {
        if (code < 0)
        {
            //you need to call CallNextHookEx without further processing
            //and return the value returned by CallNextHookEx
            return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
        }

        int msg = wParam.ToInt32();
        string messages = string.Join(", ", _messageMapping.Where(t => t.Item1 == msg).Select(t => t.Item2));
        if (string.IsNullOrWhiteSpace(messages))
            messages = msg.ToString();
        Trace.WriteLine($"Messages: { messages }");

        //return the value returned by CallNextHookEx
        return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
    }

Вы можете завершить поток из любого другого потока, вызвав BeginInvoke для созданного _hookControl:

        _hookControl.BeginInvoke(((Action)(() => Application.ExitThread())));
person Romout    schedule 06.09.2018

При установке низкоуровневого хука отзывчивость мыши теперь становится зависимой от вашего основного потока, и распространенная ошибка заключается в том, чтобы установить хук в начале процесса запуска.

SetHook
LongRunningStartupProcess
Мышь не реагирует до этого момента

Во время запуска лучше всего направить крючок на нить, чтобы это произошло в конце процесса запуска. Dispatcher.CurrentDispatcher.BeginInvoke(new Action(SetHook));

DispatchSetHook
LongRunningStartupProcess
SetHook(обратный вызов)
Мышь становится отзывчивой

Это все еще имеет текущие проблемы с управлением в вашем приложении, чтобы гарантировать, что основной поток не выполняет никаких длительных процессов, поскольку это также блокирует мышь. Это легко проверить, установив хук, а затем выполнив Thread.Sleep в основном потоке.

person David Ewen    schedule 21.10.2011

Когда вы получите событие хука, выключите книгу, затем сделайте свою работу и, если действительно нужно, снова включите хук.

Это предотвратит отставание мыши.

Оставайтесь на крючке только тогда, когда вам это действительно нужно.

person Martin Blore    schedule 24.02.2011
comment
Сомневаюсь, что это поможет, так как мышь лагает только при первой подсечке. - person Itay Karo; 28.02.2011

Ваш крючок стоит дорого; вам просто нужно выяснить, почему и как это исправить.

Несмотря на то, что код выглядит очень минимальным, я подозреваю, что есть некоторые начальные затраты на взаимодействие C #, вызывающие задержки, возможно, из-за JIT или пейджинга.

Если вы измените код, чтобы сделать как можно больше обработки вне этого потока, проблема должна исчезнуть. как разработчик C++, я даже беспокоюсь о Marshal.PtrToStructure, поскольку низкоуровневые хуки очень чувствительны, и я не могу сразу сказать, что эта операция гарантированно будет настолько дешевой, что она не помешает движению мыши.

В прошлом я довольно часто использовал низкоуровневые перехватчики мыши (в C++) и никогда не сталкивался с проблемами, если только сама процедура перехвата не была дорогой. В C++ я стараюсь не делать ничего, кроме PostMessage для HWND, который выполняет остальную часть обработки.

person Community    schedule 20.07.2010
comment
ИМХО, если бы проблема была в хуке, я должен был бы сталкиваться с проблемой при каждом вызове хука (что в основном составляет время жизни моего процесса). Что касается вашего первого предложения - знаете ли вы какой-либо способ исследовать этот вопрос? - person Itay Karo; 20.07.2010
comment
вероятно, лучший способ добраться до основной причины — использовать такой инструмент, как «xperf», для просмотра того, что происходит в системе. это должно дать вам прямое представление о том, что происходит в точке, где мышь останавливается. msdn.microsoft.com/en-us/performance/cc825801.aspx это не самый простой в использовании инструмент в мире, но и не сложный, к тому же бесплатный. - person ; 25.07.2010