Installing filter drivers with DIFxApp and a WiX v3 MSI

The designers of Windows Installer probably did not have driver installation in mind. Indeed, the ServiceInstall MSI database table does not even allow for service types SERVICE_KERNEL_DRIVER and SERVICE_FILE_SYSTEM_DRIVER in the ServiceType column. Originally, Microsoft encouraged driver installation by means of a “setup application”, custom code invoking the SetupAPI. The Setup API functions would be used to examine an .INF file describing required steps for the driver’s installation. Back in Windows NT 4.0, even .INF files were often avoided in lieu of directly setting up the legacy driver service in the system registry with C code.

Enter Windows 2000 with Plug and Play. Now the system uses .INF files it maintains in a store to locate and install drivers for devices on demand. When a new device is inserted, the system looks for an .INF file with a matching hardware identifier. For most hardware devices, .INF based installation becomes a de-facto requirement. However, for other driver categories, such as file system filters, device class filters, etc., an .INF file is but one way of performing the driver installation process.

Microsoft, when confronted by driver setup authors who denounce .INF files as having cryptic, old-fashioned syntax, non-ideal diagnosibility and debuggability and other shortcomings, persists in encouraging their use. They are a WHQL logo requirement and in some cases it is claimed they will be the only way to install drivers in future Windows releases.

Often driver installation is but a part of the installation process of a larger application program. For applications, Microsoft encourages (and mandates as a logo requirement) the use of MSI packages and the Windows Installer service. In order to avoid having every MSI setup author who needs to perform driver installation roll his own custom invocation of the driver’s .INF installation and in a bid to improve driver installation’s synergy with Windows Installer in general, Microsoft created DIFxApp – Driver Install Frameworks for Applications.

MSI and DIFxApp’s .INF based installation make for an odd couple. The Windows Installer model is transactional – describe what changes should be made to the system and let Windows Installer determine how to execute them and importantly, how to roll them back. After having encouraged COM DLLs to encapsulate the details of their installation and registration with the Self-Registration DllRegisterServer export for years, describing the individual elements of the COM registration using an MSI’s Registry table is now encouraged instead. DllUnregisterServer can fail in mysterious ways due to missing dependencies, etc., leaving cruft behind during uninstallation. By contrast, rolling back insertions made from the Registry table is always possible for the MSI service. If COM’s long standing installation architecture was set aside to better align with MSI’s model, one would expect a similar occurance for drivers. However, DIFxApp, the solution for installing drivers with MSI, is the moral equivalent of COM Self-Registration. If a driver’s .INF does not remove all changes it made during installation, the uninstallation process is bound to be imperfect. MSI’s ability to diagnose whether a driver’s installation has been damaged is limited and thwarted.

Be that as it may, .INF files’ tight integration with the Plug and Play system, the fact they are a logo requirement and uniformity considerations are compelling arguments for their use. From this point forward, it shall be assumed that .INF based installation and MSI integration have been chosen.

If you’ve developed a common PnP WDM driver, you probably already have an .INF for installation purposes. That .INF can usually be used as is with DIFxApp, which assumes PnP drivers by default. It is for other driver categories that the process becomes more tricky. I’ll discuss filter drivers of three varieties in particular – file system filters, file system minifilters and device class filters.

For illustration purposes, let’s consider a typical file system minifilter driver, DirFilter, which is presently a pet project of mine that will eventually perform interesting modifications to directory listing I/O operations. The following is DirFilter’s .INF file:

Signature = "$Windows NT$"
Class = "ActivityMonitor"
ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}
Provider = %Koby%
DriverVer = 09/12/2008,
CatalogFile =
DriverPackageType = FileSystemMinifilter
DefaultDestDir = %DIRID_DRIVERS%
DirFilter.DriverFiles = %DIRID_DRIVERS%
OptionDesc = %ServiceDescription%
CopyFiles = DirFilter.DriverFiles
AddService = %ServiceName%,,DirFilter.Service
DelFiles = DirFilter.DriverFiles
DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting
DisplayName = %ServiceName%
Description = %ServiceDescription%
ServiceBinary = %12%\%DriverName%.sys ; DIRID_DRIVERS
Dependencies = "FltMgr"
LoadOrderGroup = "FSFilter Activity Monitor"
AddReg = DirFilter.AddRegistry
dirfilter.sys = 1
1 = %DiskId1%
Koby = "Koby Kahane"
ServiceDescription = "DirFilter minifilter driver"
ServiceName = "DirFilter"
DriverName = "DirFilter"
DiskId1 = "DirFilter minifilter installation media"
; Instance specific information.
Instance1.Name = "DirFilter Instance"
Instance1.Altitude = "370021" ; Real world minifilters should use Microsoft allocated altitude
Instance1.Flags = 0x0 ; Allow automatic attachments

