|
|
@@ -615,321 +615,35 @@ void CSystem::HideSystemKeyboard()
|
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
|
-// 新增2:已知扫码枪品牌 VID 黑名单(按实际设备扩充)
|
|
|
-// 常见:Honeywell=VID_0C2E, Zebra/Symbol=VID_05E0
|
|
|
-// 你的这台:VID_2010(Tectonica/USBKey 通用HID)
|
|
|
+// 精确检测:当前系统是否有"可触摸的屏幕"(排除触摸板)
|
|
|
// ============================================================
|
|
|
-bool IsKnownScannerPath(const wchar_t* szDevicePath)
|
|
|
+bool CSystem::HasTouchScreenDevice()
|
|
|
{
|
|
|
- static const wchar_t* kScannerVidList[] = {
|
|
|
- L"VID_0C2E", // Honeywell 霍尼韦尔
|
|
|
- L"VID_05E0", // Zebra / Symbol 斑马
|
|
|
- //L"VID_2010", // 你的当前扫码枪(通用HID)
|
|
|
- };
|
|
|
- if (szDevicePath == nullptr)
|
|
|
- return false;
|
|
|
- for (const wchar_t* vid : kScannerVidList)
|
|
|
- {
|
|
|
- if (wcsstr(szDevicePath, vid) != nullptr)
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-// ============================================================
|
|
|
-// 修改:IsRealPhysicalKeyboard 增加 hDevice 参数 + 两个新校验
|
|
|
-// ============================================================
|
|
|
-bool IsRealPhysicalKeyboard(HANDLE hDevice, LPCWSTR szDevicePath, std::wstring* pDebugOut)
|
|
|
-{
|
|
|
- std::wstring dbg;
|
|
|
-
|
|
|
- // ========== 1. 过滤 Microsoft Keyboard RID 系统虚拟键盘 ==========
|
|
|
- if (szDevicePath != nullptr && wcsstr(szDevicePath, L"Microsoft Keyboard RID") != nullptr)
|
|
|
- {
|
|
|
- dbg += L"RID虚拟键盘,直接过滤\n";
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
+ UINT deviceCount = 0;
|
|
|
+ if (GetRawInputDeviceList(nullptr, &deviceCount, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1 || deviceCount == 0)
|
|
|
return false;
|
|
|
- }
|
|
|
|
|
|
- // ========== 2. 已知扫码枪品牌VID黑名单(保留) ==========
|
|
|
- if (IsKnownScannerPath(szDevicePath))
|
|
|
- {
|
|
|
- dbg += L"HID: 命中扫码枪VID黑名单,过滤\n";
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
+ std::vector<RAWINPUTDEVICELIST> devices(deviceCount);
|
|
|
+ if (GetRawInputDeviceList(devices.data(), &deviceCount, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1)
|
|
|
return false;
|
|
|
- }
|
|
|
|
|
|
- // ========== 3. 过滤无VID_的内置主板/笔记本内置HID键盘 ==========
|
|
|
- if (wcsstr(szDevicePath, L"VID_") == nullptr)
|
|
|
+ for (UINT i = 0; i < deviceCount; i++)
|
|
|
{
|
|
|
- dbg += L"HID: 设备路径无VID_,判定为本机内置HID,过滤\n";
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
- return false;
|
|
|
- }
|
|
|
+ if (devices[i].dwType != RIM_TYPEHID)
|
|
|
+ continue;
|
|
|
|
|
|
- // ========== 4. RIDI_DEVICEINFO:键位总数 + 指示灯数【核心判据】 ==========
|
|
|
- {
|
|
|
RID_DEVICE_INFO info{};
|
|
|
info.cbSize = sizeof(info);
|
|
|
UINT size = sizeof(info);
|
|
|
- UINT actual = GetRawInputDeviceInfoW(hDevice, RIDI_DEVICEINFO, &info, &size);
|
|
|
+ UINT actual = GetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICEINFO, &info, &size);
|
|
|
if (actual == (UINT)-1 || actual < sizeof(info))
|
|
|
- {
|
|
|
- dbg += L"RIDI_DEVICEINFO 获取失败\n";
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- const DWORD kbdKeys = info.keyboard.dwNumberOfKeysTotal;
|
|
|
- const DWORD kbdInd = info.keyboard.dwNumberOfIndicators;
|
|
|
-
|
|
|
- dbg += L"[Debug] KbdMode=" + std::to_wstring(info.keyboard.dwKeyboardMode)
|
|
|
- + L" FuncKeys=" + std::to_wstring(info.keyboard.dwNumberOfFunctionKeys)
|
|
|
- + L" Indicators=" + std::to_wstring(kbdInd)
|
|
|
- + L" KeysTotal=" + std::to_wstring(kbdKeys) + L"\n";
|
|
|
-
|
|
|
- // 主判据:真实键盘一般 101+ 键;扫码枪/伪键盘通常 <30(Raymond Chen 方法)
|
|
|
- constexpr DWORD kMinKeysTotal = 30;
|
|
|
- if (kbdKeys < kMinKeysTotal)
|
|
|
- {
|
|
|
- dbg += L"KeysTotal<30,判定为扫码枪/伪键盘,过滤\n";
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // 次判据:真实键盘通常有键盘指示灯(你的罗技两台都是 Indicators=3,扫码枪多为0)
|
|
|
- // 若以后发现某真实键盘被这条误杀,把 kRequireIndicators 改为 false 即可
|
|
|
- constexpr bool kRequireIndicators = true;
|
|
|
- if (kRequireIndicators && kbdInd < 1)
|
|
|
- {
|
|
|
- dbg += L"Indicators<1,判定为扫码枪/伪键盘,过滤\n";
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // ========== 5. 打开HID设备:只校验顶层用法,不再用OutputReport/LED当过滤条件 ==========
|
|
|
- HANDLE hTestOnline = CreateFileW(
|
|
|
- szDevicePath, 0,
|
|
|
- FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
- nullptr, OPEN_EXISTING, 0, nullptr);
|
|
|
- if (hTestOnline == INVALID_HANDLE_VALUE)
|
|
|
- {
|
|
|
- dbg += L"CreateFileW 打开设备失败\n";
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- PHIDP_PREPARSED_DATA pPreparse = nullptr;
|
|
|
- if (!HidD_GetPreparsedData(hTestOnline, &pPreparse) || pPreparse == nullptr)
|
|
|
- {
|
|
|
- dbg += L"HidD_GetPreparsedData 获取PreparsedData失败\n";
|
|
|
- CloseHandle(hTestOnline);
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- HIDP_CAPS caps{};
|
|
|
- NTSTATUS ntStatus = HidP_GetCaps(pPreparse, &caps);
|
|
|
- if (ntStatus != HIDP_STATUS_SUCCESS)
|
|
|
- {
|
|
|
- dbg += L"HidP_GetCaps 获取Caps失败\n";
|
|
|
- HidD_FreePreparsedData(pPreparse);
|
|
|
- CloseHandle(hTestOnline);
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (!(caps.UsagePage == 0x01 && caps.Usage == 0x06))
|
|
|
- {
|
|
|
- dbg += L"HID: Top-Level 不是标准键盘,过滤\n";
|
|
|
- HidD_FreePreparsedData(pPreparse);
|
|
|
- CloseHandle(hTestOnline);
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // 仅诊断,不参与判定:
|
|
|
- // 罗技等现代键盘的键盘集合 OutputReportByteLength 也可能=0,
|
|
|
- // 手动扫标准LED用法(0x45/0x46/0x47)也可能扫不到,都不能拿来过滤
|
|
|
- dbg += L"[Debug] OutputReportLen=" + std::to_wstring(caps.OutputReportByteLength) + L"\n";
|
|
|
-
|
|
|
- HidD_FreePreparsedData(pPreparse);
|
|
|
- CloseHandle(hTestOnline);
|
|
|
-
|
|
|
- dbg += L"HID: 通过真实键盘判定\n";
|
|
|
- if (pDebugOut) *pDebugOut = dbg;
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-bool CSystem::HasKeyboardDevice()
|
|
|
-{
|
|
|
- UINT deviceCount = 0;
|
|
|
- UINT ret = GetRawInputDeviceList(nullptr, &deviceCount, sizeof(RAWINPUTDEVICELIST));
|
|
|
- if (ret == (UINT)-1 || deviceCount == 0)
|
|
|
- return false;
|
|
|
-
|
|
|
- std::vector<RAWINPUTDEVICELIST> devices(deviceCount);
|
|
|
- ret = GetRawInputDeviceList(devices.data(), &deviceCount, sizeof(RAWINPUTDEVICELIST));
|
|
|
- if (ret == (UINT)-1)
|
|
|
- return false;
|
|
|
-
|
|
|
- bool hasRealKeyboard = false; // 和ShowKeyboardDeviceNames保持一致:用布尔标记
|
|
|
-
|
|
|
- for (UINT i = 0; i < deviceCount; i++)
|
|
|
- {
|
|
|
- if (devices[i].dwType != RIM_TYPEKEYBOARD)
|
|
|
continue;
|
|
|
-
|
|
|
- // 1. 先获取设备路径
|
|
|
- UINT pathLen = 0;
|
|
|
- GetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, nullptr, &pathLen);
|
|
|
- if (pathLen == 0)
|
|
|
+ if (info.dwType != RIM_TYPEHID)
|
|
|
continue;
|
|
|
|
|
|
- std::wstring devPath(pathLen, L'\0');
|
|
|
- GetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, devPath.data(), &pathLen);
|
|
|
-
|
|
|
- // 截断末尾多余'\0'
|
|
|
- size_t zeroPos = devPath.find(L'\0');
|
|
|
- if (zeroPos != std::wstring::npos)
|
|
|
- devPath.resize(zeroPos);
|
|
|
-
|
|
|
- // 2. 调用IsRealPhysicalKeyboard,传入路径
|
|
|
- std::wstring itemDebug;
|
|
|
- bool realKb = IsRealPhysicalKeyboard(devices[i].hDevice, devPath.c_str(), &itemDebug);
|
|
|
-
|
|
|
- if (realKb)
|
|
|
- {
|
|
|
- hasRealKeyboard = true; // 只要有一个真实键盘就置true,与弹窗逻辑完全对齐
|
|
|
-
|
|
|
- break; // 找到一个真实键盘就可以退出循环
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 返回值完全对齐ShowKeyboardDeviceNames:只要IsRealPhysicalKeyboard有一个为true就返回true
|
|
|
- return hasRealKeyboard;
|
|
|
-}
|
|
|
-
|
|
|
-void CSystem::ShowKeyboardDeviceNames()
|
|
|
-{
|
|
|
- UINT deviceCount = 0;
|
|
|
- UINT ret = ::GetRawInputDeviceList(nullptr, &deviceCount, sizeof(RAWINPUTDEVICELIST));
|
|
|
- std::wostringstream oss;
|
|
|
-
|
|
|
- if (ret == (UINT)-1 || deviceCount == 0)
|
|
|
- {
|
|
|
- oss << L"HasKeyboardDevice:\r\n";
|
|
|
- oss << L"GetRawInputDeviceList 返回错误 或者 设备数量=0\r\n";
|
|
|
- CLewaimaiLog::OutputMessageBox(oss.str());
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- std::vector<RAWINPUTDEVICELIST> devices(deviceCount);
|
|
|
- ret = ::GetRawInputDeviceList(devices.data(), &deviceCount, sizeof(RAWINPUTDEVICELIST));
|
|
|
- if (ret == (UINT)-1)
|
|
|
- {
|
|
|
- oss << L"HasKeyboardDevice:\r\n";
|
|
|
- oss << L"GetRawInputDeviceList 获取设备列表失败\r\n";
|
|
|
- CLewaimaiLog::OutputMessageBox(oss.str());
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- bool hasRealKeyboard = false;
|
|
|
- oss << L"=== RawInput 键盘枚举结果 ===\r\n";
|
|
|
- oss << L"总RawInput设备数:" << deviceCount << L"\r\n\r\n";
|
|
|
-
|
|
|
- for (UINT i = 0; i < deviceCount; ++i)
|
|
|
- {
|
|
|
- oss << L"[" << i << L"] dwType=" << devices[i].dwType;
|
|
|
- if (devices[i].dwType == RIM_TYPEKEYBOARD)
|
|
|
- {
|
|
|
- oss << L" (RIM_TYPEKEYBOARD)\r\n";
|
|
|
-
|
|
|
- // 先获取DevicePath,不能直接传hDevice给IsRealPhysicalKeyboard
|
|
|
- bool realKb = false;
|
|
|
- std::wstring devName;
|
|
|
- std::wstring itemDebug;
|
|
|
-
|
|
|
- UINT nameLen = 0;
|
|
|
- UINT riRet = GetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, nullptr, &nameLen);
|
|
|
- if (riRet != (UINT)-1 && nameLen > 0)
|
|
|
- {
|
|
|
- devName.resize(nameLen, L'\0');
|
|
|
- riRet = GetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, devName.data(), &nameLen);
|
|
|
- if (riRet != (UINT)-1)
|
|
|
- {
|
|
|
- size_t realLen = devName.find(L'\0');
|
|
|
- if (realLen != std::wstring::npos)
|
|
|
- devName.resize(realLen);
|
|
|
-
|
|
|
- // 传入路径,拿到判定结果与子函数调试信息
|
|
|
- realKb = IsRealPhysicalKeyboard(devices[i].hDevice, devName.c_str(), &itemDebug);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- oss << L" IsRealKeyboard: " << (realKb ? L"YES(真实键盘)" : L"NO(鼠标侧键/虚拟键盘)") << L"\r\n";
|
|
|
- // 输出IsRealPhysicalKeyboard内部调试(可选,方便排错)
|
|
|
- if (!itemDebug.empty())
|
|
|
- {
|
|
|
- oss << L" [SubDebug] " << itemDebug << L"\r\n";
|
|
|
- }
|
|
|
-
|
|
|
- if (realKb)
|
|
|
- {
|
|
|
- hasRealKeyboard = true;
|
|
|
- }
|
|
|
-
|
|
|
- // DevicePath
|
|
|
- if (!devName.empty())
|
|
|
- {
|
|
|
- oss << L" DevicePath: " << devName.c_str() << L"\r\n";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- oss << L" DevicePath: (获取失败)\r\n";
|
|
|
- }
|
|
|
-
|
|
|
- // FriendlyName
|
|
|
- UINT friendlyLen = 0;
|
|
|
- riRet = GetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICEFRIENDLYNAME, nullptr, &friendlyLen);
|
|
|
- if (riRet != (UINT)-1 && friendlyLen > 0)
|
|
|
- {
|
|
|
- std::wstring friendlyName(friendlyLen, L'\0');
|
|
|
- riRet = GetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICEFRIENDLYNAME, friendlyName.data(), &friendlyLen);
|
|
|
- if (riRet != (UINT)-1)
|
|
|
- {
|
|
|
- size_t realLen = friendlyName.find(L'\0');
|
|
|
- if (realLen != std::wstring::npos)
|
|
|
- friendlyName.resize(realLen);
|
|
|
- oss << L" FriendlyName: " << friendlyName.c_str() << L"\r\n";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- oss << L" FriendlyName: (获取失败)\r\n";
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- oss << L" FriendlyName: (无)\r\n";
|
|
|
- }
|
|
|
- oss << L"\r\n";
|
|
|
- }
|
|
|
- else if (devices[i].dwType == RIM_TYPEMOUSE)
|
|
|
- {
|
|
|
- oss << L" (RIM_TYPEMOUSE)\r\n\r\n";
|
|
|
- }
|
|
|
- else if (devices[i].dwType == RIM_TYPEHID)
|
|
|
- {
|
|
|
- oss << L" (RIM_TYPEHID)\r\n\r\n";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- oss << L" (其他类型)\r\n\r\n";
|
|
|
- }
|
|
|
+ // 用途页 0x0D=数字化仪,用途 0x04=触摸屏
|
|
|
+ if (info.hid.usUsagePage == 0x0D && info.hid.usUsage == 0x04)
|
|
|
+ return true;
|
|
|
}
|
|
|
-
|
|
|
- oss << L"============================\r\n";
|
|
|
- oss << L"检测到真实物理键盘:" << (hasRealKeyboard ? L"是" : L"否") << L"\r\n";
|
|
|
- CLewaimaiLog::OutputMessageBox(oss.str());
|
|
|
+ return false;
|
|
|
}
|