概述
刚换了工作,入职时候大佬让我研究下 驱动人生/驱动精灵 判断驱动是否正常是怎么做到的
一开始是一脸懵逼的,完全不知道要如何下手,不过应该是获取设备管理器里硬件属性,然后进行判断吧,我就跟据我的猜测进行研究。
第一个要解决的问题就是要如何获得设备管理器上的信息
在MSDN游荡了很久,发现了
CMAPI
CONFIGRET
WINAPI CM_Enumerate_Classes(
_In_ ULONG ulClassIndex,
_Out_ LPGUID ClassGuid,
_In_ ULONG ulFlags
);
The CM_Enumerate_Classes function, when called repeatedly, enumerates the local machine’s installed device classes by supplying each class’s GUID.
这函数的功能就是枚举本地机器上安装的每个设备类的GUID。(我的理解:每个设备类里面就有很多具体的设备,就类似一棵树的根,下面会有很多子节点。至于归类方式,微软说是以设备的安装方式来归类的,具体参看MSDN:Device Classes)
获得了设备类的GUID,那应该有遍历这个设备类里每个设备的函数,我就继续在MSDN游荡。。。终于发现了另一个函数,
HDEVINFO SetupDiGetClassDevs(
_In_opt_ const GUID *ClassGuid,
_In_opt_ PCTSTR Enumerator,
_In_opt_ HWND hwndParent,
_In_ DWORD Flags
);
The SetupDiGetClassDevs function returns a handle to a device information set that contains requested device information elements for a local computer.
这个函数的功能就是能返回包含本地计算机上相应请求的信息。(我刚开始并不知道这个鬼玩意能干啥,跟上一个CM_*函数也不一样,就看到第一个参数是GUID,就抱着试一试的态度用了下。)
在该函数的MSDN文档中,有示例用法,提到了另一个函数:
BOOL SetupDiEnumDeviceInfo(
_In_ HDEVINFO DeviceInfoSet,
_In_ DWORD MemberIndex,
_Out_ PSP_DEVINFO_DATA DeviceInfoData
);
The SetupDiEnumDeviceInfo function returns a SP_DEVINFO_DATA structure that specifies a device information element in a device information set.
看样子就是一个用来遍历的函数。这个函数返回指定信息集中指定元素的信息。感觉看到了希望。。。
然后又找到一个函数:
BOOL SetupDiGetDeviceRegistryProperty(
_In_ HDEVINFO DeviceInfoSet,
_In_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ DWORD Property,
_Out_opt_ PDWORD PropertyRegDataType,
_Out_opt_ PBYTE PropertyBuffer,
_In_ DWORD PropertyBufferSize,
_Out_opt_ PDWORD RequiredSize
);
The SetupDiGetDeviceRegistryProperty function retrieves a specified Plug and Play device property.
这个函数负责检索指定即插即用设备示例属性。(有的设备并不是即插即用,所以估计会有问题,有一个函数SetupDiGetDeviceProperty 是获得设备的实例属性,但是参数研究了半天也不知道怎么填。)
这个函数的第三个参数设置了要获取的内容:
我就用了SPDRP_DEVICEDESC 和SPDRP_HARDWAREID 分别是获得设备名称和设备的硬件ID。
##找到了具体的硬件,接下来是找对应的驱动
在获取驱动之前,需要先用SetupDiBuildDriverInfoList 创建驱动信息列表。
BOOL SetupDiBuildDriverInfoList(
_In_ HDEVINFO DeviceInfoSet,
_Inout_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ DWORD DriverType
);
The SetupDiBuildDriverInfoList function builds a list of drivers that is associated with a specific device or with the global class driver list for a device information set.
这个函数负责收集对应设备类的驱动,前两个参数起到确定硬件的作用,第一个参数是硬件信息集,是SetupDiGetClassDevs 函数返回的,就是确定是哪一类设备;第二个参数是设备信息,这个参数是SetupDiEnumDeviceInfo 返回的,确定具体是哪一个设备;第三个设备则是确定要生成的驱动信息列表是要对应设备类的所有驱动还是对应设备类中某个设备的驱动。
收集完驱动信息后就可以遍历驱动信息了:
BOOL SetupDiEnumDriverInfo(
_In_ HDEVINFO DeviceInfoSet,
_In_opt_ PSP_DEVINFO_DATA DeviceInfoData,
_In_ DWORD DriverType,
_In_ DWORD MemberIndex,
_Out_ PSP_DRVINFO_DATA_W DriverInfoData
);
The SetupDiEnumDriverInfo function enumerates the members of a driver list.
这个函数负责遍历SetupDiBuildDriverInfoList 生成的驱动列表。
##由此,获得对应设备的驱动信息的功能已经实现
Windows中获取驱动的流程基本是这样:首先拿到设备类,然后遍历设备类中的设备(在本机上安装的设备),然后获取对应设备的驱动程序。
驱动人生驱动精灵 识别硬件并确定驱动是否最优我猜应该是有一个自己的数据库,根据硬件的硬件ID进行匹配。
#如果我哪里分析得不对希望大佬留言指正。
Demo(VS2015上编译通过):
#include <Windows.h>
#include <Cfgmgr32.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <SetupAPI.h>
#include <tchar.h>
#pragma comment(lib, "Cfgmgr32.lib")
#pragma comment(lib, "Setupapi.lib")
using namespace std;
void printGUID(GUID& guid);
TCHAR* FindVersionFromULONGLONG(ULONGLONG data);
int main()
{
GUID guid = { 0 };
DWORD dwRet = 0;
ULONG guidIndex = 0;
setlocale(LC_ALL, "chs");//让程序支持UTF-16中文的输出
while (true)
{
dwRet = CM_Enumerate_Classes(guidIndex++, &guid, 0);
if (CR_NO_SUCH_VALUE == dwRet)
{
cout << "设备类枚举完成" << endl;
break;
}
printGUID(guid);
HDEVINFO hdevInfo = 0;
hdevInfo = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT);
if (INVALID_HANDLE_VALUE == hdevInfo)
{
cout << "SetupDiGetClassDevs failed" << endl;
break;
}
DWORD deviceIndex = 0;
SP_DEVINFO_DATA devInfoData = { sizeof(SP_DEVINFO_DATA) };
for (deviceIndex = 0; SetupDiEnumDeviceInfo(hdevInfo, deviceIndex, &devInfoData);++deviceIndex)
{
_tprintf(L"-------------------------------------------------------------n现有设备:n");
ULONG buffSize = 0;
if (CR_SUCCESS != CM_Get_Device_ID_Size(&buffSize, devInfoData.DevInst, 0))
{
break;
}
TCHAR* buff = new TCHAR[buffSize + 1];
dwRet = CM_Get_Device_ID(devInfoData.DevInst, buff, buffSize, 0);
if (CR_SUCCESS != dwRet)
{
break;
}
buff[buffSize] = TEXT('