This is, I believe, a pretty typical .INF for a minifilter. A default instance of it is set up at an altitude in an appropriate filter load order group. This is as good a place as any to note the importance of requesting a Minifilter Altitude Allocation from Microsoft for public drivers rather than using some random number which may end up causing conflicts and interoperability issues. From my experience, it takes Microsoft two months or so to respond to an allocation request so make sure to take care of it early in your release cycle.

As a first step towards a DIFxApp-based MSI driver installation, write an .INF file that will work when right clicking it and selecting Install from Explorer’s popup menu. This calls SetupAPI’s InstallHinfSection on the .INF file’s DefaultInstall section. Further, ensure that the uninstallation process works as expected, and leaves no trails behind, by using InstallHinfSection to invoke the DefaultUninstall section of the file.

You may discover that you are encountering difficulty at this stage. I recommend running the ChkINF utility, which analyzes .INF files for errors and mishaps, before testing the .INF file and indeed after every modification to an existing file. ChkINF, while sometimes terse, is nowhere near as cryptic as errors you will receive from SetupAPI. However, if you encounter setup errors even after ChkINF has verified your file, you can utilize verbose SetupAPI logging to further diagnose setup issues.

With a working “right-click Install” .INF file, the time is ripe for integration into an MSI installer. First of all, DIFxApp requires an additional directive in the Version section of your .INF file, the DriverPackageType. The driver package type is used by DIFx to determine what steps are required for the installation of your specific type of driver. For example, if you fail to specify the type of your file system filter, DIFx assumes it is a PnP driver and the “installation” will just import the .INF into the system driver store. No service will be created and success will not be achieved. Unfortunately, the documentation for the DriverPackageType is sorely lacking at best. You will see it only mentions ClassFilter and PlugAndPlay as possible types in MSDN. MSDN proves a disappointment. However, another Microsoft information source, their Requirements for Driver Packages that are Used with the DIFx Tools document, calls for the use of DIFx for Class Filters, Plug and Play drivers, File System & File System Filter drivers, Network drivers, Kernel Service Drivers and Export Drivers. The DriverPackageType values ClassFilter, FileSystem, FileSystemFilter, KernelModule, KernelService, Network and PlugAndPlay are documented there. You may notice that a special type for minifilters is conspicuously absent from the list. At first, I figured perhaps DIFx has no need for a distinction between legacy filters and minifilters. Later, an old post (free registration required) on OSR’s NTFSD mailing list turned my attention to the fact DIFx will require a system restart for a minifilter, even though it can be loaded and unloaded without one. Neal Christiansen, Microsoft’s File System Filter Group Lead, replies (back in 2004) that appropriate support for minifilter installation is forthcoming. I therefore set out to discover whether DIFxApp as available today provides special support for minifilters.

When the Windows Server 2008 WDK is installed to the default location, the x86 versions of the DIFxApp MSI custom action DLLs reside in C:\WINDDK\6001.18000\redist\DIFx\DIFxApp\English\WixLib\x86. I examined them with the Interactive Disassembler and was happy to see Microsoft made available public symbols (.PDBs) for them. To make a long story short, the DriverPackageType field is examined by DIFxApp in the SetupAPI::CDriverPackage::GetDriverType(CRefPtr<SetupAPI::CInf>, DRVPKG_TYPE&) function in DIFxAppA.dll, which references a global table, dubbed DriverTypeStringList, for possible package types:

