2017-02-14 5 views
3

동일한 문제가 발생해도 동일한 코드를 사용하여 Windows 트레이 응용 프로그램에서 실행되는 this question과 매우 유사한 코드가 있습니다. 그것은 모두 파이어 폭스, 크롬, 윈도우 익스플로러와 같은 고전적인 윈도우 응용 프로그램에서 잘 작동합니다. 그러나 마우스 포커스가 Edge 나 Calendar 또는 Mail과 같은 UWP 응용 프로그램에 도착하면 스크롤이 지저분 해지고 수십 번의 스크롤이 수행 된 후 내 응용 프로그램이 멈 춥니 다. 작업 관리자 (권한 거부)에서 종료 할 수도 없기 때문에이 동작은 매우 재현 가능합니다.Windows10에서 SendInput을 사용하여 스크롤 명령 보내기 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); 

} } 

그것이 내가 여기 창에서이 문제를 다루고있어 가능한가요? 무슨 일이 일어나고 있는지 어떻게 알 수 있나?

업데이트 : 좀 더 쉽게/재생 문제를 보여주기 위해 C++로 테스트 Win32 응용 프로그램을 만들었습니다

. 이 문제는 SendCommand에서 발생합니다. 고전적인 응용 프로그램이 포커스를 맞춘 상태에서 실행될 때 문제가 없습니다. 그러나 UWP 응용 프로그램이 초점을 맞추는 동안 실행되거나 Windows 시작/시작 메뉴로 인해 호출 응용 프로그램 (내 응용 프로그램)이 멈추고 창이 다시 시작될 때까지 멈추게됩니다.

이 문제에 대한 효과적인 해결 방법/솔루션은 후크 콜백을 처리하는 스레드에서 다른 스레드에서 SendCommand 호출을 수행하는 것입니다. SendCommand를 실행하고 후크 콜백에서 돌아 오는 스레드를 즉시 시작하면 원하는 동작이 생성되고 아무런 문제가 발생하지 않습니다.

+0

나는 이것을 알고 있습니다. 내 응용 프로그램은 UWP가 아니며 고전적인 데스크톱 응용 프로그램입니다. – Vasil

+0

이 문제는 SetWindowsHookEx의 32/64 비트 문제와 관련된 문제 일 수 있습니다. 두 번째 스레드를 만들고 해당 스레드의 컨텍스트에서 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 부울 필드를 사용하는 것을 고려 작동하지 않습니다 수업에서 재귀를 깰 수 있습니다. 나중에 으로 설정하고 SendInput() 호출 전에을 호출하면 거짓이됩니다.


는하지만 난 너무 거꾸로있어 트랙 패드의 피해자, 나는 당신이이 일을하는 이유를 알 것 같아요. 훨씬 쉬운 방법이 있습니다. FlipFlopWheel setting을 수정하면됩니다.

+0

답변 해 주셔서 감사합니다. 실제로 그것은 재귀에 들어 가지 않습니다. 나는 이것을 확인했다. 내 코드는 예제와 다르며 스크롤을 완전히 반전하는 것과는 다른 목적으로 사용됩니다. 그러나이 예제는 같은 문제를 일으 킵니다. 내 조건이나 당신의 경우, SendCommand 호출은 첫 번째 실행이 아닌 몇 번의 실행 후에 문제를 일으키고 있습니다. – Vasil

+6

한숨, 왜 실제로 사용하지 않는 코드와 문서 작성을 거부하는 이유로 내 자유 시간을 낭비하고 있습니까? 그것은 자유롭지 않다, 나는 다른 누군가를 도울 수 있었다. 그리고 아니, "약간의 실행"후에 문제가 발생하지 않습니다, 운영 체제의 스택을 불어 그 이상 걸립니다. –

관련 문제