张洋 1 month ago
parent
commit
b899630462

BIN
bin/Win32/Release/zhipuzi_pos_windows/zhipuzi_pos_windows.exe


+ 95 - 68
zhipuzi_pos_windows/helper/CSystem.cpp

@@ -614,8 +614,32 @@ void CSystem::HideSystemKeyboard()
 	}
 }
 
-// 核心判断:是否为真实物理键盘
-bool IsRealPhysicalKeyboard(LPCWSTR szDevicePath, std::wstring* pDebugOut)
+// ============================================================
+// 新增2:已知扫码枪品牌 VID 黑名单(按实际设备扩充)
+// 常见:Honeywell=VID_0C2E, Zebra/Symbol=VID_05E0
+// 你的这台:VID_2010(Tectonica/USBKey 通用HID)
+// ============================================================
+bool IsKnownScannerPath(const wchar_t* szDevicePath)
+{
+	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;
 
@@ -627,17 +651,68 @@ bool IsRealPhysicalKeyboard(LPCWSTR szDevicePath, std::wstring* pDebugOut)
 		return false;
 	}
 
-	// ========== 2. 打开HID设备 ==========
+	// ========== 2. 已知扫码枪品牌VID黑名单(保留) ==========
+	if (IsKnownScannerPath(szDevicePath))
+	{
+		dbg += L"HID: 命中扫码枪VID黑名单,过滤\n";
+		if (pDebugOut) *pDebugOut = dbg;
+		return false;
+	}
+
+	// ========== 3. 过滤无VID_的内置主板/笔记本内置HID键盘 ==========
+	if (wcsstr(szDevicePath, L"VID_") == nullptr)
+	{
+		dbg += L"HID: 设备路径无VID_,判定为本机内置HID,过滤\n";
+		if (pDebugOut) *pDebugOut = dbg;
+		return false;
+	}
+
+	// ========== 4. RIDI_DEVICEINFO:键位总数 + 指示灯数【核心判据】 ==========
+	{
+		RID_DEVICE_INFO info{};
+		info.cbSize = sizeof(info);
+		UINT size = sizeof(info);
+		UINT actual = GetRawInputDeviceInfoW(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,
+		szDevicePath, 0,
 		FILE_SHARE_READ | FILE_SHARE_WRITE,
-		nullptr,
-		OPEN_EXISTING,
-		0,
-		nullptr
-	);
-
+		nullptr, OPEN_EXISTING, 0, nullptr);
 	if (hTestOnline == INVALID_HANDLE_VALUE)
 	{
 		dbg += L"CreateFileW 打开设备失败\n";
@@ -646,8 +721,7 @@ bool IsRealPhysicalKeyboard(LPCWSTR szDevicePath, std::wstring* pDebugOut)
 	}
 
 	PHIDP_PREPARSED_DATA pPreparse = nullptr;
-	BOOLEAN bRet = HidD_GetPreparsedData(hTestOnline, &pPreparse);
-	if (!bRet || pPreparse == nullptr)
+	if (!HidD_GetPreparsedData(hTestOnline, &pPreparse) || pPreparse == nullptr)
 	{
 		dbg += L"HidD_GetPreparsedData 获取PreparsedData失败\n";
 		CloseHandle(hTestOnline);
@@ -655,7 +729,6 @@ bool IsRealPhysicalKeyboard(LPCWSTR szDevicePath, std::wstring* pDebugOut)
 		return false;
 	}
 
-	// ========== 3. 获取HID顶层能力 ==========
 	HIDP_CAPS caps{};
 	NTSTATUS ntStatus = HidP_GetCaps(pPreparse, &caps);
 	if (ntStatus != HIDP_STATUS_SUCCESS)
@@ -667,70 +740,24 @@ bool IsRealPhysicalKeyboard(LPCWSTR szDevicePath, std::wstring* pDebugOut)
 		return false;
 	}
 
