Mini-filter driver / .inf / DIRID 13 / install & uninstall

For the file system mini-filter driver project that I'm currently working on, the .inf file has finally been populated to support both x64 & ARM64, and is using DIRID 13 (Driver Store) as the destination. Building the driver for both x64 & ARM64 is working just fine, including the verifying of the .inf file and the creation of the .cat file.

I have Hyper-V virtual machines running Windows 11 24H2 (x64 & ARM64) and Windows Server 2025 24H2 (x64), with all of the gpedit.msc & bcdedit.exe "tweaks" applied for test-mode so that test-signed drivers can be installed & loaded, and the HLK 24H2 (May 2025) client is installed on all 3 of the VM. In the file explorer, I can do a "right-click -> Install", and the driver is correctly installed to the driver store and the driver's service is created. Likewise, rundll.exe & setupapi.dll can be used from the command line to perform the same driver installation operation. A subsequent "fltmc load" command results in the driver loading and attaching to a volume, or a reboot can be performed. So far, so good.

What I'm having difficulty finding is how to properly uninstall the driver from the driver store. If DIRID 12 was being used, the file paths would be invariant and the task would be as simple as deleting the service and then deleting the driver files from disk. Ideally, I need to be able to automate deployment of just the driver to my HLK client VMs, including complete cleanup of an older build before a newer build needs to go through HLK testing.

Per Microsoft's documentation, usage of a default uninstall section in the .inf file is now forbidden, ditto for a section to uninstall the service.

Google searches have mostly turned up options based on GUI tools, including a driver store explorer, none of which are acceptable for automation purposes.

The best method that I've found so far is this:

https://stackoverflow.com/questions/78644706/powershell-delete-driver-from-windows-driverstore

It uses the PowerShell cmdlet "Get-WindowsDriver -Online" to obtain an array of objects to iterate through, which allows me to find the entry with my driver's .inf file name as the "original" file name and correlate it with an "oem<#>.inf" file (ex. "oem5.inf") which can then be passed to "pnputil /delete-driver oem<#>.inf /uninstall /force". That resulted in the driver being deleted from the driver store location on disk, but it left the service present with the start type set to "disabled" and fltmc.exe still showed the driver as loaded. Issuing either "fltmc unload " or simply rebooting was sufficient to leave no active "residue"

What is the recommended "best practices" method of performing the uninstallation from the command line? Any links to appropriate online documentation from Microsoft would be helpful. Eventually, there will be a WiX project to create an MSI package for the driver, but it's "putting the cart before the horse" right now to require an MSI package for use on the HLK client machines if any other more simple method can be used.

It seems like so much of the relevant documentation is now riddled with errors, out-of-date or contradictory that it's become very frustrating trying to find out what, exactly, is required "end to end" for bringing a new driver project up from scratch starting with the .sys/.inf/.cat files, building out the HLK environment, performing the correct set of tests and merging all required .hlkx files for submission to Microsoft for signatures. Even the current HLK 24H2 (May 2025) documentation references non-existent playlist files for testing on ARM64.

The only relevant and accurate documentation, AFAIC, is the devcon sample in microsoft's driver samples repo on github. This has been the case for the last 25 years or so.

I have only been using DIRID 12 but:
if the driver is miniport driver it needs a reboot to completely be removed.
sorry I can not remember where this is documented if all.

if it a 'pure' filter driver then it you can uninstall and update it without reboot.

the worst is when there is a windows update pending, I have seen at least once after update you get old driver back.

It's a pure file system mini-filter driver... software-only... not physical hardware associated with it.

I've been looking at the source code for DevCon, as was recommend. I had initially discounted devcon.exe as all of Microsoft's current documentation shows repeatedly that it's been replaced by pnputil.exe, which is shipped with Windows itself. However, the source code for DevCon is at least showing usage examples for the 3 uninstallation steps that I've been having to piece together with PowerShell & pnputil.exe, so it seems quite reasonable to write a bit of C++ or C# to create a low-level driver / driver package uninstall program that can be used by my MSI package. It appears that the v3 WiX toolset is now no longer supported, with the driver extension going deprecated in v4 and completely removed from v5. From all that I've been reading, WiX no longer has any device driver / driver package support present in it and installer package developers are left to create their own from scratch.

1 Like

C# is less straightforward than C++ in this case.

I've been playing around with 'devcon in a class' here: HtsVsp/App/vspControl at main · HollisTech/HtsVsp
and it is a public repo, so feel free to borrow from it.

Devcon has been deprecated for more than a decade and most of what it does is unsupported. pnputil is the command-line tool for doing most of what devcon did - you can actually point to any copy of your INF (including the copy in the directory you originally installed from) when calling pnputil, so you don't necessarily need to look up the OEM name.

pnputil /delete-driver <path to any copy of your INF> /uninstall

Alternatively, you can call DiUninstallDriver to do it in code

That's a most useful gem. Thanks for mentioning it.

I see that my driver's service registry key has a REG_MULTI_SZ value named "Owners" which contains a single value, "oem5.inf", when the service was created via "pnputil /add-driver /install". If I execute "pnputil /delete-driver oem5.inf /uninstall", I see that the "Owners" value is then deleted. I don't see any documentation regarding the "Owners" value to indicate whether it would be relatively save to use it as a shortcut to finding the published "oem<#>.inf" file used for my driver.

