CreateFile 无法在 Windows 中打开 HID 设备
Posted
技术标签:
【中文标题】CreateFile 无法在 Windows 中打开 HID 设备【英文标题】:CreateFileA fails to open HID device in Windows 【发布时间】:2016-05-13 15:13:10 【问题描述】:编辑:此处报告的问题:https://github.com/signal11/hidapi/issues/276
Inkling 是 Wacom 的一款笔设备。 (InklingReader) 是一个开源项目,可以从中获取实时数据。
我正在尝试整理 InklingReader 以使用 HIDAPI 而不是 libusb (因为它在更高级别上工作:HID 而不是原始 USB,因此更紧凑和更合适。libusb 也失败了最近的 OSX)。
HID API 一个小库:一个 .h,一个(每个平台).c。
我的代码如下所示:
unsigned short inklingVendorId = 0x056a, inklingProductId = 0x0221;
if (hid_init() == FAIL) return;
handle = hid_open(inklingVendorId, inklingProductId, nullptr);
在 Windows 上 hid_open
失败。单步显示故障点here:
// path = "\\\\?\\hid#vid_056a&pid_0221&mi_00&col01#8&1ea90857&0&0000#"
// "4d1e55b2-f16f-11cf-88cb-001111000030"
//
static HANDLE open_device(const char *path, BOOL enumerate)
HANDLE handle;
DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;
// enumerate = 0
handle = CreateFileA(path,
desired_access,
share_mode,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
0);
int err = GetLastError(); // 5 i.e. ERROR_ACCESS_DENIED
return handle; // returns 0xffffffff i.e. INVALID_HANDLE
现在 HIDAPI 作者说“HIDAPI 无法在 Windows 上使用键盘和鼠标。作为安全措施的 Windows 不允许打开鼠标和键盘 HID。” (here)
如果我列举 HID 设备:
struct hid_device_info *devs, *cur_dev;
devs = hid_enumerate(inklingVendorId, inklingProductId);
cur_dev = devs;
while (cur_dev)
DBG2("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
DBG2("");
DBG2(" Manufacturer: %ls", cur_dev->manufacturer_string);
DBG2(" Product: %ls", cur_dev->product_string);
DBG2(" Release: %hx", cur_dev->release_number);
DBG2(" Interface: %d", cur_dev->interface_number);
DBG2(" Usage Page: %d", cur_dev->usage_page);
DBG2(" Usage: %d", cur_dev->usage);
DBG2("");
cur_dev = cur_dev->next;
hid_free_enumeration(devs);
...我得到的不是一个而是两个条目:
Device Found
type: 056a 0221
path: \\?\hid#vid_056a&pid_0221&mi_00&col01#8&1ea90857&0&0000#4d1e55b2-f16f-11cf-88cb-001111000030
serial_number: 2B0400001C90C22A0002DD07FE8B022A
Manufacturer: Wacom, Inc.
Product: MSC Device
Release: 1256
Interface: 0
Usage Page: 1
Usage: 2
Device Found
type: 056a 0221
path: \\?\hid#vid_056a&pid_0221&mi_00&col02#8&1ea90857&0&0001#4d1e55b2-f16f-11cf-88cb-001111000030
serial_number: 2B0400001C90C22A0002DD07FE8B022A
Manufacturer: Wacom, Inc.
Product: MSC Device
Release: 1256
Interface: 0
Usage Page: 13
Usage: 2
(注意:OSX 只报告第二个条目!在 OSX 上没有问题!)
比较path
:
路径:\?\hid#vid_056a&pid_0221&mi_00&col01#8&1ea90857&0&0000#...
路径:\?\hid#vid_056a&pid_0221&mi_00&col02#8&1ea90857&0&0001#...
根据http://www.usb.org/developers/hidpage/Hut1_12v2.pdf,
UsagePage/Usage = 1/2 = 通用桌面控件/鼠标。 UsagePage/Usage = 13/2 = Digitizers/Pen。
(编辑:有时第一个路径是 1/2,第二个是 13/2,其他时候它被交换)。
而 HIDAPI 是 only taking the first one it finds。
所以看起来这应该是解决方案。 Inkling 暴露了 2 个“设备”,而 hidapi 使用了错误的(鼠标)设备,并且 Windows 不允许访问鼠标或键盘设备。
所以我调整了代码...
while (cur_dev)
if (cur_dev->vendor_id == vendor_id &&
cur_dev->product_id == product_id &&
cur_dev->usage_page == 13)
...要获得正确的条目,它应该可以正常工作吗?
不,CreateFileA 只是引发了一个不同的错误:
usage_page== 1 => 错误代码 5 (ERROR_ACCESS_DENIED) usage_page==13 => 错误代码 32 (ERROR_SHARING_VIOLATION)
嗯。这是相当令人不安的。我好像走投无路了!
我试过摆弄 CreateFileA 的参数,例如用STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE
替换GENERIC_READ | GENERIC_WRITE
——现在它愉快地创建了一个句柄。但是后续的hid_read
-s 没有收集到任何数据。
谷歌搜索,https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/af869f90-7bda-483d-ba2d-51680073fe9f/createfile-returns-invalid-handle-while-trying-to-access-hid-device-on-windows-8-desktop-app?forum=wdk 似乎包含一些建议的解决方法:
toaster 和 firefly 都可以在 HID 堆栈中工作。烤面包机展示了如何 通过原始 PDO 寻址过滤器,萤火虫展示了如何访问 它与 WMI。从 C 的角度来看,我认为原始 PDO 要简单得多 要编码,WMI 有点讨厌和复杂。
fireflytoaster
作者推荐的是toaster中的东西,但它是一个很大的CodeBase,我没有Windows驱动程序编程的经验。
看起来我必须挖掘很多非常陌生的领域才能让任何事情发挥作用,所以在开始之前我在这里问。如果没有人回答并且我想通了,我会回答我自己的问题。
我唯一能想到的另一件事是,也许另一个进程已经在使用这条路径。也许如果我可以终止这个过程, CreateFileA 可能会成功? Roel 的 libusb 方法涉及分离内核驱动程序:https://github.com/roelj/inklingreader/blob/master/src/usb/online-mode.c#L98
PS 我在某个地方读到,如果另一个进程已经打开了这个设备,我们的打开必须与之前打开的权限相匹配。而且我还读到 Windows 在检测到时会自动打开所有 HID 设备。
Find out which process has an exclusive lock on a USB device handle
PPS 也许一个想法是尝试替代 HID 库 What is the best usb library to communicate with usb HID devices on Windows?
PPPS 也许我需要以管理员身份运行我的代码。但这不是一个好的解决方案。
【问题讨论】:
Windows 内置了数字化器感知功能,您肯定正在为平板电脑的使用而战。您可用的旋钮是禁用服务(应命名为“平板电脑输入服务”或“触控键盘和手写面板服务”或“TabletServiceWacom”)或禁用设备管理器中的“Wacom 虚拟 HID 设备”。使用原始输入可能会更好。 【参考方案1】:我见过类似的行为。升级到 Windows 10 周年版后开始出现 ERROR_SHARING_VIOLATION 问题。只有在 Windows 启动时连接的 USB HID 设备才会出现此问题。如果在 Windows 启动后拔下并插入 USB 设备,则 CreateFile 成功。我还没有找到根本原因或解决方案。
【讨论】:
【参考方案2】:你是对的:如果其他应用程序已经打开了这个设备,就会出现ERROR_SHARING_VIOLATION
。您需要像这样调用CreateFileW API:
DWORD desired_access = GENERIC_WRITE | GENERIC_READ;
DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
::CreateFileW(deviceInterfacePath, desired_access, share_mode, 0, OPEN_EXISTING, 0, 0);
如果您不提供dwShareMode
,则表示您正在尝试以独占方式打开设备。如果其他应用程序(可能原生支持此类设备的新 Windows 版本)已经打开此设备供其使用,则可能会失败。
关于键盘和鼠标设备的注意事项:你也可以在不设置desired_access
(使用零值)的情况下调用::CreateFileW
:在这种情况下你可以使用HidD_GetManufacturerString
/HidD_GetProductString
/ HidD_GetSerialNumberString
/HidD_GetAttributes
(可能还有其他一些)带有返回句柄的 HID 方法。但是您无法将数据读/写到此类设备。如果您需要获取 HID 键盘/鼠标的名称或 VID/PID,这应该很有用。
这里是list of HID device types and their access modes on Windows。
【讨论】:
以上是关于CreateFile 无法在 Windows 中打开 HID 设备的主要内容,如果未能解决你的问题,请参考以下文章
win32 api CreateFile 和 WriteFile 问题
无法通过 CreateFile 打开设备,ERROR_INVALID_FUNCTION