Why only one WM_DEVICECHANGE message when a multi-volume USB Device is removed?

3.4k views Asked by At

I'm writing an application that detects when a certain USB Mass Storage Device is plugged-in, and when it is unplugged - by listening for WM_DEVICECHANGE messages.

I've also registered my application to listen for WM_DEVICECHANGE messages for DBT_DEVTYP_DEVICEINTERFACE (using the RegisterDeviceNotification API call) and I get both the DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE messages when a USB Mass Storage Device is plugged-in or unplugged.

Now, the problem occurs when a USB device that has multiple volumes is plugged-in, and then unplugged.

I get the following messages when the device is plugged in:

  • WM_DEVICECHANGE (DBT_DEVICEARRIVAL of type DBT_DEVTYP_DEVICEINTERFACE)
  • WM_DEVICECHANGE (DBT_DEVICEARRIVAL of type DBT_DEVTYP_VOLUME)
  • WM_DEVICECHANGE (DBT_DEVICEARRIVAL of type DBT_DEVTYP_VOLUME)

And the following messages when it is plugged out:

  • WM_DEVICECHANGE (DBT_DEVICEREMOVECOMPLETE of type DBT_DEVTYP_VOLUME)
  • WM_DEVICECHANGE (DBT_DEVICEREMOVECOMPLETE of type DBT_DEVTYP_DEVICEINTERFACE)

So, only one remove message even though there are two volumes. Why??

I have two questions:

  • How can I correlate DBT_DEVTYP_DEVICEINTERFACE messages with DBT_DEVTYP_VOLUME messages (essentially, how do I know which VOLUME message corresponds to which DEVICEINTERFACE message - since I get them both for the device)?
  • Is there a way to make Windows notify me of both volume removals?
1

There are 1 answers

0
Mike Dinescu On BEST ANSWER

Ok, so I was able to answer one of my own questions: Is there a way to make Windows notify me of both volume removals?

Yes - even though windows sends only one DBT_DEVTYP_VOLUME WM_DEVICECHANGE message, you actually do get notified of both volume removals - but, as always, the answer lies deep down buried in MSDN:

Although the dbcv_unitmask member may specify more than one volume in any message, this does not guarantee that only one message is generated for a specified event. Multiple system components may independently generate messages for logical volumes at the same time.

So, all I had to do was ignore the example function that Microsoft gives in one of their samples,

char FirstDriveFromMask (ULONG unitmask)
{
   char i;

   for (i = 0; i < 26; ++i)
   {
      if (unitmask & 0x1)
         break;
      unitmask = unitmask >> 1;
   }

   return (i + 'A');
}

And replace it with a function that interprets the mask for all drives affected. So the one message I was getting was indeed for both volumes, and both volume drive letters were available in the mask.

// [IN] ULONG unitmask
// [IN/OUT] char* outDriveLetters  - an array of characters to be passed in
//                                   that is filled out with the drive letters
//                                   in the mask (this must be 26 bytes to be safe)
// RETURNS the number of drive letters in the mask
int MaskToDriveLetters (ULONG unitmask, char* outDriveLetters)
{
   int cnt = 0;
   for (i = 0; i < 26; ++i)
   {
      if (unitmask & 0x1)
      {
         outDriveLetters[cnt++] = 'A' + i;
         cnt++;
      }
      unitmask = unitmask >> 1;
   }

   outDriveLetters[cnt] = 0; // set the last character to \0 (optional)
   return cnt;  // the number of drives that were set in the mask
}

I still have the other question to answer though - how can the two messages (DBT_DEVTYP_DEVICEINTERFACE and DBT_DEVTYP_VOLUME) be correlated?