A stern warning
Do not attempt to "install" a filter by manually writing registry keys. As you've noticed, it's not easy, and even if you seem to get it working, it will all collapse when the OS tries to install the next LWF. Furthermore, I added some additional hardening features designed exactly to prevent people from doing this to Windows 10; you'll have to do some significant damage to the OS before you can hijack network bindings in Windows 10.
How to structure your driver package
Anyway, what you're describing is indeed possible. The way to do it is to provide the following in your driver package:
- A PNP-style INF. This INF has:
- The
PORTS
class
- An
AddService
directive, that installs your driver service
- A
CopyFiles
directive to bring in any files you need
- Any other bits you need for the PNP device
- A NetCfg-style INF. This INF has:
- The
NETSERVICE
class
- The usual LWF stuff:
Characteristics=0x40000
, FilterMediaTypes=xxx
, FilterType=xxx
, etc.
- A reference to the service you installed in the other INF (
HKR,Ndi,Service,,xxx
)
- Do not include an
AddService
or CopyFiles
; that's already taken care of by the first INF
- One .sys file. This driver does:
- In
DriverEntry
, call NdisFRegisterFilterDriver
, and pass the name of your service "xxx"
- In
DriverEntry
, call WdfDriverCreate
or fill out the DRIVER_OBJET
dispatch table as you normally would for any other PNP driver
- Implement
FilterAttach
and etc normally; implement your WDF EvtXxx or WDM IRP handlers normally
- Don't forget to call
NdisFDeregisterFilterDriver
in EvtDriverUnload
or DriverUnload
, and also in the failure path for DriverEntry
How to install this fine mess
The good news is that, with these 2 INFs, you can meet your requirement of having 1 .sys file do two things. The bad news is that you've now got 2 INFs. Worse, one of the INFs is a NetCfg-style INF, so you can't just Include
+Need
it. The only way to install a NetCfg-style INF is to call INetCfgClassSetup::Install
(or NetCfg.exe
, its command-line wrapper). Windows Update only knows how to install PNP-style INFs, and PNP only knows how to Include
other PNP-style INFs.
So the simplest solution is to ship an installer exe/msi that invokes the INetCfg API. If you can do that, it's simply a matter of a couple calls to SetupCopyOemInf
and the INetCfg
boilerplate that you can find in the bindview sample.
But, if you have to support a hardware-first installation, you need to bring out the big guns. You'll need to write a Co-Installer and include it with your driver package. The Co-Installer's job is to call the INetCfg
APIs when your driver package is installed, and deregister when the package is uninstalled.
Co-Installers are generally discouraged, and are not supported for Universal drivers. So you should avoid a Co-Installer unless you've got no choice. Unfortunately I cannot think of any other way to register an NDIS LWF when a PNP device driver is installed through Windows Update. (This doesn't mean there isn't a crafty way to do it; I don't know everything.)
Note that you'd need a Co-Installer anyway even if you were shipping 2 .sys files. The need to call INetCfg
doesn't change just because you merged the driver binaries.
Limitations
You'll have a full-fledged NDIS LWF driver, as well as a full-fledged PNP device driver. The only (minor) thing that doesn't work is that you cannot call NdisRegisterDeviceEx
in this driver. The reason is that when you call NdisRegisterDeviceEx
from a LWF, NDIS will attempt to co-opt your driver's dispatch table. But in this PNP+LWF dual driver, the dispatch table is owned by WDF or by you. This limitation is no problem, since you can call WdfDeviceCreate
, and this routine is easier to use and has more features than the NDIS one anyway.
With the above configuration, the driver service is owned by PNP. That means the lifetime of your .sys file is owned by PNP. You cannot manually "net start" a PNP driver service; the only way to get your .sys file loaded is to actually enumerate your hardware. That means you can't have your NDIS LWF running when the hardware is not present. Typically this is what you'd want anyway. If it's not, you can try messing with the ServiceName directive, but there's some weird caveats with that, and I don't fully understand it myself.