Home > front end >  How to determine if a installed driver it's a third-party driver in .NET?
How to determine if a installed driver it's a third-party driver in .NET?

Time:10-07

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: enter image description here


I found that the PowerShell's enter image description here

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

...
  • Related