检测Windows 8桌面程序中的键盘状态

时间:2012-08-16 18:57:23

标签: windows windows-8

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

3 个答案:

答案 0 :(得分:0)

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

  1. 使用SetupDiGetClassDevs查找所有键盘设备。
  2. 使用SetupDiGetDeviceRegistryProperty读取某些键盘设备属性以忽略PS / 2键盘
  3. 检查触控支持,因为Win 8触控设备似乎总是有一个额外的HID键盘设备。
  4. 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;
        }
    }
    

答案 1 :(得分:0)

在Windows 10上,此API是UWP API的一部分,而桌面应用程序中的can be called也很好。

要使用C#(或其他.NET语言)进行调用,您需要向项目文件添加一些引用:

<Reference Include="System.Runtime.WindowsRuntime" />
<Reference Include="Windows.Foundation.FoundationContract">
  <HintPath>C:\Program Files (x86)\Windows Kits\10\References\10.0.16299.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd</HintPath>
  <Private>False</Private>
</Reference>
<Reference Include="Windows.Foundation.UniversalApiContract">
  <HintPath>C:\Program Files (x86)\Windows Kits\10\References\10.0.16299.0\Windows.Foundation.UniversalApiContract\5.0.0.0\Windows.Foundation.UniversalApiContract.winmd</HintPath>
  <Private>False</Private>
</Reference>

第一个参考将针对GAC进行解析,另外两个参考位于VS安装中(如果需要,可以选择其他版本)。将Private设置为False意味着不部署这些程序集的本地副本。

Console.WriteLine(new Windows.Devices.Input.KeyboardCapabilities().KeyboardPresent != 0 ? "keyboard available" : "no keyboard");

在C ++中,您可以执行以下操作:

#include <roapi.h>
#include <wrl.h>
#include <windows.devices.input.h>

#pragma comment(lib, "runtimeobject")

int APIENTRY wWinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR lpCmdLine,
    _In_ int nCmdShow)
{
    RoInitialize(RO_INIT_MULTITHREADED);
    {
        INT32 isKeyboardAvailable;
        Microsoft::WRL::ComPtr<ABI::Windows::Devices::Input::IKeyboardCapabilities> pKeyboardCapabilities;
        Microsoft::WRL::Wrappers::HStringReference KeyboardClass(RuntimeClass_Windows_Devices_Input_KeyboardCapabilities);
        if (SUCCEEDED(RoActivateInstance(KeyboardClass.Get(), &pKeyboardCapabilities)) &&
            SUCCEEDED(pKeyboardCapabilities->get_KeyboardPresent(&isKeyboardAvailable)))
        {
            OutputDebugStringW(isKeyboardAvailable ? L"keyboard available\n" : L"no keyboard\n");
        }
    }
    RoUninitialize();
}

答案 2 :(得分:-1)

简单:查看HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet001 \ Services \ kbdclass