剛換了工作,入職時候大佬讓我研究下 驅動人生/驅動精靈 判斷驅動是否正常是怎麼做到的
一開始是一臉懵逼的,完全不知道要如何下手,不過應該是獲取設備管理器裏硬件屬性,然後進行判斷吧,我就跟據我的猜測進行研究。
第一個要解決的問題就是要如何獲得設備管理器上的信息
在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('\0');
_tprintf(TEXT("Device ID:%s\n"), buff);
delete[]buff;
DWORD dwPropertyRegDataType, dwSize;
wchar_t propertyBuffer[4096];
if (SetupDiGetDeviceRegistryProperty(hdevInfo, &devInfoData, SPDRP_DEVICEDESC,
&dwPropertyRegDataType, (PBYTE)propertyBuffer, sizeof(propertyBuffer), &dwSize))
{
_tprintf(L"Device Name:%s\n", propertyBuffer);
}
if (SetupDiGetDeviceRegistryProperty(hdevInfo, &devInfoData, SPDRP_HARDWAREID,
&dwPropertyRegDataType, (PBYTE)propertyBuffer, sizeof(propertyBuffer), &dwSize))
{
_tprintf(L"硬件ID:\n");
wchar_t* p = propertyBuffer;
for (; *p != TEXT('\0') && p + dwSize / sizeof(char) <= propertyBuffer + ARRAYSIZE(propertyBuffer); p += lstrlen(p) + 1)
{
_tprintf(L"%s\n", p);
}
}
else
{
continue;
}
_tprintf(L"\n兼容ID:\n");
if (SetupDiGetDeviceRegistryProperty(hdevInfo, &devInfoData, SPDRP_COMPATIBLEIDS,
&dwPropertyRegDataType, (PBYTE)propertyBuffer, sizeof(propertyBuffer), &dwSize))
{
wchar_t* p = propertyBuffer;
for (; *p != TEXT('\0') && p + dwSize / sizeof(char) <= propertyBuffer + ARRAYSIZE(propertyBuffer); p += lstrlen(p) + 1)
{
_tprintf(TEXT("%s\n"), p);
}
}
_tprintf(TEXT("\n對應驅動:\n"));
//遍歷驅動
if (SetupDiBuildDriverInfoList(hdevInfo, &devInfoData, SPDIT_COMPATDRIVER))
{
SP_DRVINFO_DATA driverInfoData = { sizeof(SP_DRVINFO_DATA) };
int i = 0;
for (; SetupDiEnumDriverInfo(hdevInfo, &devInfoData, SPDIT_COMPATDRIVER, i, &driverInfoData); ++i);
--i;
_tprintf(TEXT("設備名稱:%s\n"), driverInfoData.Description);
_tprintf(TEXT("驅動程序提供商:%s\n"), driverInfoData.ProviderName);
_tprintf(TEXT("設備製造商:%s\n"), driverInfoData.MfgName);
_tprintf(TEXT("驅動程序版本:%s\n"), FindVersionFromULONGLONG(driverInfoData.DriverVersion));
SYSTEMTIME sysTime;
FileTimeToSystemTime(&driverInfoData.DriverDate, &sysTime);
_tprintf(TEXT("驅動程序日期:%d/%d/%d\n"),
sysTime.wYear, sysTime.wMonth, sysTime.wDay);
SetupDiDestroyDriverInfoList(hdevInfo, &devInfoData, SPDIT_COMPATDRIVER);
}
else
{
_tprintf(TEXT("\n沒找到對應驅動程序\n"));
}
_tprintf(TEXT("-------------------------------------------------------------\n"));
}
}
system("pause");
return 0;
}
void printGUID(GUID& guid)
{
_tprintf(TEXT("GUID:%8X %4X %4X %2X %2X %2X %2X %2X %2X %2X %2X\n"),
guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
}
TCHAR* FindVersionFromULONGLONG(ULONGLONG data)
{
static TCHAR buff[64];
memset(buff, 0, sizeof(buff));
ULONG temp = 0;
TCHAR numbuff[8];
for (int i = 48; i >= 0; i -= 16)
{
temp = (data >> i) & 0xFFFF;
_ultot_s(temp, numbuff, 10);
_tcscat_s(buff, numbuff);
_tcscat_s(buff, TEXT("."));
}
buff[_tcslen(buff) - 1] = 0;
return buff;
}