2017-02-14 17 views
3

У меня есть код, очень похожий на this question, работающий в приложении лотка Windows, даже с этим точным кодом из вопроса я получаю такое же поведение. Все это хорошо работает в классических приложениях Windows, таких как Firefox, Chrome, Windows Explorer и т. Д. Однако, когда фокус мыши попадает в приложение UWP, такое как Edge или Calendar или Mail, свиток становится неустойчивым и после нескольких десятков свитков выполняется мое приложение зависает и даже не может быть прекращено из диспетчера задач (разрешение отклонено), это поведение очень воспроизводимо.Отправка команд прокрутки с помощью SendInput в приложениях UWP Windows10

Я вставить код из вопроса здесь:

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); 

} } 

Возможно ли, что я имею дело с ошибкой в ​​окнах здесь? Любые указания на то, как я могу узнать, что происходит?

Update:

Я создал тестовый Win32 приложение в C++ более легко воспроизвести/показать проблему. Проблема заключается в SendCommand, который, когда выполняется, когда какое-либо классическое приложение находится в фокусе, отлично работает. Однако при выполнении в то время, когда приложение UWP находится в фокусе, или даже меню запуска/запуска Windows заставляет вызывающее приложение (мое приложение) зависать и застревать до перезапуска окон.

Эффективное обходное решение/решение этой проблемы - выполнить вызов SendCommand в другом потоке из потока, обрабатывающего обратный вызов hook. Сразу же запуск потока, который выполняет SendCommand, и возврат из обратного вызова hook вызывает желаемое поведение и не вызывает никаких проблем.

+0

@hatchet Я знаю об этом. Мое приложение не UWP, это классическое настольное приложение. – Vasil

+0

Это может быть проблема, связанная с 32/64-разрядной версией SetWindowsHookEx. Вы можете обойти это, создав второй поток и запустив SetWindowsHookEx в контексте этого потока. См. Обсуждение 32/64 и насоса сообщений: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx – hatchet

+0

Где ваше приложение на C++? –

ответ

4
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) 

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


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

+0

Спасибо за ответ. На самом деле он не попадает в рекурсию. Я проверил это. Мой код отличается от примера и для другой цели, чем инвертирование прокрутки. Но этот пример вызывает ту же проблему. Как с моим условием, так и с вашим, вызов SendCommand вызывает проблему после нескольких исполнений, а не при первом выполнении. – Vasil

+6

Sigh, почему вы тратите свое свободное время на код, который вы фактически не используете, и причина, по которой вы отказываетесь документировать? Это не бесплатно, я мог бы помочь кому-то другому. И нет, это не вызывает проблемы после «нескольких исполнений», в результате чего стек операционной системы занимает больше, чем это. –