Recently, at work, I need to obtain the device's driver information based on the device's HardwareID, such as the driver version. After exploration, we have obtained two different solutions. Both methods have their own advantages and written them to share with you.
1 Use Win32_PnPSignedDriver class in WMI
Details of Win32_PnPSignedDriver:/en-us/library/
Using WMI (Windows Management Instrumentation) is the most convenient method. You can obtain the DriverVersion we need based on the program snippet below.
private string GetDriverVersion( string hardwareID ) { string queryString = "SELECT HardwareID, DriverVersion FROM Win32_PnPSignedDriver"; SelectQuery selectQuery = new SelectQuery( queryString ); ManagementObjectSearcher searcher = new ManagementObjectSearcher(selectQuery); foreach (ManagementObject mo in ()) { object tempID = mo["HardwareID"]; if( tempID!=null && ().ToUpper() == ().ToUpper() ) { return mo["DriverVersion"].ToString(); } } return "UnknownVersion"; }
This way of obtaining drivers is very concise, but one very serious problem is efficiency. On average, it takes about 3 seconds to get a DriverVersion for every query performed. For our applications, this time is unacceptable. Maybe you would say, why not use more qualifying symbols to further reduce the number of queries?
If we change the connection string to:
string queryString = "SELECT HardwareID, DriverVersion FROM Win32_PnPSignedDriver WHERE HardwareID='somehardware'";
There is no significant improvement in the efficiency of the program. And we also found a problem. If we have a '\' in somehardware (that is, HardwareID='some\\hardware'), we will definitely get a "Invalid Query" exception. However, it is normal to query in WMITOOLS. I hope experts can come out and give me some advice. Finally, according to the description of MSDN, only Windows Vista, Windows XP and Windows 2003 support this class. Since our program needs to run at 2000, this method does not work.
2 Using PInvoke
Since WMI cannot be used, I thought of using PInvoke to call the Windows API. By querying MSDN, we know that we can use the function SetupDixxxx to implement our functions. The basic ideas are as follows:
(1) Use SetupDiGetClassDevs function to get a class containing all device information.
(2) Use SetupDiEnumDeviceInfo to obtain the information of a specific device and save it in a structure called SP_DEVINFO_DATA.
(3) Use SetupDiGetDeviceRegistryProperty to obtain the HardwareID of the device and compare it with the input HardwareID
(4) If the two HardwareIDs are the same, then use SetupDiBuildDriverInfoList to get the driver information list of this device
(5) Use SetupDiEnumDriverInfo to traverse the driver information list, get all the required information, and save it in a structure called SP_DRVINFO_DATA
(6) You can get the driver version from SP_DRVINFO_DATA. It is a DWORDLONG type number that needs to be converted into a structure
It is worth noting that the above functions are all encapsulated. To use these functions, you need to install Windows DDK.
In C#, when we use pInvoke to call Windows API, we need to pay attention to the type correspondence and structure alignment. For example, the above SP_DEVINFO_DATA structure needs to be declared as follows
[StructLayout(, Pack = 4, CharSet = )] public struct SP_DEVINFO_DATA { public int cbSize; public Guid ClassGuid; public IntPtr DevInst; public IntPtr Reserved; }
Note that Pack = 4 and public IntPtr Reserved. If the declaration is not as stated, the call cannot be successful.
SP_DRVINFO_DATA can also be declared in the same way.
[StructLayout(, Pack = 4, CharSet = )] public struct SP_DRVINFO_DATA { public int cbSize; public int DriverType; public IntPtr Reserved; [MarshalAs(, SizeConst = 256)] public string Description; [MarshalAs(, SizeConst = 256)] public string MfgName; [MarshalAs(, SizeConst = 256)] public string ProviderName; public FILETIME DriverDate; public ulong DriverVersion; }
For the last version converted from DWORDLONG, you can convert it in the following way. DWORDLONG is an 8-byte unsigned integer, and x in it is an unsigned integer from 0 to 65536, accounting for 2 bytes. Therefore, the 8-byte integer can be directly divided into 4 2-byte integers, and finally combined to be the version number. Assuming version version = 1407379348914176, convert version into a binary number as:
101 00000000 00000001 00001010 00101000 00000000 00000000
--- --------------------- ---------------------- ---------------------
5 1 2600 0
Therefore, the version can be obtained is 5.1.2600.0.
You can use the following example function to obtain version information
//version = 1407379348914176, the converted version is 5.1.2600.0private string GetVersionFromLong( ulong version ) { ulong baseNumber = 0xFFFF; StringBuilder sb = new StringBuilder(); ulong temp = 0L; for( int offset = 48; offset >= 0; offset -= 16 ) { temp = (version >> offset) & baseNumber; ( () + "." ); } return (); }
By calling the API, the speed has been greatly improved, and a query can be completed within 1 second. And suitable for Win2000, Win XP, Win2003 and Vista.