如何用C++和SetupAPI获取Windows当前活动显卡驱动版本
获取Windows当前活动显卡驱动版本的正确姿势(C++ + SetupAPI)
你碰到的问题其实很典型——用SPDIT_COMPATDRIVER枚举到的是所有兼容该GPU的驱动包,而不是当前系统正在使用的那一个。要拿到活动驱动版本,得换个方向:从已安装并正在运行的显示设备实例入手,直接读取它绑定的驱动信息。
核心思路
- 枚举当前系统中存在的显示设备实例(过滤掉未连接的设备)
- 对每个设备实例,获取其绑定的驱动服务信息
- 从驱动服务的注册表项或对应驱动文件中读取版本号
代码实现示例
#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




