应用程序不在前台时获得精确的鼠标移动增量

时间:2016-08-14 06:07:29

标签: c++ windows winapi input mouse

我试图找出如何从Win32 API获取精确的鼠标移动信息,这种信息在我的应用程序没有聚焦时仍然有效。到目前为止,我尝试了三种不同的方法:

  1. 使用WM_MOUSEMOVE通过SetWindowsHookEx注册WH_MOUSE_LL个活动。当应用程序在后台时,这可以工作,但返回相对低精度的屏幕坐标。此外,指针弹道应用于这些值,使它们不太适合我的目的。
  2. 在循环中调用GetCursorPos。这也适用于后台,但同样,它会返回低分辨率的屏幕坐标并应用指针加速。
  3. 通过RegisterRawInputDevices注册原始输入,然后观察WM_INPUT个事件(通过调用GetRawInputData来实际访问数据)。这个 - 至少,根据文档 - 返回本机鼠标单位的坐标,所以它尽可能精确。不幸的是,这仅在应用程序集中时才有效。 The docs说:

      

    应用程序可以在前台和后台时接收数据。

    但我没有看到任何证实这些说法的行为。

  4. 由于我的主要问题是关于第三个选项,我在下面列出了我的代码。

    那么,即使应用程序没有焦点,我怎样才能以一种有效的方式访问本机鼠标值?是否需要额外的配置才能使#3工作,还是有其他选择?

    #include "stdafx.h"
    // ...
    
    #define MAX_LOADSTRING 100
    
    // Global Variables:
    HINSTANCE hInst;                                // current instance
    WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
    WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name
    
    HWND mainWindow;
    
    // Forward declarations of functions included in this code module:
    ATOM                MyRegisterClass(HINSTANCE hInstance);
    BOOL                InitInstance(HINSTANCE, int);
    LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
    
    int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                         _In_opt_ HINSTANCE hPrevInstance,
                         _In_ LPWSTR    lpCmdLine,
                         _In_ int       nCmdShow)
    {
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
    
        // TODO: Place code here.
    
        // Initialize global strings
        LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
        LoadStringW(hInstance, IDC_MOUSETEST, szWindowClass, MAX_LOADSTRING);
        MyRegisterClass(hInstance);
    
        // Perform application initialization:
        if (!InitInstance (hInstance, nCmdShow))
        {
            return FALSE;
        }
    
        HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MOUSETEST));
    
        MSG msg;
    
        // Main message loop:
        while (GetMessage(&msg, nullptr, 0, 0))
        {
            if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    
        return (int) msg.wParam;
    }
    
    
    
    //
    //  FUNCTION: MyRegisterClass()
    //
    //  PURPOSE: Registers the window class.
    //
    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
        WNDCLASSEXW wcex;
    
        wcex.cbSize = sizeof(WNDCLASSEX);
    
        wcex.style          = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc    = WndProc;
        wcex.cbClsExtra     = 0;
        wcex.cbWndExtra     = 0;
        wcex.hInstance      = hInstance;
        wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MOUSETEST));
        wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
        wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_MOUSETEST);
        wcex.lpszClassName  = szWindowClass;
        wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
        return RegisterClassExW(&wcex);
    }
    
    //
    //   FUNCTION: InitInstance(HINSTANCE, int)
    //
    //   PURPOSE: Saves instance handle and creates main window
    //
    //   COMMENTS:
    //
    //        In this function, we save the instance handle in a global variable and
    //        create and display the main program window.
    //
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
       hInst = hInstance; // Store instance handle in our global variable
    
       mainWindow = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW | RIDEV_INPUTSINK,
          CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
    
       if (!mainWindow)
       {
          return FALSE;
       }
    
       ShowWindow(mainWindow, nCmdShow);
       UpdateWindow(mainWindow);
    
       // Register for raw input events
    
       RAWINPUTDEVICE targetDevice = { 0x01, 0x02, 0, mainWindow };
       BOOL registerRawSuccess = RegisterRawInputDevices(&targetDevice, 1, sizeof(RAWINPUTDEVICE));
    
       if (!registerRawSuccess)
           return FALSE;
    
       return TRUE;
    }
    
    //
    //  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
    //
    //  PURPOSE:  Processes messages for the main window.
    //
    //
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
        // ...
        case WM_INPUT :
            {
                UINT dwSize = 40;
                static BYTE lpb[40];
    
                if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT,
                    NULL, &dwSize, sizeof(RAWINPUTHEADER)) == -1)
                {
                    // TODO: Handle this... something broke
                }
    
                UINT result = GetRawInputData((HRAWINPUT)lParam, RID_INPUT,
                    lpb, &dwSize, sizeof(RAWINPUTHEADER));
    
                RAWINPUT* raw = (RAWINPUT*)lpb;
    
                if (raw->header.dwType == RIM_TYPEMOUSE)
                {
                    // Use raw->data.mouse.lLastX and raw->data.mouse.lLastY
                }
                break;
            }
        // ...
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    

1 个答案:

答案 0 :(得分:0)

使用Raw Input API时,如果您希望在应用程序在后台运行时收到RIDEV_INPUTSINK条消息,请指定WM_INPUT标记:

  

如果设置,这使得呼叫者即使在呼叫者不在前台时也能接收输入。请注意,必须指定hwndTarget

更改此行:

RAWINPUTDEVICE targetDevice = { 0x01, 0x02, 0, mainWindow };

对此:

RAWINPUTDEVICE targetDevice = { 0x01, 0x02, RIDEV_INPUTSINK, mainWindow };