- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
精简版:
在我正在测试的系统中,USB 设备和电缆应始终连接在相同的连接器上,因此在 USBview 应用程序中查看时,USB 树看起来应该始终相同。但由于我没有从该树中识别设备的信息,我仍然无法判断设备 X 是否在现场实际连接到 X。但是,我可以让设备 X 开始发送输入消息。所以我希望能够通过 USB 设备生成的输入消息来验证所有设备和布线是否正确连接。
带有更多详细信息的长版:我想测试所有 USB 电缆是否正确连接到系统中预先指定的连接器。要正确执行此操作,我需要有关系统中 USB 输入设备连接到的端口的信息。我知道这是可行的,因为我已经调试了 USBview 示例应用程序(它可以在 here 中找到)。不幸的是,我事先不知道连接的设备,所以我只能测试端口号并让设备生成输入消息来帮助我检查布线是否正确连接。为了做到这一点,我需要找出生成的消息的来源(它是设备位置信息)。这是我迷路的地方。
我已经订阅了从键盘和鼠标接收 WM_INPUT 消息,我得到了这些消息。我还通过从消息中获取原始设备名称(或路径,更多信息 here )并使用它在注册表中从 HKLM\SYSTEM\CurrentControlSet\Enum\USB
查找位置信息来获取生成消息的设备的位置信息。 .要查找位置信息,我首先找到以输入设备的硬件 ID(供应商 ID 或 VID 和产品 ID 或 PID)命名的子项,该子项也是原始设备路径的一部分,然后枚举其所有子项(实例 ID),直到找到一个带有 ParentIdPrefix
具有与实例 ID 匹配的值,该 ID 也是原始设备路径的一部分。对于那个子键,我查找 LocationInformation
的值(格式化 Port_#000X.Hub_#000Y
)。这适用于连接到我的笔记本电脑或扩展坞的键盘和鼠标,即使以随机顺序重新连接设备,我从输入消息中获得的端口和集线器编号也是一致且可靠的,但是当我以随机方式重新连接设备时,它不再一致和可靠在它们之间添加 USB 集线器。集线器编号似乎取决于集线器连接到系统的顺序,例如先连接 A,然后连接 B,结果 A 为 Port_#0001.Hub_#0004,B 为 Port_#0001.Hub_#0005,但连接它们反过来会导致 A 的 Port_#0001.Hub_#0005 和 B 的 Port_#0001.Hub_#0004(这是我的应用程序在下次收到输入消息时报告的位置信息)。尽管 USBview 示例应用程序报告这些设备的集线器和端口号一致(即使重新连接和重新启动),所以我查找位置信息时肯定有错误......但是什么?显然我不能仅仅依靠注册表来获取位置信息(我知道 USBview 使用 SetupDi* 调用和它自己的枚举例程)。所以如何可靠地找到与生成 WM_INPUT 消息的设备对应的 USBview 中的位置信息? 例如,我可以将在 WM_INPUT 消息中获得的原始输入设备句柄与我可以用来获取位置信息的任何内容进行匹配吗?
这是我到目前为止的代码......
...在 InitInstance 中:
// register for raw input device input messages
RAWINPUTDEVICE rid[2];
rid[0].usUsagePage = 0x01;
rid[0].usUsage = 0x06; // keyboard
rid[0].dwFlags =
RIDEV_DEVNOTIFY | // receive device arrival / removal messages
RIDEV_INPUTSINK; // receive messages even if not in foreground
rid[0].hwndTarget = hWnd;
rid[1].usUsagePage = 0x01;
rid[1].usUsage = 0x02; // mouse
rid[1].dwFlags =
RIDEV_DEVNOTIFY |
RIDEV_INPUTSINK;
rid[1].hwndTarget = hWnd;
if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE)
{
DisplayLastError(TEXT("Failed to register for raw input devices"), hWnd);
return FALSE;
}
return TRUE;
case WM_INPUT:
{
LONG lResult = Input(hWnd, lParam, ++ulCount);
if (lResult != 0) PostQuitMessage(lResult);
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
LONG Input(HWND hWnd, LPARAM lParam, ULONG ulCount)
{
UINT dwSize = 0;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
LPBYTE lpb = new BYTE[dwSize];
if (lpb == NULL)
{
MessageBox(hWnd, TEXT("Unable to allocate buffer for raw input data!"), TEXT("Error"), MB_OK);
return 1;
}
std::unique_ptr<BYTE, void(*)(LPBYTE)> lpbd(lpb, [](LPBYTE p) { delete[] p; });
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize)
{
MessageBox(hWnd, TEXT("GetRawInputData returned incorrect size!"), TEXT("Error"), MB_OK);
return 1;
}
RAWINPUT* raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEKEYBOARD && raw->data.keyboard.VKey == 0x51)
{
OutputDebugString(TEXT("Q for Quit was pressed, exiting application\n"));
return 1;
}
TCHAR ridDeviceName[256];
dwSize = 256;
UINT dwResult = GetRawInputDeviceInfo(raw->header.hDevice, RIDI_DEVICENAME, &ridDeviceName, &dwSize);
if (dwResult == 0 || dwResult == UINT(-1))
{
return DisplayLastError(TEXT("Failed to get raw input device info"), hWnd);
}
const std::wstring devicePath(ridDeviceName);
OutputDebugString((std::to_wstring(ulCount) + L": Received WM_INPUT for USB device with path: " + devicePath + L"\n").c_str());
HKEY hKey;
std::wstring keypath = L"SYSTEM\\CurrentControlSet\\Enum\\USB";
LONG lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keypath.c_str(), 0, KEY_READ, &hKey);
if (lResult != ERROR_SUCCESS)
{
keypath = keypath.insert(0, L"Failed to open registry key");
return DisplayLastError(&keypath[0], lResult, hWnd);
}
std::unique_ptr<HKEY__, void(*)(HKEY)> hkeyd(hKey, [](HKEY h) { RegCloseKey(h); });
DWORD dwIndex = 0;
TCHAR subKeyName[256];
do
{
DWORD dwSubKeyNameLength = 256;
lResult = RegEnumKeyEx(hKey, dwIndex++, subKeyName, &dwSubKeyNameLength, NULL, NULL, NULL, NULL);
if (lResult != ERROR_SUCCESS && lResult != ERROR_NO_MORE_ITEMS)
{
keypath = keypath.insert(0, L"Failed to enumerate registry key");
return DisplayLastError(&keypath[0], lResult, hWnd);
}
if (lResult == ERROR_SUCCESS && devicePath.find(subKeyName) != -1)
{
const std::wstring hardwareId(subKeyName);
keypath += L"\\" + hardwareId;
HKEY hSubKey;
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keypath.c_str(), 0, KEY_READ, &hSubKey);
if (lResult != ERROR_SUCCESS)
{
keypath = keypath.insert(0, L"Failed to open registry key");
return DisplayLastError(&keypath[0], lResult, hWnd);
}
std::unique_ptr<HKEY__, void(*)(HKEY)> hsubkeyd(hSubKey, [](HKEY h) { RegCloseKey(h); });
// \\?\HID#VID_046D&PID_C016#7&d0f899c&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}
// vendorID productID ParentIdPrefix (without the &0000)
// \\?\HID#VID_413C&PID_2003#7&2a634b73&0&0000#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}
// DeviceClass Guid, leads to prefixed info in registry
DWORD dwSubIndex = 0;
do
{
dwSubKeyNameLength = 256;
lResult = RegEnumKeyEx(hSubKey, dwSubIndex++, subKeyName, &dwSubKeyNameLength, NULL, NULL, NULL, NULL);
if (lResult != ERROR_SUCCESS && lResult != ERROR_NO_MORE_ITEMS)
{
keypath = keypath.insert(0, L"Failed to enumerate registry key");
return DisplayLastError(&keypath[0], lResult, hWnd);
}
if (lResult == ERROR_SUCCESS)
{
std::wstring targetkeypath = keypath + L"\\" + subKeyName;
HKEY hTargetKey;
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, targetkeypath.c_str(), 0, KEY_READ, &hTargetKey);
if (lResult != ERROR_SUCCESS)
{
targetkeypath = targetkeypath.insert(0, L"Failed to open registry key");
return DisplayLastError(&targetkeypath[0], lResult, hWnd);
}
std::unique_ptr<HKEY__, void(*)(HKEY)> htargetkeyd(hTargetKey, [](HKEY h) { RegCloseKey(h); });
TCHAR valueBuffer[256];
DWORD dwBufferSize = 256;
lResult = RegQueryValueEx(hTargetKey, L"ParentIdPrefix", 0, NULL, (LPBYTE)valueBuffer, &dwBufferSize);
if (lResult != ERROR_SUCCESS && lResult != ERROR_FILE_NOT_FOUND)
{
targetkeypath = targetkeypath.insert(0, L"Failed to get registry value of ParentIdPrefix for key");
return DisplayLastError(&targetkeypath[0], lResult, hWnd);
}
if (lResult == ERROR_SUCCESS && devicePath.find(valueBuffer) != -1)
{
dwBufferSize = 256;
lResult = RegQueryValueEx(hTargetKey, L"LocationInformation", 0, NULL, (LPBYTE)valueBuffer, &dwBufferSize);
if (lResult != ERROR_SUCCESS)
{
targetkeypath = targetkeypath.insert(0, L"Failed to get registry value of LocationInformation for key");
return DisplayLastError(&targetkeypath[0], lResult, hWnd);
}
OutputDebugString((std::to_wstring(ulCount) + L": " + hardwareId + L" is located at: " + valueBuffer + L"\n").c_str());
}
}
}
while (lResult == ERROR_SUCCESS || lResult == ERROR_FILE_NOT_FOUND);
}
}
while (lResult == ERROR_SUCCESS);
return ERROR_SUCCESS; // non-0 return codes indicate failure
}
SetupDiGetDeviceRegistryProperty
获取位置信息但这只是向我显示了我之前从注册表中获得的相同位置信息。 USBview 示例应用程序必须滚动它自己的枚举,一旦我发现它基于什么,我将使用它作为位置信息,而不是注册表报告的位置信息。我非常想知道为什么 USBview 示例应用程序报告 USB 连接的可靠端口编号(以及基于什么?)但系统在注册表中维护的位置信息似乎取决于连接顺序?
最佳答案
如果集线器的驱动程序正确设置了设备所连接的 USB 端口号,则可以在地址值的注册表中找到它。早期的瑞萨 USB3 驱动程序没有。您可以通过我的增强版USBview,UsbTreeView检查地址值是否设置正确。 .
我不知道它究竟来自哪里,但对于任何 USB 设备和 USB 集线器 CM_Get_DevInst_Registry_Property(CM_DRP_ADDRESS) 或 SetupDiGetDeviceRegistryProperty(SPDRP_ADDRESS) 提供 USB 端口号。
USBview 使用 USB API 使用自下而上的方法。由于您从设备的 DevicePath 开始,使用设置 API 的自下而上的方法更方便:
您需要设备的 DEVINST 通过 CM_Get_Parent 向上走设备树并读取每个设备的地址,直到您点击 USB 根集线器或其主机 Controller 。因为只有 DevicePath,所以首先需要设备的 InterfaceClassGuid。您可以通过 SetupDiOpenDeviceInterface 获得它。
使用 InterfaceClassGuid,您可以通过 SetupDiGetClassDevs 获得设备列表。通过 SetupDiEnumDeviceInterfaces(提供 DevicePath)请求每个设备索引,直到您点击您的设备或它返回 FALSE。
如果你点击你的设备,你也会得到 DevInst 并到达 CM_ API 的世界,在那里你可以通过 CM_Get_Parent(&DevInstParent, DevInst) 向上走设备树。您的 HID 设备的第一个父设备可能是它的 USB 设备,其父设备是 USB 标准集线器或 USB 根集线器。
我从未见过具有 USB 硬件序列号的 USB 标准集线器,因此当它连接到新位置时,Windows 会为其创建一个新设备实例。
您所能得到的只是它的设备实例 ID (CM_Get_Device_ID),其中有一个固定部分,如 USB\VID_05E3&PID_0608,以及每个新实例的末尾生成部分,如 5&130B8FC2&0&3。如果您的设备树没有改变,那么这应该足够了。
USB 端口号不是任意的,它们是固定的,因此 UsbTreeView 显示为“端口链”的内容是固定的,并给出了几乎完整的位置信息。作为主机 Controller 的编号,UsbTreeView 在 GUID_DEVINTERFACE_USB_HOST_CONTROLLER SetupDi 枚举中使用其索引。添加或删除主机 Controller 时,这可能会发生变化。因此,与其枚举索引,其设备实例 ID 在这里可能是更好的选择。
USB 端口号在硬件中,因此它们不会更改。
关于c++ - 查找生成 WM_INPUT 消息的设备的位置信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23954997/
iphone设备UDID、iphone设备ID和iphone设备Token之间有什么区别? 通常,当我们使用苹果推送通知服务时,会使用 iPhone 设备 token 。 但我的目标只是识别唯一的 i
我们使用 firebase 从服务器向 Android 和 IOS 设备发送通知,并且我们使用旧版 FCM 发送通知。但是当我们的应用程序在后台时,通知由系统本身处理,因此我们无法通过应用程序处理它。
在 Google 上搜索后,我发现人们说只能通过“MFi 程序”将 iOS 设备与非 iOS 设备连接起来。这是真的吗? 我的项目主要集中于直接通过蓝牙与Arduino设备发送和接收信息。 iOS和非
所以我有一个通用应用程序,我正在设置 UIScrollView 的内容大小。显然,iPhone 和 iPad 上的内容大小会有所不同。如何为 iPad 设置某种尺寸,为 iPhone 和 iPod t
问题:如何在 pod 中使用连接到主机的原始设备作为 block 设备。 我尝试使用类型为“BlockDevice”的“hostPath” volumes: - my-data: hostPath
Implemented GCKDeviceScannerListener Singleton Class on ViewController, however its delegate methods
我有一个 (PhoneGap) 应用程序,它将成功获得 Passbook 通行证,并且还将成功接收与 Passbook 分开的推送通知(当伪造设备 ID 时)。 我遇到的问题是发送给注册设备的设备 I
我正在尝试找到一种方法,通过我目前正在使用的 iOS 应用程序访问我的信标的电池电量。我正在使用 Kontakt 的 iBeacon 设备。我浏览了 Estimote iOS SDK,他们提供了一种实
我正在努力让 CUDA 应用程序也能监控 GPU 的核心温度。可通过 NVAPI 访问该信息。 问题是我想确保在运行代码时监控的是同一个 GPU。 但是,似乎有信息表明我从 NvAPI_EnumPhy
从沙箱模式到生产模式,设备 token 有何不同? 我认为我已将一些设备 token 锁定为生产模式,并且无法将它们从开发中插入。 关于如何检查有什么想法吗? 最佳答案 当您使用开发证书构建应用程序时
目录 /run/user/1000/gvfs 和 ~/.gvfs 分别是空的和不存在的。我的图形文件管理器 (Thunar) 能够检测和访问设备的内部和外部存储器。 命令 gvfs-mount -l
我有一个 Android 平板电脑,它有一个迷你 USB 端口和一个 USB 端口,我想编写一个与 USB key 通信的应用程序。我写了一个demo来找出U盘,但是没有任何反应。 令我不安的是,如果
我们将 PHP 版本从 5.4.25 更改为 5.4.45,并在服务器上安装了 MS SQL 驱动程序。在更改服务器之前,一切正常,但在更改服务器之后,我遇到了 Web 服务问题。我们的身份验证 So
我想知道是否有人使用此 API 在 Android 设备上同时从 2 个后置摄像头捕获图像或视频:https://source.android.com/docs/core/camera/concurr
我正在为客户构建一个物联网解决方案,网络管理员坚持要求设备仅通过访客网络进行连接,该网络有一个强制门户,其中的服务条款必须通过按下 UI 按钮来接受,然后才能获得外部互联网访问。到目前为止,我见过的大
我无法弄清楚这里的格式规则..在我的示例中,代码行太多,无法为每行添加 4 个空格,因此这里是我需要帮助的代码的链接 http://nitemsg.blogspot.com/2011/01/heres
如果我在我的设备上接受推送通知,并且不保存设备 token ,那么我如何在自定义 View 中查看设备 token 或恢复警报 View ? 我删除了应用程序并重新安装,但看不到设备 token 警报
我试图找出在尝试并行比较和复制设备 block 与 pthreads 时我做错了什么。看起来我正在脱离同步并且比较阶段无法正常工作。任何帮助将不胜感激 #ifndef __dbg_h__ #defin
我刚刚写完所有这些内容,但这个红色的小栏告诉我我不能发布图片或两个以上的链接。因此,如果您可以引用 this Imgur album , 那简直太好了。谢谢。 我在这里相对较新,甚至对 android
我需要启用 mysql 常规日志并将其通过 nsf 移动到我系统中的另一个驱动器/设备! 所以,我在 my.cnf 中启用了它: general_log = 1 general_log_fi
我是一名优秀的程序员,十分优秀!