You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在C#中实现GPU厂商ID与设备ID到名称的解析,及获取PNPDeviceID的方案咨询

在C#中实现GPU厂商ID与设备ID到名称的解析,及获取PNPDeviceID的方案咨询

我来帮你一步步解决这些问题,结合你现有的Vortice.DXGI代码,给出可直接落地的实现方案:


一、厂商ID解析:固定字典映射最可靠

PCI厂商ID是全球统一分配的固定值,不需要依赖任何外部API或网络,直接用本地字典映射是最高效的方案。

实现代码

// 全局维护PCI厂商ID与名称的映射字典
private static readonly Dictionary<uint, string> _vendorIdMap = new()
{
    { 0x8086, "Intel Corporation" },
    { 0x10DE, "NVIDIA Corporation" },
    { 0x1002, "Advanced Micro Devices, Inc. (AMD/ATI)" },
    { 0x1022, "Advanced Micro Devices, Inc. (AMD)" },
    { 0x13B5, "ARM Limited" },
    { 0x1414, "Microsoft Corporation" },
    { 0x163C, "Qualcomm Technologies, Inc." },
    { 0x1912, "Radeon Technologies Group" },
    // 可根据需要补充更多小众厂商ID,参考PCI-SIG官方列表
};

public static string ResolveVendorIdToVendorName(uint vendorId)
{
    // 注意:VendorId的十进制值就是十六进制ID的十进制表示(比如0x8086的十进制是32902)
    if (_vendorIdMap.TryGetValue(vendorId, out var vendorName))
        return vendorName;
    
    // 未知厂商返回十六进制格式的ID
    return $"Unknown Vendor (0x{vendorId:X4})";
}

二、设备ID解析:依赖Windows系统API获取真实名称

设备ID是各厂商自行分配的,没有全局统一的固定字典,最可靠的方式是从Windows系统的设备数据库中读取对应名称。这里我们用Windows原生的SetupAPI来枚举显示适配器设备,匹配厂商ID和设备ID后获取设备名称或PNPDeviceID。

第一步:封装SetupAPI调用的辅助类

这个类负责和Windows底层API交互,获取设备的详细信息:

using System.Runtime.InteropServices;
using System.Text;

public static class SetupApiHelper
{
    private const uint DIGCF_PRESENT = 0x00000002; // 仅枚举当前系统中存在的设备
    // 显示适配器设备的类GUID(固定值)
    private static readonly Guid _displayAdapterClassGuid = new Guid("4d36e968-e325-11ce-bfc1-08002be10318");

    [StructLayout(LayoutKind.Sequential)]
    private struct SP_DEVINFO_DATA
    {
        public uint cbSize;
        public Guid ClassGuid;
        public uint DevInst;
        public IntPtr Reserved;
    }

