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

如何用C++和SetupAPI获取Windows当前活动显卡驱动版本

获取Windows当前活动显卡驱动版本的正确姿势(C++ + SetupAPI)

你碰到的问题其实很典型——用SPDIT_COMPATDRIVER枚举到的是所有兼容该GPU的驱动包,而不是当前系统正在使用的那一个。要拿到活动驱动版本,得换个方向:从已安装并正在运行的显示设备实例入手,直接读取它绑定的驱动信息。

核心思路

  1. 枚举当前系统中存在的显示设备实例(过滤掉未连接的设备)
  2. 对每个设备实例,获取其绑定的驱动服务信息
  3. 从驱动服务的注册表项或对应驱动文件中读取版本号

代码实现示例

#include <windows.h>
#include <setupapi.h>
#include <devguid.h>
#include <initguid.h>
#include <tchar.h>
#include <iostream>

#pragma comment(lib, "setupapi.lib")

void GetActiveDisplayDriverVersion() {
    // 获取当前系统中存在的显示类设备
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_DISPLAY, nullptr, nullptr, DIGCF_PRESENT | DIGCF_PROFILE);
    if (hDevInfo == INVALID_HANDLE_VALUE) {
        std::cerr << "SetupDiGetClassDevs failed: " << GetLastError() << std::endl;
        return;
    }

    SP_DEVINFO_DATA devInfoData = {0};
    devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

    // 遍历每个显示设备实例
    for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); i++) {
        // 获取设备绑定的驱动服务名
        TCHAR szServiceName[MAX_PATH] = {0};
        if (!SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_SERVICE, nullptr, (PBYTE)szServiceName, sizeof(szServiceName), nullptr)) {
            std::cerr << "SetupDiGetDeviceRegistryProperty (Service) failed: " << GetLastError() << std::endl;
            continue;
        }

        // 打开服务控制管理器
        SC_HANDLE hSCM = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
        if (!hSCM) {
            std::cerr << "OpenSCManager failed: " << GetLastError() << std::endl;
            continue;
        }

        // 打开驱动服务
        SC_HANDLE hService = OpenService(hSCM, szServiceName, SERVICE_QUERY_CONFIG);
        if (!hService) {
            std::cerr << "OpenService failed: " << GetLastError() << std::endl;
            CloseServiceHandle(hSCM);
            continue;
        }

        // 获取驱动文件路径
        DWORD dwBytesNeeded = 0;
        QueryServiceConfig(hService, nullptr, 0, &dwBytesNeeded);
        LPQUERY_SERVICE_CONFIG pConfig = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, dwBytesNeeded);
        if (QueryServiceConfig(hService, pConfig, dwBytesNeeded, &dwBytesNeeded)) {
            TCHAR szDriverPath[MAX_PATH] = {0};
            _tcscpy_s(szDriverPath, MAX_PATH, pConfig->lpBinaryPathName);
            
            // 移除路径中的引号(如果存在)
            if (szDriverPath[0] == _T('"')) {
                _tcscpy_s(szDriverPath, MAX_PATH, szDriverPath + 1);
                szDriverPath[_tcslen(szDriverPath) - 1] = _T('\0');
            }

            // 读取驱动文件的版本信息
            DWORD dwHandle = 0;
            DWORD dwSize = GetFileVersionInfoSize(szDriverPath, &dwHandle);
            if (dwSize > 0) {
                LPVOID pVersionInfo = LocalAlloc(LPTR, dwSize);
                if (GetFileVersionInfo(szDriverPath, dwHandle, dwSize, pVersionInfo)) {
                    VS_FIXEDFILEINFO* pFixedInfo = nullptr;
                    UINT uLen = 0;
                    if (VerQueryValue(pVersionInfo, _T("\\"), (LPVOID*)&pFixedInfo, &uLen)) {
                        DWORD dwFileVersionMS = pFixedInfo->dwFileVersionMS;
                        DWORD dwFileVersionLS = pFixedInfo->dwFileVersionLS;
                        TCHAR szVersion[MAX_PATH] = {0};
                        _stprintf_s(szVersion, MAX_PATH, _T("%u.%u.%u.%u"), 
                            HIWORD(dwFileVersionMS), LOWORD(dwFileVersionMS), 
                            HIWORD(dwFileVersionLS), LOWORD(dwFileVersionLS));
                        std::wcout << L"Active Display Driver Version: " << szVersion << std::endl;
                    }
                }
                LocalFree(pVersionInfo);
            }
        }
        LocalFree(pConfig);
        CloseServiceHandle(hService);
        CloseServiceHandle(hSCM);
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);
}

int main() {
    GetActiveDisplayDriverVersion();
    return 0;
}

关键细节说明

  • DIGCF_PRESENT | DIGCF_PROFILE:这两个参数确保只枚举当前系统中已连接、且属于硬件配置文件的设备,过滤掉未使用的虚拟设备或离线设备。
  • SPDRP_SERVICE:通过这个属性可以直接拿到设备绑定的驱动服务名,每个活动设备对应的驱动服务都是正在运行的,指向的就是当前使用的驱动文件。
  • 文件版本读取:驱动服务的二进制路径对应的文件就是当前正在加载的驱动,读取它的版本信息比读取注册表更可靠,因为版本号直接嵌入在驱动文件中,不会被篡改。

另外,你也可以直接从设备的注册表路径读取DriverVersion值(路径为HKLM\SYSTEM\CurrentControlSet\Enum\<设备实例路径>\Device Parameters\DriverVersion),但通过驱动文件读取版本的方式兼容性更好。

内容的提问来源于stack exchange,提问作者Christoph

火山引擎 最新活动