Client Context Management in WPD Drivers
A WPD driver provides the communication channel between applications and the physical device. There can be multiple WPD applications running at any time, and the driver needs to handle requests from different clients and identify the clients based on the queued requests. In other words, the driver needs an efficient and easy way to store client data on a per-connection basis, and retrieve the data per request. Fortunately, UMDF supports context areas, a generic mechanism to save data with a framework object. A WPD driver can allocate a data structure or object to hold the data, assign it to the framework object's context area, and retrieve the context at a later time. The appropriate per-connection WDF framework object to use is the WDF file object.
Step 1: Assigning the Context
The driver assigns the context when the client opens a connection to it. When a WPD application calls IPortableDevice::Open, the WPD API creates a handle to the driver using Win32 CreateFile. Under the hood, UMDF initializes an IWDFFile object and forwards it, along with the Creation request, to the driver's IQueueCallbackCreate::OnCreateFile method. The IWDFFile in this case represents a Win32 HANDLE that is used for subsequent communication from this client to the driver.
A example of a CreateFile callback implementation is WpdWudfSampleDriver's CQueue::OnCreateFile. A driver-specific ContextMap COM object is used to store client data (application name, version, in-progress enumeration and resource contexts, etc). Note that the use of COM objects as context data is NOT required by UMDF - UMDF sees the context data as an opaque PVOID. If you are using a COM object for storing context data, your driver needs to maintain the reference count for that COM object, and ensure that its resources are freed in the appropriate cleanup methods.
To save context data, the driver initializes a new ContextMap object, and calls IWDFObject::AssignContext for the IWDFFile object handed in by UMDF. The parameters for AssignContext are the pointers to an IObjectCleanup object [containing the context cleanup code], and the newly-created ContextMap [containing the data to store]. IObjectCleanup::OnCleanup will be called when the file object is destroyed during CloseHandle. See "Step 3" for further details on how to implement OnCleanup.
In addition, only one context can be assigned to the file object (or any UMDF framework object). Subsequent calls to AssignContext will fail if a context has already been assigned. To add/remove client-specific data dynamically, one way is to implement a mapping object for managing the data (e.g. WpdWudfSampleDriver's ContextMap object), and assign a pointer to that mapping object as file object's context.
Step 2: Retrieving and Saving Context Information
To access the client data during requests, the WPD driver gets the context from the IWDFFile object.
The sequence is:
- Call IWDFIoRequest::GetFileObject to get the IWDFFile object.
- Call IWDFObject::RetrieveContext on the IWDFFile object to access the context area. In the sample driver, this will be the pointer to the ContextMap object that was created in CQueue::OnCreateFile during IPortableDevice::Open.
- Add/remove data to the ContextMap object directly when processing the WPD commands. Each client connection (i.e. IPortableDevice::Open) will have its own IWDFFile object and ContextMap object.
Example code from WpdWudfSampleDriver:
- CQueue::OnDeviceIoControl - Retrieving the context map from the WDF request's file object
- WpdBaseDriver::OnSaveClientInfo - Adding client information to the context map when processing the WPD_COMMAND_COMMON_SAVE_CLIENT_INFORMATION command
Step 3: Cleaning up the Context
When the client application calls IPortableDevice::Close, the WPD API will in turn call CloseHandle on the Win32 handle associated with that open connection. Before destroying the IWDFFile object in response to the CloseHandle, UMDF calls the file object's IObjectCleanup::OnCleanup method that the driver passed into AssignContext during OnCreateFile.
An example implementation of the IQueueCleanup callback is WpdWudfSampleDriver's CQueue::OnCleanup. This method retrieves the ContextMap stored in the IWDFObject object (in this case, the instance of IWDFFile from OnCreateFile) and frees the allocated memory, including the objects that the ContextMap holds. To avoid memory leaks, ensure that the objects are properly cleaned up, and (if applicable) decrement the reference count.
A great WDF book is Developing Drivers with WDF by Orwick/Smith. Chapter 5 (pages 124-125) covers techniques for object-specific context data storage using UMDF. The WpdWudfSampleDriver sample code is available in the Windows Driver Kit.
This posting is provided "AS IS" with no warranties, and confers no rights.