C++ 在后台接收按键

C++ 在后台接收按键,c++,winapi,C++,Winapi,我正在使用winapi开发一个Win32应用程序,我很好奇是否有一种方法可以在应用程序失去焦点/无窗口时接收键盘事件 最初我正在读取应用程序消息队列并检查WM_KEYDOWN消息,但是消息只在窗口处于焦点时发送到应用程序。我知道可以使用DirectInput,但如果可能,我希望避免让我的应用程序需要DirectX 除了使用DirectInput,在窗口不对焦时是否还有其他方法接收键盘按键 编辑:不,在你问之前,我不是在写键盘记录器。应用程序需要在后台响应某些可重新绑定的键盘快捷键。实现这种行为的

我正在使用winapi开发一个Win32应用程序,我很好奇是否有一种方法可以在应用程序失去焦点/无窗口时接收键盘事件

最初我正在读取应用程序消息队列并检查
WM_KEYDOWN
消息,但是消息只在窗口处于焦点时发送到应用程序。我知道可以使用DirectInput,但如果可能,我希望避免让我的应用程序需要DirectX

除了使用DirectInput,在窗口不对焦时是否还有其他方法接收键盘按键

编辑:不,在你问之前,我不是在写键盘记录器。应用程序需要在后台响应某些可重新绑定的键盘快捷键。

实现这种行为的最佳方法是挂接键盘

这是一个方便的教程,解释了基础知识:
它还将钩子类型区分为系统范围的钩子类型和特定于进程的钩子类型。

实现这种行为的最佳方法是钩子键盘

这是一个方便的教程,解释了基础知识:

它还将钩子类型区分为系统范围的钩子类型和特定于进程的钩子类型。

稍后编辑:如果窗口失焦,即使忽略了
WM\u KILLFOCUS
,它也无法处理
WM\u KEYDOWN
消息。因此,您需要定期单独检查
GetAsyncKeyState()

您可以绕过
WndProc()
消息中的
WM\u KILLFOCUS
,这样应用程序就不会失去焦点。它仍然可以处理任何按键

比如:

LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{

    switch (msg)
    {

    /* cases blah blah */

    case WM_KILLFOCUS:
        return 0;

    /* other cases' blah blah */

    }
    return DefWindowProc(hWindow, msg, wParam, lParam);
}

后期编辑:如果窗口失焦,即使忽略
WM\u KILLFOCUS
,也无法处理
WM\u KEYDOWN
消息。因此,您需要定期单独检查
GetAsyncKeyState()

您可以绕过
WndProc()
消息中的
WM\u KILLFOCUS
,这样应用程序就不会失去焦点。它仍然可以处理任何按键

比如:

LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{

    switch (msg)
    {

    /* cases blah blah */

    case WM_KILLFOCUS:
        return 0;

    /* other cases' blah blah */

    }
    return DefWindowProc(hWindow, msg, wParam, lParam);
}

这是winapi直接支持的,您需要使用。

这是winapi直接支持的,您需要使用。

Microsoft DirectX库包含一个可用于获取键盘状态的函数。它是DirectInputAPI的一部分。下面的代码演示如何轮询键盘以获取键状态信息。必须添加额外的逻辑来检测何时按下/释放键,并将其转换为字符

请注意,这需要编译Microsoft DirectX SDK

//Public domain: no attribution required
#include "stdafx.h"
#include "dxlog.h"

#pragma comment(lib, "dinput8")
#pragma comment(lib, "dxguid")

LPDIRECTINPUT8 din;
LPDIRECTINPUTDEVICE8 dinkbd;
BYTE keystate[256];
DIDATAFORMAT dfi;

void init_dinput(HINSTANCE hInst, HWND hWnd)
{
    HRESULT hr;
    hr = DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&din, NULL);
    hr = din->CreateDevice(GUID_SysKeyboard, &dinkbd, NULL);
    hr = dinkbd->SetDataFormat(&c_dfDIKeyboard);
    // share the keybdb and collect even when not the active application
    hr = dinkbd->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
}

void detect_input(void)
{
    dinkbd->Acquire();
    dinkbd->GetDeviceState(256, keystate);
}

void clean_dinput(void)
{
    dinkbd->Unacquire();
    din->Release();
}

