You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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。

关键结论

  1. 无法在SetupDiEnumDeviceInfo循环中直接获取可用于CreateFile的设备路径:SP_DEVINFO_DATA对应的是设备实例,不是可直接打开的设备接口,没有直接的路径属性可以用。
  2. SetupDiGetDeviceRegistryProperty没有任何参数可以直接传给CreateFile:该函数仅获取设备的注册表属性(如类名、实例ID等),这些属性都不是可打开的设备路径。

正确实现步骤

要获取相机设备的可打开路径,必须通过设备接口枚举实现,步骤如下:

  1. 使用GUID_DEVINTERFACE_VIDEO_CAPTURE调用SetupDiGetClassDevs,添加DIGCF_PRESENT | DIGCF_DEVICEINTERFACE标志,只枚举当前存在的设备接口。
  2. 循环调用SetupDiEnumDeviceInterfaces枚举每个相机设备接口。
  3. 调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 18:35:06