A UMDF driver communicates with some devices by using a Windows file handle, as in the following examples:
A device that is exposed through the Windows socket API.
A device that is emulated using Windows named pipes.
A device that is emulated using the Windows file system.
The driver obtains the file handle by calling a Windows function such as socket, CreateFile, or CreateNamedPipe that returns a file handle.
A driver can bind this handle to a framework I/O target by using the FileHandle I/O target. The driver can then use the framework's I/O target interface to send I/O requests to the file handle, thereby getting all the benefits of I/O targets.
The IWDFFileHandleTargetFactory interface creates an I/O target that is associated with a file handle. A driver gets a pointer to this interface by querying the framework device object. The driver can then call the CreateFileHandleTarget method and pass the file handle to create the I/O target object in the framework.
The framework device object is the parent of the I/O target object, so the lifetime of the I/O target object is by default the same as that of the device object. If the driver finishes using an I/O target object while the device object remains active, the driver can delete the I/O target object by calling IWDFObject::DeleteWdfObject.
Listing 9-16 shows how a UMDF driver creates a FileHandle I/O target that represents a named pipe.
Listing 9-16: Creating a file handle I/O target in a UMDF driver
HANDLE m_WriteHandle; // Device Handle IWDFIoTarget * m_WriteTarget; // I/O target HRESULT hr = S_OK; IWDFFileHandleTargetFactory * pFileHandleTargetFactory = NULL; // Create a pipe and get the handle. m_WriteHandle = CreateNamedPipe(NP_NAME, . . . //Additional parameters omitted for brevity); if (SUCCEEDED(hr)) { hr = m_FxDevice->QueryInterface(IID_PPV_ARGS(&pFileHandleTargetFactory)); if (SUCCEEDED(hr)) { hr = pFileHandleTargetFactory->CreateFileHandleTarget (m_WriteHandle, &m_WriteTarget); } } . . . //Additional code omitted SAFE_RELEASE(pFileHandleTargetFactory);
In the listing, the driver calls the Windows CreateNamedPipe function to open a handle to a named pipe. If this function succeeds, the driver queries the framework's device object for a pointer to the IWDFFileHandleTargetFactory interface. It then calls the CreateFileHandleTarget method, which creates a framework I/O target object that corresponds to the file handle and returns a pointer to the I/O target object's IWDFIoTarget interface. When the driver has finished using the IWDFFileHandleTargetFactory interface, it releases its reference on the interface.
After creating the I/O target, the driver can call methods on the IWDFIoTarget and IWDFIoTargetStateManagement interfaces to format I/O requests for the target device, get information about the target, and manage the state of the target.
You should use a file handle I/O target if your driver can create a Windows file handle for the target and your UMDF driver is not loaded as part of the device stack for the target devnode. For example, if your driver uses a socket, you should use a file handle I/O target. However, if your driver loads as part of the device stack for the target devnode, you should just use the default I/O target.
Although you can use the Windows API directly, you should consider using a file handle I/O target for the following reasons:
I/O flows through all the drivers in the UMDF device stack.
You get all the advantages of a WDF I/O target: the ability to manage the state of the target, I/O processing based on the target's state, and the ability to send I/O with various configurations, such as with or without a time-out or in a synchronous or asynchronous manner.
The I/O target coordinates I/O completion and cancellation, which can be tricky to get right especially if your driver handles multiple outstanding I/O requests.
For the same reasons, you should use an I/O target to communicate with the kernel-mode portion of your device stack.
-Praveen Rao, Windows Driver Foundation Team, Microsoft