The implementation code of obtaining driver information according to HardwareID in C

  • 2021-11-13 02:32:02
  • OfStack

Recently, we need to obtain the driver information of the device according to the HardwareID of the device, such as the driver version. After groping, we got two different solutions, each of which has its own merits, and wrote it out for everyone to share.

1 Use the Win32_PnPSignedDriver class in WMI

More information on Win32_PnPSignedDriver: http://msdn2.microsoft. com/en-us/library/aa394354. aspx
Using WMI (Windows Management Instrumentation) is the most convenient method. We can get the DriverVersion we need according to the following program fragment.


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 searcher.Get())
  {
    object tempID = mo["HardwareID"];
    if( tempID!=null && tempID.ToString().ToUpper() == hardwareID.Trim().ToUpper() )
    {
      return mo["DriverVersion"].ToString();
    }
  }

  return "UnknownVersion";
}

This way to get the driver is very simple, but there is a very serious problem is the efficiency problem. On average, it takes about 3 seconds to get an DriverVersion for every query executed. For our application, this time is unacceptable. Maybe you will say, why not use more qualifiers to go one step further and reduce the number of queries?

If we change the connection string to:


string queryString = "SELECT HardwareID, DriverVersion FROM Win32_PnPSignedDriver WHERE HardwareID='somehardware'";

The efficiency of the program has not improved significantly. Moreover, a problem is found. If our somehardware contains a '\' (that is, HardwareID = 'some\\ hardware'), then 1 will definitely get an "Invalid Query" exception. However, it is normal to inquire in WMITOOLS, and I hope Daren will come out and give directions. 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 times, this method will not work.

2 Using PInvoke

Because you can't use WMI, you think of calling Windows API using PInvoke. By querying MSDN, we know that we can use SetupDixxxx to implement our functions. The basic ideas are as follows:
(1) Use the function SetupDiGetClassDevs to get a class containing all device information.
(2) Using SetupDiEnumDeviceInfo, the information of a specific device is obtained and stored in a structure named SP_DEVINFO_DATA.
(3) The HardwareID of the device is obtained by SetupDiGetDeviceRegistryProperty, and compared with the input HardwareID
(4) If the two HardwareID are identical, then use SetupDiBuildDriverInfoList to get the driver information list of this device
(5) Using SetupDiEnumDriverInfo to traverse the driver information list, get all the required information and save it in a structure named SP_DRVINFO_DATA
(6) The driver version is available from SP_DRVINFO_DATA. Is a number of type DWORDLONG, which needs to be converted to the structure of x. x. x. x

It should be noted that the above functions are encapsulated in setupapi. dll, and 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 type correspondence and structure alignment. For example, the SP_DEVINFO_DATA structure above needs to be declared as follows


[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
public struct SP_DEVINFO_DATA
{
   public int cbSize;
   public Guid ClassGuid;
   public IntPtr DevInst;
   public IntPtr Reserved;
}

Note LayoutKind. Sequential, Pack = 4, and public IntPtr Reserved. If it is not declared as this, the call cannot succeed.
SP_DRVINFO_DATA can also be declared in the same way as 1.


[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
public struct SP_DRVINFO_DATA
{
  public int cbSize;
  public int DriverType;
  public IntPtr Reserved;
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
  public string Description;
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
  public string MfgName;
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
  public string ProviderName;
  public FILETIME DriverDate;
  public ulong DriverVersion;
}

For the final version of DWORDLONG to x. x. x. x, you can convert as follows. DWORDLONG is an 8-byte unsigned integer, and x in x. x. x. x is an unsigned integer from 0 to 65536, accounting for 2 bytes. Therefore, you can directly divide an 8-byte integer into 4 2-byte integers, and finally add up to the version number. Assuming version version = 1407379348914176, converting version to binary numbers is:
101 00000000 00000001 00001010 00101000 00000000 00000000
--- --------------------- ---------------------- ---------------------
5 1 2600 0
Therefore, you can get a version of 5.1. 2600.0.

You can use the following example function to get version information


//version = 1407379348914176 The converted version is 5.1.2600.0
private 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;
    sb.Append( temp.ToString() + "." );
  }
  
  return sb.ToString();
}

By calling API, the speed has been greatly improved, and one query can be completed within one second. It is also suitable for Win2000, Win, XP, Win2003 and Vista.


Related articles: