In C# or VB.NET, I would like to retrieve a list of third-party installed drivers. Only the third-party drivers.
I found these WMI classes from which in combination I think I could retrieve all the installed drivers and useful information including the directory path:
NOTE: I think that at least .net 5.0 (version 7.1.7 of the related PowerShell Nuget packages) is required to run this. I tried it in .NET Framework 4.8 with Microsoft.PowerShell.5.ReferenceAssemblies
package and it was unable to find the Get-WindowsDriver cmdlet.
Also take into account that this was my very first time using these PowerShell automation APIs. Maybe the related PowerShell Automation code is not properly optimized.
First of all these two enums and a class that will serve to represent the information of a driver:
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.IO
Imports System.Management.Automation
Imports System.Management.Automation.Runspaces
Imports System.Text
Imports Microsoft.PowerShell
''' <summary>
''' Specifies the behavior to get a driver when calling the <see cref="GetSystemDrivers()"/> function.
''' </summary>
<Flags>
Public Enum GetDriverFlags As Short
''' <summary>
''' Get those drivers that are commonly known as default drivers.
''' <para></para>
''' These drivers are included on the Windows distribution media
''' and therefore are automatically installed as part of Windows.
''' <para></para>
''' This is the opposite behavior to <see cref="GetDriverFlags.NotInbox"/> flag.
''' </summary>
Inbox = 1 << 0
''' <summary>
''' Get those drivers that are commonly known as third-party drivers.
''' <para></para>
''' These drivers are not included on the Windows distribution media
''' and therefore are not automatically installed as part of Windows.
''' <para></para>
''' This is the opposite behavior to <see cref="GetDriverFlags.Inbox"/> flag.
''' </summary>
NotInbox = 1 << 1
''' <summary>
''' Get all the drivers (inbox and not-inbox drivers).
''' </summary>
Any = GetDriverFlags.Inbox Or GetDriverFlags.NotInbox
End Enum
''' <summary>
''' Specifies the signature status of a driver.
''' <para></para>
''' This enum is used in <see cref="DismDriverInfo.Signature"/> property.
''' </summary>
Public Enum DismDriverSignature As Integer
''' <summary>
''' The signature status of the driver is unknown.
''' <para></para>
''' DISM only checks for a valid signature for boot-critical drivers.
''' </summary>
Unknown = 0
''' <summary>
''' The driver is unsigned.
''' </summary>
Unsigned = 1
''' <summary>
''' The driver is signed.
''' </summary>
Signed = 2
End Enum
''' <summary>
''' Represents basic information for a installed system driver.
''' </summary>
<Serializable>
Public Class DismDriverInfo
#Region " Notes and research informartion "
' - DismDriverPackage structure:
' https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismdriverpackage-structure
' - Useless DISM object properties that were not implemented in this class:
'
' Path, WinPath, SysDrivePath, ScratchDirectory, LogPath, LogLevel,
' MajorVersion, MinorVersion, Build, Revision
' (those can be accessed via the "DismDriverInfo.Version" property)
#End Region
#Region " Properties "
''' <summary>
''' Gets the provider of the driver.
''' </summary>
Public ReadOnly Property Provider As String
''' <summary>
''' Gets the class name of the driver.
''' </summary>
Public ReadOnly Property ClassName As String
''' <summary>
''' Gets the description of the driver class.
''' <para></para>
''' Note: the description is spelled using the current system language.
''' </summary>
Public ReadOnly Property ClassDescription As String
''' <summary>
''' Gets the class global unique identifier (GUID) of the driver.
''' </summary>
Public ReadOnly Property ClassGuid As Guid
''' <summary>
''' Gets the .inf file name of the driver.
''' </summary>
Public ReadOnly Property DriverFile As String
''' <summary>
''' Gets the driver version.
''' </summary>
Public ReadOnly Property Version As Version
''' <summary>
''' Gets the manufacturer's build date of the driver.
''' </summary>
Public ReadOnly Property BuildDate As Date
''' <summary>
''' Gets a value that indicates the driver signature status.
''' </summary>
Public ReadOnly Property Signature As DismDriverSignature
''' <summary>
''' Gets a value indicating whether the driver is included on the Windows distribution media
''' and automatically installed as part of Windows.
''' </summary>
Public ReadOnly Property Inbox As Boolean
''' <summary>
''' Gets a value indicating whether the driver is located on the operating system
''' that is currently running on the local computer.
''' </summary>
Public ReadOnly Property Online As Boolean
''' <summary>
''' Gets a value indicating whether the driver is boot-critical.
''' </summary>
Public ReadOnly Property BootCritical As Boolean
''' <summary>
''' Gets a value indicating whether the computer needs to be restarted to complete driver install/uninstall.
''' </summary>
Public ReadOnly Property RestartRequired As Boolean
''' <summary>
''' Gets the catalog file for the driver.
''' </summary>
Public ReadOnly Property CatalogFile As String
''' <summary>
''' Gets the original file name of the driver.
''' </summary>
Public ReadOnly Property OriginalFileName As String
''' <summary>
''' Gets the driver directory.
''' </summary>
Public ReadOnly Property Directory As DirectoryInfo
Get
Return New DirectoryInfo(IO.Path.GetDirectoryName(Me.OriginalFileName))
End Get
End Property
#End Region
#Region " Private Fields "
''' <summary>
''' A reference to the underlying <see cref="PSObject"/> object of type: "Microsoft.Dism.Commands.BasicDriverObject".
''' </summary>
Private ReadOnly dismDriverObject As PSObject
#End Region
#Region " Constructors "
''' <summary>
''' Prevents a default instance of the <see cref="DismDriverInfo"/> class from being created.
''' </summary>
<DebuggerNonUserCode>
Private Sub New()
End Sub
''' <summary>
''' Initializes a new instance of the <see cref="DismDriverInfo"/> class.
''' </summary>
''' <param name="dismDriverObject">
''' A <see cref="PSObject"/> object whose <see cref="PSObject.BaseObject"/> property
''' is of type: "Microsoft.Dism.Commands.BasicDriverObject"
''' <para></para>
''' This object is obtained from the return value of a call to <see cref="PowerShell.Invoke()"/> function
''' that invoked the PowerShell's 'Get-WindowsDriver' cmdlet.
''' </param>
''' <remarks>
''' Get-WindowsDriver cmdlet reference:
''' <see href="https://learn.microsoft.com/en-us/powershell/module/dism/get-windowsdriver"/>
''' </remarks>
<DebuggerStepThrough>
Public Sub New(dismDriverObject As PSObject)
If Not dismDriverObject.BaseObject?.GetType().FullName.Equals("Microsoft.Dism.Commands.BasicDriverObject", StringComparison.Ordinal) Then
Throw New ArgumentException("Invalid DISM driver object.", paramName:=NameOf(dismDriverObject))
End If
Me.dismDriverObject = dismDriverObject.Copy()
Try
Me.Provider = CStr(Me.dismDriverObject.Properties("ProviderName").Value)
Me.ClassName = CStr(Me.dismDriverObject.Properties("ClassName").Value)
Me.ClassDescription = CStr(Me.dismDriverObject.Properties("ClassDescription").Value)
Me.ClassGuid = New Guid(CStr(Me.dismDriverObject.Properties("ClassGuid").Value).Trim("{}".ToCharArray()))
Me.DriverFile = CStr(Me.dismDriverObject.Properties("Driver").Value)
Me.Version = New Version(Me.dismDriverObject.Properties("Version").Value.ToString)
Me.BuildDate = Date.Parse(CStr(Me.dismDriverObject.Properties("Date").Value))
Me.Signature = DirectCast(Me.dismDriverObject.Properties("DriverSignature").Value, DismDriverSignature)
Me.Inbox = CBool(Me.dismDriverObject.Properties("Inbox").Value)
Me.Online = CBool(Me.dismDriverObject.Properties("Online").Value)
Me.BootCritical = CBool(Me.dismDriverObject.Properties("BootCritical").Value)
Me.RestartRequired = CBool(Me.dismDriverObject.Properties("RestartNeeded").Value)
Me.CatalogFile = CStr(Me.dismDriverObject.Properties("CatalogFile").Value)
Me.OriginalFileName = CStr(Me.dismDriverObject.Properties("OriginalFileName").Value)
Catch ex As Exception
Throw
End Try
End Sub
#End Region
#Region " Public Methods "
''' <summary>
''' Returns a String that represents the current <see cref="DismDriverInfo"/> object.
''' </summary>
''' <param name="expandedString">
''' If <see langword="True"/>, returns a single-line string; otherwise, a multi-line string.
''' </param>
''' <returns>
''' A string that represents the current <see cref="DismDriverInfo"/> object.
''' </returns>
<EditorBrowsable(EditorBrowsableState.Always)>
<DebuggerStepThrough>
Public Shadows Function ToString(Optional singleLine As Boolean = True) As String
Dim sb As New StringBuilder(capacity:=512)
If singleLine Then
sb.Append("["c)
sb.Append($"{NameOf(Me.Provider)}:{Me.Provider}, ")
sb.Append($"{NameOf(Me.ClassName)}:{Me.ClassName}, ")
sb.Append($"{NameOf(Me.ClassDescription)}:{Me.ClassDescription}, ")
sb.Append($"{NameOf(Me.ClassGuid)}:{Me.ClassGuid}, ")
sb.Append($"{NameOf(Me.DriverFile)}:{Me.DriverFile}, ")
sb.Append($"{NameOf(Me.Version)}:{Me.Version}, ")
sb.Append($"{NameOf(Me.BuildDate)}:{Me.BuildDate.ToShortDateString()}, ")
sb.Append($"{NameOf(Me.Signature)}:{Me.Signature}, ")
sb.Append($"{NameOf(Me.Inbox)}:{Me.Inbox}, ")
sb.Append($"{NameOf(Me.Online)}:{Me.Online}, ")
sb.Append($"{NameOf(Me.BootCritical)}:{Me.BootCritical}, ")
sb.Append($"{NameOf(Me.RestartRequired)}:{Me.RestartRequired}, ")
sb.Append($"{NameOf(Me.CatalogFile)}:{Me.CatalogFile}, ")
sb.Append($"{NameOf(Me.OriginalFileName)}:{Me.OriginalFileName}, ")
sb.Append($"{NameOf(Me.Directory)}:{Me.Directory.FullName}")
sb.Append("]"c)
Else
sb.AppendLine($"{NameOf(Me.Provider)}: {Me.Provider}")
sb.AppendLine($"{NameOf(Me.ClassName)}: {Me.ClassName}")
sb.AppendLine($"{NameOf(Me.ClassDescription)}: {Me.ClassDescription}")
sb.AppendLine($"{NameOf(Me.ClassGuid)}: {Me.ClassGuid}")
sb.AppendLine($"{NameOf(Me.DriverFile)}: {Me.DriverFile}")
sb.AppendLine($"{NameOf(Me.Version)}: {Me.Version}")
sb.AppendLine($"{NameOf(Me.BuildDate)}: {Me.BuildDate.ToShortDateString()}")
sb.AppendLine($"{NameOf(Me.Signature)}: {Me.Signature}")
sb.AppendLine($"{NameOf(Me.Inbox)}: {Me.Inbox}")
sb.AppendLine($"{NameOf(Me.Online)}: {Me.Online}")
sb.AppendLine($"{NameOf(Me.BootCritical)}: {Me.BootCritical}")
sb.AppendLine($"{NameOf(Me.RestartRequired)}: {Me.RestartRequired}")
sb.AppendLine($"{NameOf(Me.CatalogFile)}: {Me.CatalogFile}")
sb.AppendLine($"{NameOf(Me.OriginalFileName)}: {Me.OriginalFileName}")
sb.AppendLine($"{NameOf(Me.Directory)}: {Me.Directory.FullName}")
End If
Return sb.ToString()
End Function
#End Region
End Class
Secondly, this function which will serve to retrieve all the installed drivers:
Public NotInheritable Class DriverUtil
<DebuggerNonUserCode>
Private Sub New()
End Sub
''' <summary>
''' Get infomation about the drivers that are installed
''' on the operating system that is currently running on the local computer.
''' </summary>
''' <param name="flags">
''' Flags combination that determine the function behavior.
''' </param>
''' <returns>
''' A <see cref="ReadOnlyCollection(Of DismDriverInfo)"/> object that represents the information about every driver found.
''' </returns>
<DebuggerStepThrough>
Public Shared Function GetSystemDrivers(flags As GetDriverFlags) As ReadOnlyCollection(Of DismDriverInfo)
If Not flags.HasFlag(GetDriverFlags.Inbox) AndAlso
Not flags.HasFlag(GetDriverFlags.NotInbox) Then
Throw New InvalidEnumArgumentException(NameOf(flags), flags, GetType(GetDriverFlags))
End If
Dim driverInfoList As New List(Of DismDriverInfo)
Dim sessionState As InitialSessionState = InitialSessionState.CreateDefault()
sessionState.ExecutionPolicy = ExecutionPolicy.Unrestricted
sessionState.ImportPSModule("Dism")
Using runspace As Runspace = RunspaceFactory.CreateRunspace(sessionState),
ps As PowerShell = PowerShell.Create(runspace)
Dim hasFlagInbox As Boolean = flags.HasFlag(GetDriverFlags.Inbox)
Dim hasFlagNotInbox As Boolean = flags.HasFlag(GetDriverFlags.NotInbox)
' Save default runspace, if any.
Dim previousDefaultRunSpace As Runspace = Runspace.DefaultRunspace
' Avoids running PowerShell scripts on new threads that has not unrestricted execution policy.
Runspace.DefaultRunspace = runspace
Runspace.DefaultRunspace.Open()
Dim scratchDirectoryPath As String = Path.GetTempPath()
Dim logFilePath As String = Path.Join(Path.GetTempPath(), "Get-WindowsDriver.log")
ps.AddCommand("Get-WindowsDriver")
' Specifies that the action is to be taken on the operating system that is currently running on the local computer.
ps.AddParameter("Online")
' Specifies the full path and file name to log to. If not set, the default is %WINDIR%\Logs\Dism\dism.log.
ps.AddParameter("LogPath", logFilePath)
ps.AddParameter("LogLevel", "2") ' 2 = Errors and warnings
' Specifies a temporary directory that will be used when extracting files for use during servicing.
' The directory must exist locally.
ps.AddParameter("ScratchDirectory", scratchDirectoryPath)
If hasFlagInbox Then
' Get information about default drivers and third-party drivers.
' If you do not specify this parameter, only gets information about third-party drivers.
ps.AddParameter("All")
End If
Dim dismDriverObjects As Collection(Of PSObject)
Try
dismDriverObjects = ps.Invoke()
Catch ex As Exception
Throw
End Try
For Each dismDriverObject As PSObject In dismDriverObjects
Dim driverInfo As New DismDriverInfo(dismDriverObject)
If flags <> GetDriverFlags.Any Then
If (hasFlagInbox AndAlso Not driverInfo.Inbox) OrElse
(hasFlagNotInbox AndAlso driverInfo.Inbox) Then
Continue For
End If
End If
driverInfoList.Add(driverInfo)
Next dismDriverObject
Runspace.DefaultRunspace.Close()
' Restore default runspace, if any.
Runspace.DefaultRunspace = previousDefaultRunSpace
End Using
Return driverInfoList.AsReadOnly()
End Function
End Class
Third and last this code example that demonstrates how to differentiate between third-party drivers and default drivers:
' Get all drivers.
Dim allDriversList As ReadOnlyCollection(Of DismDriverInfo) = DriverUtil.GetSystemDrivers(GetDriverFlags.Any)
Debug.WriteLine($"{NameOf(allDriversList)} count: {allDriversList.Count}")
' Get only default (integrated) drivers.
Dim defaultDriversList As ReadOnlyCollection(Of DismDriverInfo) = DriverUtil.GetSystemDrivers(GetDriverFlags.Inbox)
Debug.WriteLine($"{NameOf(defaultDriversList)} count: {defaultDriversList.Count}")
' Get only third-party drivers.
Dim thirdPartyDriversList As ReadOnlyCollection(Of DismDriverInfo) = DriverUtil.GetSystemDrivers(GetDriverFlags.NotInbox)
Debug.WriteLine($"{NameOf(thirdPartyDriversList)} count: {thirdPartyDriversList.Count}")
' Iterate third-party drivers.
For Each driverInfo As DismDriverInfo In thirdPartyDriversList
Debug.WriteLine(driverInfo.ToString(singleLine:=False))
Next driverInfo
Output sample:
ProviderName: Advanced Micro Devices, Inc
ClassName: System
ClassDescription: Dispositivos del sistema
ClassGuid: 4d36e97d-e325-11ce-bfc1-08002be10318
Driver: oem2.inf
Version: 2.2.0.130
Date: 11/03/2020
IsSigned: True
Inbox: False
Online: True
BootCritical: True
RestartNeeded: False
CatalogFile: amdgpio2.cat
OriginalFileName: C:\Windows\System32\DriverStore\FileRepository\amdgpio2.inf_amd64_c72fc3523c1372d0\amdgpio2.inf
Directory: C:\Windows\System32\DriverStore\FileRepository\amdgpio2.inf_amd64_c72fc3523c1372d0
ProviderName: VMware, Inc.
ClassName: Net
ClassDescription: Adaptadores de red
ClassGuid: 4d36e972-e325-11ce-bfc1-08002be10318
Driver: oem16.inf
Version: 14.0.0.5
Date: 09/03/2021
IsSigned: True
Inbox: False
Online: True
BootCritical: False
RestartNeeded: False
CatalogFile: vmnetadapter.cat
OriginalFileName: C:\Windows\System32\DriverStore\FileRepository\netadapter.inf_amd64_8e12d1edcc9e768d\netadapter.inf
Directory: C:\Windows\System32\DriverStore\FileRepository\netadapter.inf_amd64_8e12d1edcc9e768d
...