Using Device Interfaces in UMDF Drivers
Warning
UMDF 2 is the latest version of UMDF and supersedes UMDF 1. All new UMDF drivers should be written using UMDF 2. No new features are being added to UMDF 1 and there is limited support for UMDF 1 on newer versions of Windows 10. Universal Windows drivers must use UMDF 2.
The archived UMDF 1 samples can be found in the Windows 11, version 22H2 - May 2022 Driver Samples Update.
For more info, see Getting Started with UMDF.
A device interface is a symbolic link to a Plug and Play (PnP) device that an application can use to access the device. A user-mode application can pass the interface's symbolic link name to an API element, such as the Microsoft Win32 CreateFile function. To obtain a device interface's symbolic link name, the user-mode application can call SetupDi functions. For more information about SetupDi functions, see SetupDi Device Interface Functions.
Each device interface belongs to a device interface class. For example, a driver stack for a CD-ROM device might provide an interface that belongs to the GUID_DEVINTERFACE_CDROM class. One of the CD-ROM device's drivers would register an instance of the GUID_DEVINTERFACE_CDROM class to inform the system and applications that a CD-ROM device is available. For more information about device interface classes, see Introduction to Device Interfaces.
Registering a Device Interface
To register an instance of a device interface class, a UMDF-based driver can call IWDFDevice::CreateDeviceInterface from within its IDriverEntry::OnDeviceAdd callback function. If the driver supports multiple instances of the interface, it can assign a unique reference string to each instance.
Enabling and Disabling a Device Interface
If creation succeeds, the framework automatically enables and disables the interface based on the device's PnP state.
In addition, a driver can disable and re-enable a device interface as necessary. For example, if a driver determines that its device has stopped responding, the driver can call IWDFDevice::AssignDeviceInterfaceState to disable the device's interfaces and prohibit applications from obtaining new handles to the interface. (Existing handles to the interface are not affected.) If the device later becomes available, the driver can call IWDFDevice::AssignDeviceInterfaceState again to re-enable the interfaces.
Receiving Requests to Access a Device Interface
When an application requests access to a driver's device interface, the framework calls the driver's IQueueCallbackCreate::OnCreateFile callback function. The driver can call IWDFFile::RetrieveFileName to obtain the name of the device or file that the application is accessing. If the driver specified a reference string when it registered the device interface, the operating system includes the reference string in the file or device name that IWDFFile::RetrieveFileName returns.
Creating Device Events
Your UMDF-based driver can create device-specific, custom events (called device events) by calling IWDFDevice::PostEvent. A driver that has registered to use any of the device's interfaces can receive notifications of a device's custom events. UMDF-based drivers receive such notifications by providing an IRemoteInterfaceCallbackEvent::OnRemoteInterfaceEvent callback function.
Custom events are unique to the device. Both the developer of the driver that creates the event and the developer of the driver that receives the event must understand the meaning of the event.
Accessing Another Driver's Device Interface
If you want your UMDF-based driver to send I/O requests to a device interface that another driver provides, you can create a remote I/O target that represents the device interface.
First, your driver must register to receive a notification when a device interface is available. Use the following steps:
When your driver calls IWDFDriver::CreateDevice, the driver can provide an IPnpCallbackRemoteInterfaceNotification interface. The IPnpCallbackRemoteInterfaceNotification::OnRemoteInterfaceArrival callback function of this interface informs your driver when device interfaces are available.
After your driver calls IWDFDriver::CreateDevice, it can call IWDFDevice2::RegisterRemoteInterfaceNotification for each device interface that the driver will use.
Subsequently, the framework calls the driver's IPnpCallbackRemoteInterfaceNotification::OnRemoteInterfaceArrival callback function each time that a specified device interface becomes available. The callback function can call IWDFRemoteInterfaceInitialize::GetInterfaceGuid and IWDFRemoteInterfaceInitialize::RetrieveSymbolicLink to determine which device interface has arrived.
Your driver's IPnpCallbackRemoteInterfaceNotification::OnRemoteInterfaceArrival callback function should typically do the following:
Call IWDFDevice2::CreateRemoteInterface to create a remote interface object, optionally providing IRemoteInterfaceCallbackEvent and IRemoteInterfaceCallbackRemoval interfaces.
Call IWDFDevice2::CreateRemoteTarget to create a remote target object, optionally providing an IRemoteTargetCallbackRemoval interface.
Call IWDFRemoteTarget::OpenRemoteInterface to connect the device interface to the remote target.
If the device interface is one that the SWENUM software device enumerator creates, your driver must call OpenRemoteInterface from a work item. (For example, see the QueueUserWorkItem function in the Windows SDK.)
Now the driver can format and send I/O requests to the remote I/O target.
In addition to the IPnpCallbackRemoteInterfaceNotification::OnRemoteInterfaceArrival callback function, a UMDF-based driver can provide two additional callback functions to receive notifications of device interface events:
The IRemoteInterfaceCallbackRemoval::OnRemoteInterfaceRemoval callback function notifies the driver when a device interface is removed.
The IRemoteInterfaceCallbackEvent::OnRemoteInterfaceEvent callback function notifies the driver when a device's custom events arrive.