代码之家  ›  专栏  ›  技术社区  ›  Shemp

窗口处于焦点时,低级键盘挂钩无法与DirectInput一起使用

  •  0
  • Shemp  · 技术社区  · 2 年前

    我正试图拦截并禁止 Alt+Tab 组合(对我来说,不仅禁止这种组合很重要),这样应用程序窗口就不会通过按下中的这些键来切换(例如,在某些情况下,这与游戏中的全屏模式有关) Windows 操作系统( Windows 10 22H2 ,显然这与 DInput 版本,下面将对此进行详细介绍)。为了做到这一点,我 use SetWindowsHookEx 具有 WH_KEYBOARD_LL 但是,如果应用程序使用API进行处理,则此挂钩不起作用 DirectInput ( DInput8 )输入。值得注意的是,如果应用程序最小化(即使使用 直接输入 在中 DISCL_BACKGROUND 与上述行为相矛盾的模式)。

    我看到上面立了一面旗 DISCL_FOREGROUND 帮助了一些人——我最初有这面旗帜,尝试了各种组合 CooperativeLevel -这也于事无补。

    此外,钩子和 直接输入 以前可能对很多人都有效,但现在它 seems 事实并非如此(中的处理逻辑之间存在差异 DInput7 DInput8 ,以及在不同的Windows操作系统之间)。在这里,我只能假设 DInput7 使用较早,内部使用相同的钩子( 键盘钩子 ),而不是 Raw Input

    根据判断 documentation 直接输入 过程 原始输入 (即。 WM_INPUT 消息)。我试图拦截 WM_INPUT 使用 键盘钩子 钩与 WH_GETMESSAGE -它没有帮助(在我的情况下,这个钩子根本不起作用 直接输入 已启用)。

    我试图更改初始化的顺序,以防万一(至于 WH_KEYBOARD_LL ,所以对于 WH_get消息 )-没用。

    在这里,我发现了许多同样的人面临着同样的问题,但我没有找到这些主题的解决方案: example1 (Windows XP), example2 (问题的焦点相似,但 原始输入 在这里使用,而不是 直接输入 ), example3

    问题是:

    1. 为什么钩子 WH_KEYBOARD_LL 只有当窗口的焦点丢失时才能工作?有可能解决这个问题吗 直接输入 并且窗口焦点处于活动状态?
    2. 基于第1点:为什么钩子即使在 直接输入 当窗口焦点丢失时处于活动状态( DISCL_BACKGROUND )?
    3. 在的引擎盖下 直接输入 (谈论 DInput8 ,参见上面的区别)是 原始输入 处理。如果是,为什么 键盘钩子 钩与 WH_get消息 拦截 WM_INPUT 不工作?
    4. 这个问题还有什么其他解决方案?至少我知道 there is RegisterHotKey

    一个重现问题的最小示例:

    #include <Windows.h>
    #define DIRECTINPUT_VERSION 0x0800
    #include <dinput.h>
    
    #pragma comment (lib, "dinput8.lib")
    #pragma comment (lib, "dxguid.lib")
    
    HWND g_hWnd = {};
    HHOOK g_LLKeyboardHook = {};
    
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            case WM_DESTROY:
            {
                PostQuitMessage(EXIT_SUCCESS);
                return ERROR_SUCCESS;
            }
            case WM_PAINT:
            {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hwnd, &ps);
    
                FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
    
                EndPaint(hwnd, &ps);
                return ERROR_SUCCESS;
            }
        }
    
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    
    LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
        const auto hookStruct = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
    
        switch (nCode)
        {
            case HC_ACTION:
            {
                // For tests ('A' key)
                if (hookStruct->vkCode == 0x41)
                {
                    // MessageBox(g_hWnd, TEXT("Pressed 'A' from Hook"), TEXT("Hook"), 0u);
                    // Block it
                    return TRUE;
                }
                // Alt + Tab
                else if (hookStruct->flags & LLKHF_ALTDOWN && hookStruct->vkCode == VK_TAB)
                {
                    // MessageBox(g_hWnd, TEXT("Pressed 'Alt + Tab' from Hook"), TEXT("Hook"), 0u);
                    // Block it
                    return TRUE;
                }
            }
        }
    
        return CallNextHookEx(g_LLKeyboardHook, nCode, wParam, lParam);
    }
    
    bool InitializeDirectInput(LPDIRECTINPUT8& di, LPDIRECTINPUTDEVICE8& diKeyboard, HINSTANCE hInstance)
    {
        if (!hInstance || !g_hWnd)
        {
            return false;
        }
    
        auto res = DirectInput8Create(
            hInstance,
            DIRECTINPUT_VERSION,
            IID_IDirectInput8,
            reinterpret_cast<void**>(&di),
            NULL);
        if (FAILED(res))
        {
            MessageBox(g_hWnd, TEXT("DirectInput8Create failed"), TEXT("Error"), 0u);
            return false;
        }
    
        res = di->CreateDevice(
            GUID_SysKeyboard,
            &diKeyboard,
            NULL);
        if (FAILED(res))
        {
            MessageBox(g_hWnd, TEXT("CreateDevice failed"), TEXT("Error"), 0u);
            return false;
        }
    
        res = diKeyboard->SetDataFormat(&c_dfDIKeyboard);
        if (FAILED(res))
        {
            MessageBox(g_hWnd, TEXT("SetDataFormat failed"), TEXT("Error"), 0u);
            return false;
        }
    
        res = diKeyboard->SetCooperativeLevel(g_hWnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
        if (FAILED(res))
        {
            MessageBox(g_hWnd, TEXT("SetCooperativeLevel failed"), TEXT("Error"), 0u);
            return false;
        }
    
        return true;
    }
    
    void CloseDirectInput(LPDIRECTINPUT8& di, LPDIRECTINPUTDEVICE8& diKeyboard)
    {
        if (diKeyboard)
        {
            diKeyboard->Unacquire();
        }
        if (di)
        {
            di->Release();
        }
    }
    
    void DetectKeys(LPDIRECTINPUTDEVICE8& diKeyboard)
    {
        BYTE keyState[256] = {};
        HRESULT res = {};
    
        res = diKeyboard->Acquire();
        if (FAILED(res))
        {
            // When the application loses focus, capture cannot be performed
            return;
        }
    
        res = diKeyboard->GetDeviceState(sizeof(keyState), static_cast<LPVOID>(keyState));
        if (FAILED(res))
        {
            return;
        }
    
        if (keyState[DIK_A] & 0x80)
        {
            MessageBox(g_hWnd, TEXT("Pressed 'A' from DirectInput"), TEXT("DirectInput"), 0u);
        }
    }
    
    int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR pCmdLine, int nCmdShow)
    {
        const wchar_t windowClassName[] = L"Sample Window Class";
    
        WNDCLASS wc = {};
        wc.lpfnWndProc = WindowProc;
        wc.hInstance = hInstance;
        wc.lpszClassName = windowClassName;
    
        RegisterClass(&wc);
    
        g_hWnd = CreateWindowEx(
            0u,                                             // Optional window styles.
            windowClassName,                                // Window class
            L"Low-level keyboard hook with DirectInput",    // Window text
            WS_OVERLAPPEDWINDOW,                            // Window style
    
            // Size and position
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    
            NULL,       // Parent window    
            NULL,       // Menu
            hInstance,  // Instance handle
            NULL        // Additional application data
        );
        if (!g_hWnd)
        {
            MessageBox(NULL, TEXT("CreateWindowEx failed"), TEXT("Error"), 0u);
            return EXIT_FAILURE;
        }
    
        LPDIRECTINPUT8 di = nullptr;
        LPDIRECTINPUTDEVICE8 diKeyboard = nullptr;
    
        if (!InitializeDirectInput(di, diKeyboard, hInstance))
        {
            return EXIT_FAILURE;
        }
    
        g_LLKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, 0u);
        if (!g_LLKeyboardHook)
        {
            MessageBox(g_hWnd, TEXT("SetWindowsHookEx failed"), TEXT("Error"), 0u);
            CloseDirectInput(di, diKeyboard);
            return EXIT_FAILURE;
        }
    
        ShowWindow(g_hWnd, nCmdShow);
    
        MSG msg = {};
        while (true)
        {
            if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
            {
                if (msg.message == WM_QUIT)
                    break;
    
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
    
            DetectKeys(diKeyboard);
        }
    
        UnhookWindowsHookEx(g_LLKeyboardHook);
        
        CloseDirectInput(di, diKeyboard);
    
        return EXIT_SUCCESS;
    }
    
    0 回复  |  直到 2 年前