void print_state()
{
    WCHAR pState[4096] = L"";
    WCHAR temp[32];

    for (int i = 0; i < 256; i++)
    {
        if (keystate[i] != 0)
        {
            wsprintf (temp, L"%d(%d) ", i, keystate[i]);
            lstrcat(pState, temp);
        }
    }
    if (lstrlen(pState) != 0)
    {
        lstrcat(pState, L"\n");
        OutputDebugString(pState);
    }
}
//公共域:不需要属性
#包括“stdafx.h”
#包括“dxlog.h”
#pragma注释(lib,“dinput8”)
#pragma注释(lib,“dxguid”)
LPDIRECTINPUT8 din;
LPDIRECTINPUTDEVICE8 dinkbd;
字节键状态[256];
双数据格式dfi;
无效初始输入(HINSTANCE hInst,HWND HWND)
{
HRESULT-hr;
hr=DirectInput8Create(hInst,DIRECTINPUT_版本,IID_IDirectInput8,(void**)和din,NULL);
hr=din->CreateDevice(GUID\u系统键盘和dinkbd,NULL);
hr=dinkbd->SetDataFormat(&c_-dfDIKeyboard);
//即使不是活动应用程序,也共享keybdb和collect
hr=dinkbd->SetCooperativeLevel(hWnd,DISCL_非独占| DISCL_背景);
}
无效检测_输入(无效)
{
dinkbd->Acquire();
dinkbd->GetDeviceState(256,键状态);
}
无效清洁装置(无效)
{
dinkbd->Unacquire();
din->Release();
}
无效打印状态()
{
WCHAR pState[4096]=L”“;
WCHAR温度[32];
对于(int i=0;i<256;i++)
{
如果(键状态[i]!=0)
{
wsprintf(临时,L“%d(%d)”,i,键状态[i]);
lstrcat(pState,temp);
}
}
如果(lstrlen(pState)!=0)
{
lstrcat(pState,L“\n”);
OutputDebugString(pState);
}
}

Microsoft DirectX库包含一个可用于获取键盘状态的函数。它是DirectInputAPI的一部分。下面的代码演示如何轮询键盘以获取键状态信息。必须添加额外的逻辑来检测何时按下/释放键,并将其转换为字符

请注意,这需要编译Microsoft DirectX SDK

//Public domain: no attribution required
#include "stdafx.h"
#include "dxlog.h"

#pragma comment(lib, "dinput8")
#pragma comment(lib, "dxguid")

LPDIRECTINPUT8 din;
LPDIRECTINPUTDEVICE8 dinkbd;
BYTE keystate[256];
DIDATAFORMAT dfi;

void init_dinput(HINSTANCE hInst, HWND hWnd)
{
    HRESULT hr;
    hr = DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&din, NULL);
    hr = din->CreateDevice(GUID_SysKeyboard, &dinkbd, NULL);
    hr = dinkbd->SetDataFormat(&c_dfDIKeyboard);
    // share the keybdb and collect even when not the active application
    hr = dinkbd->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
}

void detect_input(void)
{
    dinkbd->Acquire();
    dinkbd->GetDeviceState(256, keystate);
}

void clean_dinput(void)
{
    dinkbd->Unacquire();
    din->Release();
}

void print_state()
{
    WCHAR pState[4096] = L"";
    WCHAR temp[32];

    for (int i = 0; i < 256; i++)
    {
        if (keystate[i] != 0)
        {
            wsprintf (temp, L"%d(%d) ", i, keystate[i]);
            lstrcat(pState, temp);
        }
    }
    if (lstrlen(pState) != 0)
    {
        lstrcat(pState, L"\n");
        OutputDebugString(pState);
    }
}
//公共域:不需要属性
#包括“stdafx.h”
#包括“dxlog.h”
#pragma注释(lib,“dinput8”)
#pragma注释(lib,“dxguid”)
LPDIRECTINPUT8 din;
LPDIRECTINPUTDEVICE8 dinkbd;
字节键状态[256];
双数据格式dfi;
无效初始输入(HINSTANCE hInst,HWND HWND)
{
HRESULT-hr;
hr=DirectInput8Create(hInst,DIRECTINPUT_版本,IID_IDirectInput8,(void**)和din,NULL);
hr=din->CreateDevice(GUID\u系统键盘和dinkbd,NULL);
hr=dinkbd->SetDataFormat(&c_-dfDIKeyboard);
//即使不是活动应用程序,也共享keybdb和collect
hr=dinkbd->SetCooperativeLevel(hWnd,DISCL_非独占| DISCL_背景);
}
无效检测_输入(无效)
{
dinkbd->Acquire();
dinkbd->GetDeviceState(256,键状态);
}
无效清洁装置(无效)
{
dinkbd->Unacquire();
din->Release();
}
无效打印状态()
{
WCHAR pState[4096]=L”“;
WCHAR温度[32];
对于(int i=0;i<256;i++)
{
如果(键状态[i]!=0)
{
wsprintf(临时,L“%d(%d)”,i,键状态[i]);
lstrcat(pState,temp);
}
}
如果(lstrlen(pState)!=0)
{
lstrcat(pState,L“\n”);
OutputDebugString(pState);
}
}

实际上,这会更容易。但它安全吗?如果另一个应用程序试图获得焦点呢?这难道不会阻止任何其他应用程序获得焦点吗?其思想是,当其他应用程序正常运行时,应用程序应该能够在后台接收按键。@Bad Wolf您应该挂接键盘。在未知行为的国度里漫游并不理想。@Bad Wolf:它不会阻止任何应用程序获得焦点。我有一个窗口和Chrome的例子,当我点击RMB时