It is now evident that available driver package types as of DIFx 2.1 are ClassCoInstaller, ClassFilter, DeviceFilter, FileSystem, FileSystemFilter, FileSystemMinifilter, KernelModule, KernelService, NdisImMiniport, Network and PlugAndPlay. With this information, I opted for the FileSystemMinifilter type for DirFilter’s installation .INF file.

Next, a catalog file is required by DIFx. Even though the purpose of the catalog is to be WHQL signed by Microsoft, DIFx can be used to install drivers without WHQL certification. Nevertheless, the catalog still needs to be there. The easy way is to use the Inf2Cat tool for the catalog’s generation. Place the .INF file and the driver files it references in a staging area and you can run it like so:

E:\Projects\dirfilter\staging>inf2cat /driver:E:\Projects\dirfilter\staging /os:Server2008_X86,Vista_X86,Server2003_X86,XP_X86,2000 /verbose

Specifying, of course, the operating systems and platforms appropriate for your driver.

An .INF file, a .CAT file and a .SYS file are the three files required for a DIFxApp driver package. The DIFxApp model requires your MSI to install these files to a source directory, per driver package (i.e., if you have multiple drivers, they’ll need multiple .INFs in multiple directories) and performs the installation from there to the Windows driver store and eventually the drivers directory under system32.

Microsoft distributes a WiX library and documentation for using it with DIFxApp. However, that library targets the older WiX v2.0 release. WiX v3.0 is the actively developed version, and has different schema, semantics and a slightly different integration model with DIFxApp. If you use WiX v2.0, consult Microsoft’s documentation. Otherwise, let’s proceed. The following .wxs file is the sample installer I wrote for installing DirFilter with DIFxApp:

<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns=''
    <Product Name='DirFilterInstaller' Id='59486bf1-77ad-460f-802d-29dbab0f78be' Language='1033' Codepage='1252' Version='1.0' Manufacturer='KK' UpgradeCode='96fe2ba0-fe8b-45b4-85ef-ea9aca103e6f'>
        <Package Id='*' Keywords='DirFilter' Description='DirFilter Installer' Comments='Installs DirFilter' Manufacturer='KK' InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
        <Media Id='1' Cabinet='' EmbedCab='yes' DiskPrompt='DirFilter Media' />
        <Property Id='DiskPrompt' Value='DirFilter Install Media' />
        <Directory Id='TARGETDIR' Name='SourceDir'>
            <Directory Id='ProgramFilesFolder'>
                <Directory Id='INSTALLDIR' Name='DirFilterApp'>
                    <Directory Id='DirFilterAppDrivers' Name='Drivers'>
                        <Directory Id='DirFilterDir' Name='DirFilter'>
                            <Component Id='DirFilterDriver' Guid='8c64e674-5476-46e4-93cd-ba1ae78622df'>
                                <File Id='DirFilterSYS' Name='DirFilter.sys' DiskId='1' Source='DirFilter.sys' KeyPath='yes' />
                                <File Id='DirFilterINF' Name='DirFilter.inf' DiskId='1' Source='DirFilter.inf' />
                                <File Id='DirFilterCAT' Name='' DiskId='1' Source='' />
                                <difx:Driver Legacy='yes' />
        <Feature Id='Complete' Level='1'>
            <ComponentRef Id='DirFilterDriver' />
        <UIRef Id="WixUI_Minimal" />

Several points are worth mentioning regarding this installer:

  • The XML schema for the WiX v3 DIFxApp extension is referenced at the top of the file. This is required for the Driver element to work. Note that in WiX v3, the Driver element is used instead of driver-specific attributes that were available for the Component element in the WiX v2 DIFxApp extension.
  • As said before, DIFx requires one driver per directory and one driver per .INF file. The driver component is placed in a directory under the application’s directory. Additional drivers will be placed in additional components under additional directories.
  • The “Legacy” attribute is used in the DIFx Driver element because my driver package is not WHQL signed. Of course the best solution for this is to ensure your driver is up to par and actually get WHQL certification. You should also note that Legacy mode disables integrity checks, allowing the driver installation to appear successful even when critical files are missing, etc.
  • Do NOT copy the GUIDs I used if you end copying and pasting from this example. Make sure you generate your own with uuidgen and replace them.

