对于Metro应用程序,有Windows.Devices.Input.KeyboardCapabilities.KeyboardPresent。 有没有办法让Windows 8桌面程序检测是否存在物理键盘?
答案 0 :(得分:0)
这有点繁琐,我不知道我提议的方法是否适用于所有情况,但这是我最终使用的方法:
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