LowLevelMouseProc в фоновом потоке

Я пытаюсь настроить хук мыши в фоновом потоке.

delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
LowLevelMouseProc _proc = HookCallback;
SetWindowsHookEx(PInvoke.WH_MOUSE_LL, _proc, IntPtr.Zero, 0);

а также

IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam){/**/}

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

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

Есть ли способ сохранить нить для этой цели? Или, если существует другой способ подключения мыши без риска зависания?

Я случайно заметил, что при выполнении рабочего потока

GetMessage(out msg, new IntPtr(0), 0, 0);

Никакое сообщение не получено, но поток поддерживается для требуемой цели. Также мне нужен элегантный способ закрыть поток, но GetMessage никогда не возвращается.

Я не совсем понимаю все эти сообщения, я просто хочу иметь возможность подключить мышь и защитить ее от зависания.

Любая помощь приветствуется.


person Mafin    schedule 02.10.2010    source источник


Ответы (3)


Низкоуровневый хук мыши требует, чтобы цикл обработки сообщений выполнялся в потоке, который вызвал SetWindowsHookEx. Вот почему он не работает в простом фоновом потоке и работает в потоке пользовательского интерфейса. Если вы хотите использовать этот хук в фоновом потоке, вызовите метод Application.Run после SetWindowsHookEx. Поток остается в этом цикле и обрабатывает сообщения ловушек низкого уровня.

person Alex F    schedule 11.11.2010

В вашем методе обратного вызова хука просто запустите новый поток. Что-то вроде этого:

IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
    ThreadPool.QueueUserWorkItem(obj =>
    {
        // do whatever ...
    });
}

Не забудьте вызвать в своем основном потоке, если для обработки требуется доступ к вашим формам в элементах управления.

РЕДАКТИРОВАТЬ:

Если вы делаете что-то в своем основном потоке формы, что приводит к зависанию пользовательского интерфейса, вам следует рассмотреть возможность выполнения этого действия в фоновом потоке, а не в основном потоке. Когда вам нужно обновить элементы управления в результате вашей обработки, вы можете вызвать.

this.WhateverControl.Invoke( /* ... /* );

person Community    schedule 02.10.2010

У меня была аналогичная проблема в моей программе. После создания клавиатурного хука обратный вызов хука передавался в UI-поток. Когда поток пользовательского интерфейса был занят, обратный вызов находился в очереди диспетчера пользовательского интерфейса, но Windows отключит вас, если обратный вызов будет длиться слишком долго. Мои попытки создания отдельного потока для этих обратных вызовов были бесполезны, пока я не попытался запустить отдельный Dispatcher в этом потоке. Итак, я пытаюсь решить свою проблему с помощью этого кода:

private void InitializeKeyboardHookWithSeparateDispatcher()
    {
        using (var objCreated = new ManualResetEventSlim(false))
        {
            var thread = new Thread(() =>
            {
                _keyboardListener = new KeyboardListener();
                // ReSharper disable once AccessToDisposedClosure
                objCreated.Set();
                System.Windows.Threading.Dispatcher.Run();
            });
            thread.SetApartmentState(ApartmentState.STA);
            thread.IsBackground = true;
            thread.Start();
            objCreated.Wait();
        }
    }

KeyboardListener — это моя высокоуровневая абстракция, которая на самом деле вызывает SetWindowsHookEx внутри. Я использую manualreseteventslim, потому что KeyboardListener нужно вводить в другие объекты моей программы с помощью внедрения конструктора.

person Andrey Nikolaev    schedule 07.04.2018