The .wxs source for this WiX installer is straightforward enough. However, actually compiling it and linking it requires referencing several external dependencies. When running Candle, the WiX compiler, the WixDifxAppExtension should be referenced (and others you may depend on, such as WixUIExtension). When running Light, the WiX linker, you should reference the extension as before. Additionally, in the newer builds of WiX v3.0, multiplatform support for DIFx requires you reference the .wixlib with the DIFxApp custom action DLLs for the appropriate platform:

E:\Projects\dirfilter\staging>%WIX_PATH%\candle -ext %WIX_PATH%\WixUIExtension.dll -ext %WIX_PATH%\WixDifxAppExtension.dll DirFilterInstaller.wxs
Microsoft (R) Windows Installer Xml Compiler version 3.0.4617.0
Copyright (C) Microsoft Corporation. All rights reserved.


E:\Projects\dirfilter\staging>%WIX_PATH%\light -ext %WIX_PATH%\WixUIExtension.dll -
ext %WIX_PATH%\WixDifxAppExtension.dll DirFilterInstaller.wixobj %WIX_PATH%\difx
-o DirFilterInstaller.msi
Microsoft (R) Windows Installer Xml Linker version 3.0.4617.0
Copyright (C) Microsoft Corporation. All rights reserved.

(The WIX_PATH environment variable is set to WiX 3.0’s bin directory on my system.)

Failing to reference WixDifxAppExtension or difxapp_[platform].wixlib will result in assorted compilation or linkage errors during installer build.

When debugging DIFxApp driver installation, usually the interesting things are performed by the SetupAPI directly and therefore the SetupAPI log should be examined first. However, DIFxApp’s custom actions write diagnostic information into the MSI log (which can be enabled with a command-line switch to msiexec) and can provide additional insight into the driver installation process. Here’s an excerpt from the MSI log of a successful DirFilter installation:

