2017-02-14 38 views
3

我的代碼與運行在Windows托盤應用程序中的this question非常相似,即使從問題中得到的確切代碼我也會得到相同的行爲。它在經典的Windows應用程序上運行良好,例如Firefox,Chrome,Windows資源管理器等。但是,當鼠標焦點到達UWP應用程序(例如Edge或Calendar或Mail)時,滾動會變得緊張,在執行幾十個滾動後,我的應用程序掛起甚至不能從任務管理器(權限被拒絕)終止,這種行爲是非常可重複的。在Windows10 UWP應用程序上使用SendInput發送滾動命令

我會從這裏的問題粘貼代碼:

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

@hatchet我意識到這一點。我的應用程序不是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()調用之前將其設置爲true,之後爲false


但我想我知道你爲什麼這樣做,我也一直是一個讓它倒退的觸控板受害者。有一個更簡單的方法來做到這一點,只需修改FlipFlopWheel setting

+0

感謝您的回答。其實它沒有進入遞歸。我已經檢查過這個。我的代碼與示例不同,用於與完全顛倒滾動不同的目的。但是這個例子導致了同樣的問題。無論是我的條件還是你的條件,SendCommand調用都會在執行幾次後導致問題,而不是在第一次執行時。 – Vasil

+6

嘆了口氣,你爲什麼要浪費我的空閒時間,你實際上沒有使用的代碼和拒絕文檔的原因?這不是免費的,我可以幫助其他人。不,在「少量執行」之後不會造成問題,吹捧操作系統的堆棧比這更多。 –

相關問題