如何通过SetupDiEnumDeviceInfo获取相机设备句柄或可用路径?
相机设备CreateFile句柄获取问题解决方案
问题描述
我想调用CreateFile()打开相机设备的句柄,但缺少设备路径。我了解可通过SetupDiEnumDeviceInterfaces配合SetupDiGetDeviceInterfaceDetail获取设备路径,但在获取相机设备的接口时遇到问题:使用键盘GUID调用SetupDiGetClassDevs可正常获取接口及关联路径,且该方式对多个GUID有效,但相机GUID却无法获取,似乎我的相机设备没有接口。请问是否可在SetupDiEnumDeviceInfo循环中直接获取设备路径?或者SetupDiGetDeviceRegistryProperty的哪个参数可传入CreateFile以获取有效句柄?
附原代码:
int getDeviceHandlesByClass(WCHAR* inputClassName) { HDEVINFO hDevInfoSet; SP_DEVINFO_DATA DeviceInfoData; DWORD i = 0; hDevInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES); if (hDevInfoSet == INVALID_HANDLE_VALUE) { std::cout << "Error on SetupDiGetClassDevs" << std::endl; exit(0); } DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); while (SetupDiEnumDeviceInfo(hDevInfoSet, i, &DeviceInfoData)) { i++; WCHAR deviceClassName[256]; if (!SetupDiGetDeviceRegistryProperty(hDevInfoSet, &DeviceInfoData, SPDRP_CLASS, NULL, (BYTE*)deviceClassName, sizeof(deviceClassName), NULL)) { return 1; } if (wcscmp(deviceClassName, inputClassName) == 0) { std::wcout << "PDO: " << deviceClassName << std::endl; } DWORD size; WCHAR instanceID[256]; if (SetupDiGetDeviceInstanceId(hDevInfoSet, &DeviceInfoData, instanceID, 256, &size)) { std::wcout << "INSTANCE ID: " << instanceID << std::endl; } HANDLE hFile = CreateFile(L"???WHAT SHALL I PASS HERE???", // file path GENERIC_READ, // desired access FILE_SHARE_READ, // share mode NULL, // security attributes OPEN_EXISTING, // creation disposition FILE_ATTRIBUTE_NORMAL, // file attributes NULL); // template file if (hFile == INVALID_HANDLE_VALUE) { // Handle error cout << GetLastError() << endl; } else { cout << "test" << endl; } // Close the file handle CloseHandle(hFile); } SetupDiDestroyDeviceInfoList(hDevInfoSet); }
解决方案
核心问题分析
你之前无法获取相机设备接口,大概率是用错了GUID。相机设备属于视频捕获设备类,标准GUID是GUID_DEVINTERFACE_VIDEO_CAPTURE(定义在ksmedia.h头文件中),而非普通的设备类GUID。
关键结论
- 无法在
SetupDiEnumDeviceInfo循环中直接获取可用于CreateFile的设备路径:SP_DEVINFO_DATA对应的是设备实例,不是可直接打开的设备接口,没有直接的路径属性可以用。 SetupDiGetDeviceRegistryProperty没有任何参数可以直接传给CreateFile:该函数仅获取设备的注册表属性(如类名、实例ID等),这些属性都不是可打开的设备路径。
正确实现步骤
要获取相机设备的可打开路径,必须通过设备接口枚举实现,步骤如下:
- 使用
GUID_DEVINTERFACE_VIDEO_CAPTURE调用SetupDiGetClassDevs,添加DIGCF_PRESENT | DIGCF_DEVICEINTERFACE标志,只枚举当前存在的设备接口。 - 循环调用
SetupDiEnumDeviceInterfaces枚举每个相机设备接口。 - 调用
SetupDiGetDeviceInterfaceDetail获取接口详情,其中包含的DevicePath就是可直接传给CreateFile的路径。
修改后的完整代码
#include <windows.h> #include <setupapi.h> #include <devguid.h> #include <ksmedia.h> #include <iostream> #pragma comment(lib, "setupapi.lib") int getCameraDeviceHandles() { HDEVINFO hDevInfoSet; SP_DEVICE_INTERFACE_DATA deviceInterfaceData; PSP_DEVICE_INTERFACE_DETAIL_DATA pDetailData = nullptr; DWORD i = 0; DWORD requiredSize = 0; // 枚举当前存在的视频捕获设备接口 hDevInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_VIDEO_CAPTURE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hDevInfoSet == INVALID_HANDLE_VALUE) { std::cout << "SetupDiGetClassDevs 失败,错误码:" << GetLastError() << std::endl; return 1; } deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); while (SetupDiEnumDeviceInterfaces(hDevInfoSet, NULL, &GUID_DEVINTERFACE_VIDEO_CAPTURE, i, &deviceInterfaceData)) { i++; // 先调用一次获取缓冲区所需大小 SetupDiGetDeviceInterfaceDetail(hDevInfoSet, &deviceInterfaceData, NULL, 0, &requiredSize, NULL); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { std::cout << "SetupDiGetDeviceInterfaceDetail 获取缓冲区大小失败,错误码:" << GetLastError() << std::endl; continue; } // 分配内存存储设备接口详情 pDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredSize); if (!pDetailData) { std::cout << "内存分配失败" << std::endl; continue; } pDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); // 获取包含设备路径的接口详情 if (SetupDiGetDeviceInterfaceDetail(hDevInfoSet, &deviceInterfaceData, pDetailData, requiredSize, NULL, NULL)) { std::wcout << "相机设备路径:" << pDetailData->DevicePath << std::endl; // 打开设备,相机通常需要读写权限及共享模式 HANDLE hFile = CreateFile(pDetailData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { std::cout << "设备打开成功" << std::endl; CloseHandle(hFile); } else { std::cout << "设备打开失败,错误码:" << GetLastError() << std::endl; } } else { std::cout << "SetupDiGetDeviceInterfaceDetail 失败,错误码:" << GetLastError() << std::endl; } LocalFree(pDetailData); pDetailData = nullptr; } SetupDiDestroyDeviceInfoList(hDevInfoSet); return 0; } int main() { getCameraDeviceHandles(); return 0; }
注意事项
- 必须包含
ksmedia.h才能使用GUID_DEVINTERFACE_VIDEO_CAPTURE。 CreateFile的参数需适配相机设备:通常需要GENERIC_READ | GENERIC_WRITE权限,共享模式设为FILE_SHARE_READ | FILE_SHARE_WRITE,避免独占设备导致其他程序无法访问。SetupDiGetDeviceInterfaceDetail需要先调用一次获取缓冲区大小,因为DevicePath的长度不固定,无法预先分配固定大小的数组。
内容的提问来源于stack exchange,提问作者anitarazafi
相关产品推荐
相关产品推荐