MSI (s) (2C:44) [20:22:39:061]: Executing op: ActionStart(Name=MsiInstallDrivers,,)
Action 20:22:39: MsiInstallDrivers.
MSI (s) (2C:44) [20:22:39:077]: Executing op: CustomActionSchedule(Action=MsiInstallDrivers,ActionType=3073,Source=BinaryData,Target=InstallDriverPackages,CustomActionData=2.15{8C64E674-5476-46E4-93CD-BA1AE78622DF}C:\Program Files\DirFilterApp\Drivers\DirFilter\82DirFilterInstallerKK)
MSI (s) (2C:90) [20:22:39:092]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSI29.tmp, Entrypoint: InstallDriverPackages
DIFXAPP: ENTER: InstallDriverPackages()
DIFXAPP: INFO: 'CustomActionData' property 'DIFxApp Version' is '2.1'.
DIFXAPP: INFO: 'CustomActionData' property 'UI Level' is '5'.
DIFXAPP: INFO: 'CustomActionData' property 'componentId' is '{8C64E674-5476-46E4-93CD-BA1AE78622DF}'.
DIFXAPP: INFO: 'CustomActionData' property 'componentPath' is 'C:\Program Files\DirFilterApp\Drivers\DirFilter\'.
DIFXAPP: INFO: 'CustomActionData' property 'flags' is 0x8.
DIFXAPP: INFO: 'CustomActionData' property 'installState' is '2'.
DIFXAPP: INFO: 'CustomActionData' property 'ProductName' is 'DirFilterInstaller'.
DIFXAPP: INFO: 'CustomActionData' property 'ManufacturerName' is 'KK'.
DIFXAPP: INFO: user SID of user performing the install is 'S-1-5-21-1417001333-651377827-725345543-1003'.
DIFXAPP: INFO: opening HKEY_USERS\S-1-5-21-1417001333-651377827-725345543-1003\Software\Microsoft\Windows\CurrentVersion\DIFxApp\Components\{8C64E674-5476-46E4-93CD-BA1AE78622DF} (User's SID: 'S-1-5-21-1417001333-651377827-725345543-1003') ...
DIFXAPP: INFO: ENTER: DriverPackageInstallW
DIFXAPP: INFO: Copied 'DirFilter.inf' to driver store...
DIFXAPP: INFO: Copied '' to driver store...
DIFXAPP: INFO: Commiting queue...
DIFXAPP: INFO: Copied file: 'C:\Program Files\DirFilterApp\Drivers\DirFilter\dirfilter.sys' -> 'C:\WINDOWS\system32\DRVSTORE\DirFilter_11E71DD96C3AADF7CDEC882620E8410DECE9B5EF\dirfilter.sys'.
DIFXAPP: INFO: Installing INF file "C:\WINDOWS\system32\DRVSTORE\DirFilter_11E71DD96C3AADF7CDEC882620E8410DECE9B5EF\DirFilter.inf" of Type 4.
DIFXAPP: INFO: Installing File System Driver 'C:\WINDOWS\system32\DRVSTORE\DirFilter_11E71DD96C3AADF7CDEC882620E8410DECE9B5EF\DirFilter.inf'
DIFXAPP: INFO: Service 'DirFilter' was started
DIFXAPP: SUCCESS:Installation completed with code 0x0.
DIFXAPP: INFO: RETURN: DriverPackageInstallW (0x0)

DIFXAPP: INFO: ENTER: DriverPackageGetPathW
DIFXAPP: SUCCESS:Found driver store entry.
DIFXAPP: INFO: RETURN: DriverPackageGetPathW (0x7A)
DIFXAPP: INFO: ENTER: DriverPackageGetPathW
DIFXAPP: SUCCESS:Found driver store entry.
DIFXAPP: INFO: RETURN: DriverPackageGetPathW (0x0)
DIFXAPP: INFO: driver store entry for 'C:\Program Files\DirFilterApp\Drivers\DirFilter\DirFilter.inf' is 'C:\WINDOWS\system32\DRVSTORE\DirFilter_11E71DD96C3AADF7CDEC882620E8410DECE9B5EF\DirFilter.inf'.
DIFXAPP: INFO: The component Id '{8C64E674-5476-46E4-93CD-BA1AE78622DF}' is now set to point to driver store: 'C:\WINDOWS\system32\DRVSTORE\DirFilter_11E71DD96C3AADF7CDEC882620E8410DECE9B5EF\DirFilter.inf'
DIFXAPP: INFO: A reboot is not needed to install the component '{8C64E674-5476-46E4-93CD-BA1AE78622DF}'.
DIFXAPP: RETURN: InstallDriverPackages() 0 (0x0)

Notice how DIFxApp places a copy of your driver package in the system driver store, a behavior which is distinct from “right-click Install” .INF invocation. Notice also it reports whether the installation operation with SetupAPI was successful and will additionally take care of scheduling a reboot if the driver package so requires.

DIFxApp works equally well for installing a legacy file system filter. Simply use the FileSystemFilter DriverPackageType value instead.

Device class filters require special consideration. MSDN has a page about Installing a Filter Driver. For device filters, the process is straightforward – your .INF simply adds an UpperFilter under the appropriate device’s hardware key, using the special “HKR” relative root. However, a device class filter has no hardware key to refer to and indeed may wish to filter several distinct device classes. The MSDN page says one ought to write a custom “setup application” that shall invoke the .INF file for the several device classes of interest, as desired. However, DIFxApp offers no such flexibility and it does not appear that it can be used to drive such an .INF file. However, the fact it has a ClassFilter type clearly implies it has device class filters in mind.

As is often the case, the answer to this dillemma lies in the WDK. The WDK features a sample WiX v2.0 project for illustrating DIFxApp’s use, which in fact installs a class filter. In a default installation, you can find it in C:\WINDDK\6001.18000\src\setup\DIFxApp\WiXLib\ClassFilter.wxs. Nearby, in C:\WINDDK\6001.18000\src\setup\infs\clasfilt\ClasFilt.Inf, resides the sample .INF file. Among other things, the file includes an explicit registration as a class UpperFilter:

; Change {setup-ClassGUID} to the string form of the ClassGUID that you are installing the filter on.
; Change UpperFilters to LowerFilters if this is a lower class filter.
HKLM, System\CurrentControlSet\Control\Class\{setup-ClassGUID}, UpperFilters, 0x00010008, clasfilt

Fair enough. Multiple entries can be included in the registry section for UpperFilter registration directly under the class keys of interest. However, if you opt for this approach you may find to your dismay you no longer fall in ChkINF’s favor:

(W22.1.2213) INF files should not set registry entries under 'HKLM,System\CurrentControlSet\Control\Class'.

I guess you’re damned if you do and damned if you don’t. I suggest not drinking ChkINF’s Kool-Aid, setting up the device class filter registration directly and using DIFxApp rather than iterating with a custom setup application, obviously.

Hopefully this lengthy writeup will prove useful to other authors of MSI-based driver installations.


15 thoughts on “Installing filter drivers with DIFxApp and a WiX v3 MSI

  1. Thanks for posting this article. It has been very helpfull for me.
    I already had an msi file prepared so I have used DIFxApp.msm to author installation package. There is a mess in documentation how to do that but it has been pretty simple at the end. . Only first 6 steps has sense.
    I’m installing minifilter however installer stilll wants me to restart after installation completing :(

  2. Jan, are using a DriverPackageType of FileSystemMinifilter rather than FileSystemFilter? This should prevent the restart prompt from the installer, unless you are doing something specific in your INF that triggers a restart like overwriting an in-use file, etc.

  3. Sorry for replaying so late but I do not get notification on mail.
    As a DriverPackageType I select FileSystemMiniFilter (f -> F). To suppress restarting I also had to change service StartType from SERVICE_BOOT_START into SERVICE_SYSTEM_START so I’m not completely sure if upper F is the problem.

  4. Good Post!!.
    the drivers are getting installed in drvstore under system32. But actually I want drivers to be copied to spool\drivers\ folder. If the printer driver is adding using AddPrinter wizard by pointing the same inf,files are installed in spool. So why the difxapp copying files to system32\drvstore.
    As per my understanding difxapp will does the same work as AddPrinter Wizard does. Please let me know what I should I do inorder to achieve it.

  5. Excellent article, you saved me a ton of time!!!

    One problem that I’m having, though, is that the .sys file for my minifilter doesn’t get deleted on the uninstall. It does if I manually uninstall using the .INF and the SetupAPI call, but it does not when I use the WiX/DIFx MSI… no errors or anything, the driver stops and gets removed from the registry, but the .sys lingers around. It is still there after a reboot too. I enabled full logging for SetupAPI but no errors or warnings are reported there either, in fact the entire uninstall only generates on line of debug.

    Any ideas?

    • Jeff, I assume that the full MSI log (msiexec /l*v …) was similarly uninformative.
      Where are you installing the .sys file to? The path the File is installed to using the MSI is just a source location from which the driver is copied to the Driver Store when using DIFx. When DIFx invokes SetupAPI, the driver is added to the store, where it probably remains even if the MSI that originally had it imported to the store has been removed. Look into whether some sort of interaction with the driver store (which is not involved in a “right-click Install” of an .INF) is causing this.

      • Hi Koby, thanks for the quick response!

        I am using your wsx file with only the guids and filenames changed. This is installing the the .cat .inf and .sys to \Program Files\MyApp\Drivers\MyFilter. Then the .sys of course gets copied to \windows\system32\drivers during the real install. The files in \MyApp\* are going away just fine, it is the .sys file in \windows\system32\drivers that is being left around when using the MSI to uninstall but not when using the INF directly. Forgive me if this is something very simple, I am new to MSI’s, WiX, and DIFx. What am I missing to make the .sys from the \windows\system32\drivers directory get deleted on uninstall as well?

        Thanks for your time.

  6. Excellent and essential. This information isn’t included in the WiX 3 doc as coherently and completely as it is here. Thanks!

    • I don’t have any problem using KMDF and DIFx. Your INF file MUST handle the installation of the appropriate KMDF co installer. Check the WDK for sample INFs that show how to do this. If your INF isn’t referencing Wdf then you’ve got problems.

  7. The Driver Install Frameworks (DIFx) tools were removed from the Windows 10 Version 1607 WDK. Instead, we recommend providing your driver as a standalone package that doesn’t require an installer, ideally through Windows Update. To add your driver to Windows Update, the first step is to submit your driver package to the Sysdev Driver Portal.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s