-	// ========== 4. 校验顶层必须是标准键盘 UsagePage=0x01, Usage=0x06 ==========
 	if (!(caps.UsagePage == 0x01 && caps.Usage == 0x06))
 	{
-		dbg += L"HID: Top‑Level 不是标准键盘,过滤\n";
+		dbg += L"HID: Top-Level 不是标准键盘,过滤\n";
 		HidD_FreePreparsedData(pPreparse);
 		CloseHandle(hTestOnline);
 		if (pDebugOut) *pDebugOut = dbg;
 		return false;
 	}
 
-	//=====新增:过滤没有VID_的内置主板/笔记本内置HID键盘=====
-	if (wcsstr(szDevicePath, L"VID_") == nullptr)
-	{
-		dbg += L"HID: 设备路径无VID_,判定为本机内置HID,过滤\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";
 
-	// ========== 5. OutputReportByteLength == 0 拦截鼠标侧键虚拟键盘 ==========
-	if (caps.OutputReportByteLength == 0)
-	{
-		dbg += L"HID: OutputReport长度为0,判定鼠标侧键虚拟键盘,过滤\n";
-		HidD_FreePreparsedData(pPreparse);
-		CloseHandle(hTestOnline);
-		if (pDebugOut) *pDebugOut = dbg;
-		return false;
-	}
-
-	// ========== 6. LED指示灯检测【仅调试打印,不做过滤条件】 ==========
-	bool hasKeyboardLed = false;
-	USHORT valueCapsCount = 0;
-	ntStatus = HidP_GetValueCaps(HidP_Output, nullptr, &valueCapsCount, pPreparse);
-	if (ntStatus == HIDP_STATUS_SUCCESS && valueCapsCount > 0)
-	{
-		std::vector<HIDP_VALUE_CAPS> valueCaps(valueCapsCount);
-		ntStatus = HidP_GetValueCaps(HidP_Output, valueCaps.data(), &valueCapsCount, pPreparse);
-		if (ntStatus == HIDP_STATUS_SUCCESS)
-		{
-			for (USHORT i = 0; i < valueCapsCount; i++)
-			{
-				auto& vc = valueCaps[i];
-				if (vc.UsagePage == 0x01 && !vc.IsRange)
-				{
-					USAGE u = vc.NotRange.Usage;
-					if (u == 0x45 || u == 0x46 || u == 0x47)
-					{
-						hasKeyboardLed = true;
-						break;
-					}
-				}
-			}
-		}
-	}
-	dbg += L"[Debug] HasKeyboardLed=" + std::to_wstring(hasKeyboardLed) + L"\n";
-
-	// ========== 全部校验通过,判定真实物理键盘 ==========
-	dbg += L"HID: 通过真实键盘判定\n";
-
-	// 资源释放
 	HidD_FreePreparsedData(pPreparse);
 	CloseHandle(hTestOnline);
 
+	dbg += L"HID: 通过真实键盘判定\n";
 	if (pDebugOut) *pDebugOut = dbg;
 	return true;
 }
@@ -770,7 +797,7 @@ bool CSystem::HasKeyboardDevice()
 
 		// 2. 调用IsRealPhysicalKeyboard,传入路径
 		std::wstring itemDebug;
-		bool realKb = IsRealPhysicalKeyboard(devPath.c_str(), &itemDebug);
+		bool realKb = IsRealPhysicalKeyboard(devices[i].hDevice, devPath.c_str(), &itemDebug);
 
 		if (realKb)
 		{
@@ -837,7 +864,7 @@ void CSystem::ShowKeyboardDeviceNames()
 						devName.resize(realLen);
 
 					// 传入路径,拿到判定结果与子函数调试信息
-					realKb = IsRealPhysicalKeyboard(devName.c_str(), &itemDebug);
+					realKb = IsRealPhysicalKeyboard(devices[i].hDevice, devName.c_str(), &itemDebug);
 				}
 			}
 

+ 1 - 1
zhipuzi_pos_windows/page/CDiandanPageUI.cpp

@@ -825,7 +825,7 @@ void CDiandanPageUI::HandleClickMsg(TNotifyUI& msg)
 	else if (name == L"btn_diandan_qingkong")
 	{
 		//纯粹用于调试物理键盘
-		//CSystem::ShowKeyboardDeviceNames();
+		CSystem::ShowKeyboardDeviceNames();
 
 		this->ClickQingkong();
 	}