How to Receive WPD Arrival Event Notifications

If you followed the standard WPD events link in our previous post on WPD driver events, you may have seen the following event definition:

WPD_EVENT_DEVICE_REMOVED This event is sent when a driver for a device is being unloaded. This is typically a result of the device being unplugged.

Clients should release the IPortableDevice interface they have open on the device specified in WPD_EVENT_PARAMETER_PNP_DEVICE_ID.

In a nutshell, when a WPD device disconnects, IPortableDeviceEventCallback::OnEvent is called with this specific event GUID and the PnP Device ID in the event parameters, allowing applications to clean up the associated WPD interfaces for that device instance, display UI, etc. 

Having an event to tell an application when a device is disconnected can help applications build richer or smarter device scenarios.    Why isn't there a device arrival event, ala WPD_EVENT_DEVICE_ARRIVED, to cover the converse Disconnected -> Connected case?

The reason this hypothetical event doesn't exist is that WPD events utilize the IWDFDevice::PostEvent mechanism, which requires the WPD driver to up and running (with the device connected).    Consequently, to receive such events, a WPD application has to invoke IPortableDevice::Advise and maintain an open IPortableDevice connection.  

 

Device Interface Arrival Events to the Rescue

Fortunately, Plug-and-Play provides your application with the ability to receive broadcast device notifications on device interfaces, including WPD's.  This standard mechanism has existed long before WPD, and is documented here in MSDN.   The following code snippets show how to register for WPD device interface notifications, and how to handle the corresponding arrival event.

 //
// Step 1: Registering for WPD device interface PnP notification events
//

// The WPD device interface GUID
#define WPD_DEVINTERFACE L"{6ac27878-a6fa-4155-ba85-f98f491d4f33}"

GUID guidDevInterface            = GUID_NULL;
DEV_BROADCAST_DEVICEINTERFACE db = {0};
db.dbcc_size = sizeof(db);
db.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
CLSIDFromString(WPD_DEVINTERFACE, &guidDevInterface);
db.dbcc_classguid  = guidDevInterface;

hDevNotify = RegisterDeviceNotificationW(hWnd, &db, DEVICE_NOTIFY_WINDOW_HANDLE);

do error checking ...


//
// Step 2: Processing the DBT_DEVICEARRIVAL event when a WPD device is connected
//

LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{  
   switch (uMsg)
   {
       ....

       case WM_DEVICECHANGE:
       {
          switch(wParam) 
          {     
             case DBT_DEVICEARRIVAL:
             {
                 PDEV_BROADCAST_HDR pdbh = (PDEV_BROADCAST_HDR)lParam;
                 if (pdbh != NULL && pdbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
                 {
                     PDEV_BROADCAST_DEVICEINTERFACE pdbi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
            
                     if (IsEqualGUID(pdbi->dbcc_classguid, guidDevInterface)) 
                     {
                         // Initialization is skipped here for brevity
                         // Use the symbolic name to open a PortableDevice connection
                         // pClientInfo is an IPortableDeviceValues containing the client information
                         // NOTE: Use a separate thread if your code could block
                         hr = pIPortableDevice->Open(pdbi->dbcc_name, pClientInfo);

                         do more error checking ...
                     }
        }
                 break;         
             }
             
             ...
 
          } // switch (wParam)
       }
       break;
       ....
   } // switch (uMsg)
}

To summarize, the application will receive a WM_DEVICECHANGE message for the DBT_DEVICEARRIVAL event when the device is connected.   DEV_BROADCAST_DEVICEINTERFACE.dbcc_name is equivalent to the WPD PnP Device ID, which the application can use when calling IPortableDevice::Open.

The guidelines for processing Plug-and-Play messages apply: the event handler should process the event as quickly as possible, using a separate thread if there's a possibility of blocking execution, to avoid undue system hangs.     

 

Choices of Device Removal Events

The application will also receive a DBT_DEVICEREMOVECOMPLETE event, which can be useful for e.g. placing arrival and removal handlers in the same code module.   The difference between this event and WPD_EVENT_DEVICE_REMOVED is subtle.   The latter is a custom PnP event that is sent via WUDF during unloading of the WPD driver (usually caused by a device unplug); whereas DBT_DEVICEREMOVECOMPLETE is triggered by the removal of a device interface (also caused by a device unplug).   Usually, WPD applications that just want a notification when WPD devices are disconnected can subscribe to either event and get the same effect.

 

Further Reading (and more code samples)

To learn more on how to use PnP device notifications, refer to this detailed article from The Code Project.

 

This posting is provided "AS IS" with no warranties, and confers no rights.