windows - 检测 Windows 8 桌面程序中的键盘存在

标签 windows windows-8

对于 Metro 应用程序,有 Windows.Devices.Input.KeyboardCapabilities.KeyboardPresent。 Windows 8 桌面程序是否可以检测是否存在物理键盘?

最佳答案

这有点繁琐,我不知道我提出的方法是否适用于所有情况,但这是我最终使用的方法:

  1. 使用 SetupDiGetClassDevs 查找所有键盘设备。
  2. 使用 SetupDiGetDeviceRegistryProperty 读取一些键盘设备属性以忽略 PS/2 键盘
  3. 检查触摸支持,因为 Win 8 触摸设备似乎总是有一个额外的 HID 键盘设备。

PS/2 端口的一个问题是它们始终显示为键盘设备,即使没有插入任何东西。我只是通过假设没有人会使用 PS/2 键盘来解决这个问题,我过滤掉它们。我包括两个单独的检查来尝试确定键盘是否为 PS/2。我不知道两者的可靠性如何,但对于我测试过的机器来说,两者似乎都可以正常工作。

另一个问题 (#3) 是当 Windows 8 机器支持触摸时,它们有一个额外的 HID 键盘设备,您需要忽略它。

PS:我刚刚意识到,我的代码正在使用“缓冲区”类进行属性查询。我将其遗漏以仅保留相关代码,但您需要将其替换为一些适当的缓冲区/内存管理。

#include <algorithm>
#include <cfgmgr32.h>
#include <Setupapi.h>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string.hpp>

struct KeyboardState
{
    KeyboardState() : isPS2Keyboard(false)
    {   }

    std::wstring deviceName; // The name of the keyboard device.
    bool isPS2Keyboard;      // Whether the keyboard is a PS/2 keyboard.
};

void GetKeyboardState(std::vector<KeyboardState>& result)
{
    LPCWSTR PS2ServiceName = L"i8042prt";
    LPCWSTR PS2CompatibleId = L"*PNP0303";
    const GUID KEYBOARD_CLASS_GUID = { 0x4D36E96B, 0xE325,  0x11CE,  { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } };

    // Query for all the keyboard devices.
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&KEYBOARD_CLASS_GUID, NULL, NULL, DIGCF_PRESENT);
    if (hDevInfo == INVALID_HANDLE_VALUE)
    {
        return;
    }

    //
    // Enumerate all the keyboards and figure out if any are PS/2 keyboards.
    //
    bool hasKeyboard = false;
    for (int i = 0;; ++i)
    {
        SP_DEVINFO_DATA deviceInfoData = { 0 };
        deviceInfoData.cbSize = sizeof (deviceInfoData);
        if (!SetupDiEnumDeviceInfo(hDevInfo, i, &deviceInfoData))
        {
            break;
        }

        KeyboardState currentKeyboard;

        // Get the device ID
        WCHAR szDeviceID[MAX_DEVICE_ID_LEN];
        CONFIGRET status = CM_Get_Device_ID(deviceInfoData.DevInst, szDeviceID, MAX_DEVICE_ID_LEN, 0);
        if (status == CR_SUCCESS)
        {
            currentKeyboard.deviceName = szDeviceID;
        }

        //
        // 1) First check the service name. If we find this device has the PS/2 service name then it is a PS/2
        //    keyboard.
        //
        DWORD size = 0;
        if (!SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_SERVICE, NULL, NULL, NULL, &size))
        {
            try
            {
                buffer buf(size);
                if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_SERVICE, NULL, buf.get(), buf.size(), &size))
                {
                    LPCWSTR serviceName = (LPCWSTR)buf.get();
                    if (boost::iequals(serviceName, PS2ServiceName))
                    {
                        currentKeyboard.isPS2Keyboard = true;
                    }
                }
            }
            catch (std::bad_alloc)
            {
            }
        }

        //
        // 2) Fallback check for a PS/2 keyboard, if CompatibleIDs contains *PNP0303 then the keyboard is a PS/2 keyboard.
        //
        size = 0;
        if (!currentKeyboard.isPS2Keyboard && !SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_COMPATIBLEIDS, NULL, NULL, NULL, &size))
        {
            try
            {
                buffer buf(size);
                if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_COMPATIBLEIDS, NULL, buf.get(), buf.size(), &size))
                {
                    std::wstring value = (LPCWSTR)buf.get();

                    // Split the REG_MULTI_SZ values into separate strings.
                    boost::char_separator<wchar_t> sep(L"\0");
                    typedef boost::tokenizer< boost::char_separator<wchar_t>, std::wstring::const_iterator, std::wstring > WStringTokenzier;
                    WStringTokenzier tokens(value, sep);

                    // Look for the "*PNP0303" ID that indicates a PS2 keyboard device.
                    for (WStringTokenzier::iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
                    {
                        if (boost::iequals(itr->c_str(), PS2CompatibleId))
                        {
                            currentKeyboard.isPS2Keyboard = true;
                            break;
                        }
                    }
                }
            }
            catch (std::bad_alloc)
            {
            }
        }

        result.push_back(currentKeyboard);
    }
}

bool IsNonPS2Keyboard(const KeyboardState& keyboard)
{
    return !keyboard.isPS2Keyboard;
}

bool HasKeyboard()
{
    std::vector<KeyboardState> keyboards;
    GetKeyboardState(keyboards);

    int countOfNonPs2Keyboards = std::count_if(keyboards.begin(), keyboards.end(), IsNonPS2Keyboard);

    // Win 8 with touch support appear to always have an extra HID keyboard device which we
    // want to ignore.
    if ((NID_INTEGRATED_TOUCH & GetSystemMetrics(SM_DIGITIZER)) == NID_INTEGRATED_TOUCH)
    {
        return countOfNonPs2Keyboards > 1;
    }
    else
    {
        return countOfNonPs2Keyboards > 0;
    }
}

关于windows - 检测 Windows 8 桌面程序中的键盘存在,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11993680/

相关文章:

c++ - 在 Windows 上从用户模式程序发送和接收 ARP 数据

windows - x :Uid generator for Windows 8 projects

xaml - Windows 8 Metro 应用程序 XAML - 让自定义按钮在单击时闪烁?

c++ - 在 Windows-8 64 位中通过 eclipse luna 运行 valgrind/其他内存检查

windows - 如何使用 Windows 符号链接(symbolic link)正确地 git clone 指向子模块中的文件夹

database - 我可以在 Azure 上发布具有非关系数据库的 Web 应用程序吗?

node.js - 当我更改观看的文件时,fs.watch 触发了两次

javascript - 判断浏览器是在 windows 还是 mac/linux 上运行的 chrome

c# - 终止 TabletKeyboard(TabTip.exe) 应用程序的进程后,在 wpf 中不会恢复到其原始大小

windows-8 - 如何使用javascript取消Windows 8.1中的后台任务