    [DllImport("setupapi.dll", SetLastError = true)]
    private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, uint Enumerator, IntPtr hwndParent, uint Flags);

    [DllImport("setupapi.dll", SetLastError = true)]
    private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, StringBuilder DeviceInstanceId, uint DeviceInstanceIdSize, out uint RequiredSize);

    [DllImport("setupapi.dll", SetLastError = true)]
    private static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

    [DllImport("setupapi.dll", SetLastError = true)]
    private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, uint Property, out uint PropertyRegDataType, IntPtr PropertyBuffer, uint PropertyBufferSize, out uint RequiredSize);

    private const uint SPDRP_FRIENDLYNAME = 0x0000000C; // 设备友好名称(比如"NVIDIA GeForce RTX 3060")
    private const uint SPDRP_DEVICEDESC = 0x00000000; // 设备描述(备用)
    private const uint SPDRP_HARDWAREID = 0x00000001; // 设备硬件ID(用于匹配VendorId和DeviceId)

    /// <summary>
    /// 根据厂商ID和设备ID获取GPU的友好名称
    /// </summary>
    public static string GetGpuFriendlyName(uint vendorId, uint deviceId)
    {
        string vendorHex = $"PCI\\VEN_{vendorId:X4}";
        string deviceHex = $"DEV_{deviceId:X4}";
        IntPtr deviceInfoSet = SetupDiGetClassDevs(ref _displayAdapterClassGuid, 0, IntPtr.Zero, DIGCF_PRESENT);

        if (deviceInfoSet == IntPtr.Zero)
            return "Unknown Device";

        try
        {
            SP_DEVINFO_DATA devInfo = new SP_DEVINFO_DATA { cbSize = (uint)Marshal.SizeOf<SP_DEVINFO_DATA>() };
            for (uint i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, ref devInfo); i++)
            {
                string hardwareId = GetRegistryProperty(deviceInfoSet, ref devInfo, SPDRP_HARDWAREID);
                if (hardwareId != null && hardwareId.Contains(vendorHex) && hardwareId.Contains(deviceHex))
                {
                    // 优先取友好名称,没有则 fallback 到设备描述
                    string friendlyName = GetRegistryProperty(deviceInfoSet, ref devInfo, SPDRP_FRIENDLYNAME);
                    return friendlyName ?? GetRegistryProperty(deviceInfoSet, ref devInfo, SPDRP_DEVICEDESC) ?? "Unknown Device";
                }
            }
            return "Unknown Device";
        }
        finally
        {
            SetupDiDestroyDeviceInfoList(deviceInfoSet);
        }
    }

    /// <summary>
    /// 根据厂商ID和设备ID获取GPU的PNPDeviceID
    /// </summary>
    public static string GetGpuPnpDeviceId(uint vendorId, uint deviceId)
    {
        string vendorHex = $"PCI\\VEN_{vendorId:X4}";
        string deviceHex = $"DEV_{deviceId:X4}";
        IntPtr deviceInfoSet = SetupDiGetClassDevs(ref _displayAdapterClassGuid, 0, IntPtr.Zero, DIGCF_PRESENT);

        if (deviceInfoSet == IntPtr.Zero)
            return string.Empty;

        try
        {
            SP_DEVINFO_DATA devInfo = new SP_DEVINFO_DATA { cbSize = (uint)Marshal.SizeOf<SP_DEVINFO_DATA>() };
            for (uint i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, ref devInfo); i++)
            {
                string hardwareId = GetRegistryProperty(deviceInfoSet, ref devInfo, SPDRP_HARDWAREID);
                if (hardwareId != null && hardwareId.Contains(vendorHex) && hardwareId.Contains(deviceHex))
                {
                    StringBuilder pnpIdBuilder = new StringBuilder(256);
                    if (SetupDiGetDeviceInstanceId(deviceInfoSet, ref devInfo, pnpIdBuilder, (uint)pnpIdBuilder.Capacity, out _))
                    {
                        return pnpIdBuilder.ToString();
                    }
                }
            }
            return string.Empty;
        }
        finally
        {
            SetupDiDestroyDeviceInfoList(deviceInfoSet);
        }
    }

    // 辅助方法:读取设备注册表属性值
    private static string GetRegistryProperty(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA devInfo, uint property)
    {
        if (!SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfo, property, out _, IntPtr.Zero, 0, out uint requiredSize) &&
            Marshal.GetLastWin32Error() != 0xEA) // 忽略缓冲区不足的错误,这是正常流程
        {
            return null;
        }

        IntPtr buffer = Marshal.AllocHGlobal((int)requiredSize);
        try
        {
            if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfo, property, out _, buffer, requiredSize, out _))
            {
                return Marshal.PtrToStringAuto(buffer);
            }
            return null;
        }
        finally
        {
            Marshal.FreeHGlobal(buffer);
        }
    }
}

第二步:实现设备ID解析方法

注意:单独的设备ID没有全局唯一性,必须结合厂商ID才能准确匹配设备,所以我们修改方法签名同时传入两个ID:

public static string ResolveDeviceIdToDeviceName(uint vendorId, uint deviceId)
{
    string deviceName = SetupApiHelper.GetGpuFriendlyName(vendorId, deviceId);
    return string.IsNullOrEmpty(deviceName) ? $"Unknown Device (0x{deviceId:X4})" : deviceName;
}

三、更新主程序调用逻辑

修改你原有的Main方法,更新设备ID解析的调用,同时添加PNPDeviceID的输出:

// ... 原有代码 ...
Console.WriteLine($"GPU {i}: {desc.Description}");
Console.WriteLine($"  Vendor ID: {desc.VendorId}");
Console.WriteLine($"  Vendor ID Decoded: {ResolveVendorIdToVendorName(desc.VendorId)}");
Console.WriteLine($"  Device ID: {desc.DeviceId}");
// 传入厂商ID和设备ID进行解析
Console.WriteLine($"  Device ID Decoded: {ResolveDeviceIdToDeviceName(desc.VendorId, desc.DeviceId)}");
// 添加PNPDeviceID输出
string pnpDeviceId = SetupApiHelper.GetGpuPnpDeviceId(desc.VendorId, desc.DeviceId);
Console.WriteLine($"  PNP Device ID: {pnpDeviceId}");
Console.WriteLine($"  Dedicated Video Memory: {desc.DedicatedVideoMemory / (1024 * 1024)} MB");
// ... 原有代码 ...

四、注意事项

  1. 平台限制:上述代码依赖Windows的SetupAPI和DXGI,仅能在Windows系统上运行,和你使用Vortice.DXGI的场景完全匹配。
  2. 权限问题:普通用户权限即可读取大部分显示适配器信息,若遇到部分设备无法识别,可尝试以管理员权限运行程序。
  3. 厂商ID扩展:如果需要支持更多小众厂商,可以从PCI-SIG官方的厂商ID列表中补充字典内容。
  4. 设备ID的唯一性:单独的设备ID没有全局唯一性,必须结合厂商ID才能准确匹配到对应的GPU设备,这也是我们的解析方法需要同时传入两个参数的原因。

火山引擎 最新活动