I also ran across another DIRID 13 issue today while refactoring my .inf file to accommodate down-level systems, such as Windows 10 22H2, Windows Server 2022, Windows Server 2019 1809 and Windows Server 2016 1803. Specifically, either of "right-click -> Install", or rundll32.exe & setupapi.dll fail to create the driver's service if the "ServiceBinary" value starts with "%13%" instead of "%12%". I see the following types of failures in the "setupapi.app.log" file:

 inf:      AddService=MyDriver,,MiniFilterPre26100.Service  (MyDriver.inf line 121)
 inf:      ServiceType=2  (MyDriver.inf line 144)
 inf:      StartType=1  (MyDriver.inf line 145)
 inf:      ErrorControl=1  (MyDriver.inf line 146)
 inf:      ServiceBinary=.\MyDriver.sys  (MyDriver.inf line 142)

!!! dvi: Driver Path not in system root
inf: {Install Inf Section [DefaultInstall.NTamd64.Services] exit(0xe0000217)}
inf: Error 0xe0000217: A service installation section in this INF is invalid.
!!! dvi: Error while installing services.
!!! dvi: Error 0xe0000217: A service installation section in this INF is invalid.

When "pnputil /add-driver /install" is used, the driver gets copied to the driver store, but the service doesn't created and there is no error reported in "setupapi.dev.log" and no "setupapi.app.log" file is created.

The relevant section from my .inf file is as follows:

[MiniFilterPre26100.Service]
DisplayName = %Service.DisplayName%
Description = %Service.Description%
ServiceBinary = %13%\MyDriver.sys
Dependencies = FltMgr
ServiceType = 2 ; SERVICE_FILE_SYSTEM_DRIVER = 2
StartType = 1 ; SERVICE_SYSTEM_START = 1, SERVICE_DEMAND_START = 3, SERVICE_DISABLED = 4
ErrorControl = 1
LoadOrderGroup = "FSFilter Security Monitor"
AddReg = MiniFilterPre26100.AddRegistry

As I look at the sample mini-filter driver projects, including MiniSpy, I see that the down-level sections in the .inf files are using DIRID 12, while the OS version decorated version for Windows 11 24H2 is using DIRID 13.

From the following:

I see "Driver packages have general support for 'run from Driver Store' starting with Windows 10 1709". However, that's not what I'm experiencing. Is there additional documentation which gives a definitive statement about what the minimum OS version & build values are for "ServiceBinary" to have a value where the path value starts with "%13%"? Or, should I just take it for granted that the .inf file in the MiniSpy sample project assumes that only a driver installation performed on Windows 11 24H2 will create the service that way, and that installation on Windows 11 23H2 & older must always use a path value starting with "%12%"?

I used the following as a reference, too:

Again, everything indicates that it should be possible to run a driver from the driver store using DIRID 13 starting with Windows 8.1, yet the service creation fails when the binary path starts with "%13%".

You're looking at the device driver documentation, but you're creating a primitive driver (I acknowledge that this isn't clear). More information is here: Creating a Primitive Driver - Windows drivers | Microsoft Learn

I wouldn't consider using any value under the service key as safe to use. It's private OS data and is subject to change

Yes, I had read the primitive driver topic, and that's part of what lead me down several other rabbit holes related to .inf file structure and meeting the requirements to pass verification of the .inf file as well as getting my driver package signed later on.

That still doesn't address the service creation failure that I'm observing on down-level systems when DIRID 13 is used as the directory for the driver service's binary path. Based on the primitive driver documentation and the DIRID documentation "%13%\MyDriver.sys" should work 100% of the time starting with Windows 8.1 and going forward all the way to Windows 11 24H2. However, the driver installation performed via the .inf file fails on Windows Servrer 2019 1809 unless I change the .inf file to use DIRID 12 for the destination directory & the binary path.

Is this another case where the code that processes the .inf file lacks awareness of DIRID 13 in certain cases? I reviewed more of the file system mini-filter device driver sample projects and their respective .inf files, and all of them appear to be using DIRID 12 for all versions of Windows prior to Windows 11 24H2.

Yes - DIRID 13 support is part of the primitive driver feature, which was introduced in 1903. And even though that specific feature was there, the full featureset that I wanted for file system filters wasn't available until 24H2, so we didn't really advertise this or change the file system filter samples until then.

Thanks for the clarification. Those additional details help to improve the understanding of how down-level systems are to be handled vs. Windows 11 24H2 & newer. Is there any chance that the documentation will get an overhaul to clearly define "primitive" drivers, including the fact that all file system mini-filter drivers must be "primitive" by definition, along with giving clear instruction regarding what INF file sections & directives are & are not supported, including architecture & OS version decoration, all of this with caveats mentioned for down-level versions of Windows?

fyi - I tested pnputil.exe using "/delete-driver" and passing it the full path to my driver's .inf file on disk, which was originally used to install the driver. That worked on newer builds of Windows, but failed on Windows Server 2019 1809, where I absolutely had to provide the "oem<#>.inf" file name as the parameter value. Since that build of Windows pre-dates Windows 10 1903, I'm assuming that it's too old to support using the original source .inf file to perform an uninstall.

I will when I get some time. The documentation will likely align with what the samples do though, and say that they have to be primitive starting in 24H2 and should not be primitive prior to that. There is a pretty huge variance in how closely people actually read the documentation, so I'm inclined to keep it quite simple.