Click here to Skip to main content
15,908,172 members
Articles / Desktop Programming / MFC

Detecting when drives are added or removed

Rate me:
Please Sign up or sign in to vote.
4.30/5 (22 votes)
4 Jul 2007CPOL1 min read 113.8K   3.2K   57   37
Dealing with the WM_DEVICECHANGE message to detect volumes being added or removed.

Screenshot - Drive_Detect.jpg

Introduction

This is possibly my smallest article, and just shows a method of detecting when drives are added to or removed from the system. What you do with that information is up to you!

Background

I was answering a question from Akin Ocal yesterday, when Matthew Faithful came up with a much deeper answer than mine.

In a fit of pique / curiosity, I thought I'd come up with a different way of doing what he described, using Windows messages to do the heavy lifting. As I had to do a bit of digging from barely remembered details, I thought I'd share the work with others.

Using the code

I created a blank MFC dialog project, and added a handler for WM_DEVICECHANGE. The MFC handler structure exists, but it could not be added with the Class Wizard, so I typed it in manually.

C++
ON_WM_DEVICECHANGE ()

I then added the actual handler, which does nothing except log the fact that a drive has been added or removed.

C++
BOOL CDriveDetectDlg::OnDeviceChange( UINT nEventType, DWORD dwData )
{
    BOOL bReturn = CWnd::OnDeviceChange (nEventType, dwData);

    DEV_BROADCAST_VOLUME *volume = (DEV_BROADCAST_VOLUME *)dwData;
    CString log;

    if (nEventType == DBT_DEVICEARRIVAL)
    {
        if (volume->dbcv_devicetype == DBT_DEVTYP_VOLUME)
        {
            for (int n = 0; n < 32; n++)
            {
                if (IsBitSet (volume->dbcv_unitmask, n))
                {
                    log.Format ("Drive %c: Inserted\n", n + 'A');
                    m_DetectLog.AddString (log);
                }
            }
        }
    }

    if (nEventType == DBT_DEVICEREMOVECOMPLETE)
    {
        if (volume->dbcv_devicetype == DBT_DEVTYP_VOLUME)
        {
            for (int n = 0; n < 32; n++)
            {
                if (IsBitSet (volume->dbcv_unitmask, n))
                {
                    log.Format ("Drive %c: Removed\n", n + 'A');
                    m_DetectLog.AddString (log);
                }
            }
        }
    }

    return bReturn;
}

As you can see, there's not a vast amount of code for the job.

Updates

  • 27/6/2007 - Little drive icons added next to the drives being added / removed. The credit for steering me in the right direction (and providing some code for me to steal^h^h^h^h^hreuse) goes to dgendreau.
  • 20/6/2007 - Original version posted.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Sweden Sweden
I have now moved to Sweden for love, and recently married a lovely Swede.


-----------------
I started programming on BBC micros (6502) when I was six and never quite stopped even while I was supposed to be studying physics and uni.

I've been working for ~13 years writing software for machine control and data analysis. I now work on financial transaction transformation software, for a Software company in Gamlastan, Stockholm.
Look at my articles to see my excellent coding skills. I'm modest too!

Comments and Discussions

 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey18-Feb-12 0:02
professionalManoj Kumar Choubey18-Feb-12 0:02 
QuestionThis code does exactly what I need but the problem is with the MFC components used in it. I need to write the code in Embarcadero c++ builder, can anybody provide suggestions about how to modify the code? Pin
lilyNaz25-Oct-11 20:15
lilyNaz25-Oct-11 20:15 
AnswerRe: This code does exactly what I need but the problem is with the MFC components used in it. I need to write the code in Embarcadero c++ builder, can anybody provide suggestions about how to modify the code? Pin
Iain Clarke, Warrior Programmer12-Dec-11 4:27
Iain Clarke, Warrior Programmer12-Dec-11 4:27 
GeneralShame : copy from MSDN ... Pin
kilt8-Oct-09 23:32
kilt8-Oct-09 23:32 
GeneralRe: Shame : copy from MSDN ... Pin
Iain Clarke, Warrior Programmer13-Oct-09 4:31
Iain Clarke, Warrior Programmer13-Oct-09 4:31 
GeneralRe: Shame : copy from MSDN ... Pin
pnswdv5-Aug-14 5:11
pnswdv5-Aug-14 5:11 
QuestionFound a bug on Win2000 Pin
wangk070529-Dec-07 2:54
wangk070529-Dec-07 2:54 
GeneralRe: Found a bug on Win2000 Pin
Iain Clarke, Warrior Programmer1-Feb-08 3:55
Iain Clarke, Warrior Programmer1-Feb-08 3:55 
GeneralDBT_DEVICEQUERYREMOVE support Pin
64BitWho12-Oct-07 1:13
64BitWho12-Oct-07 1:13 
GeneralRe: DBT_DEVICEQUERYREMOVE support Pin
Iain Clarke, Warrior Programmer12-Oct-07 4:12
Iain Clarke, Warrior Programmer12-Oct-07 4:12 
GeneralRe: DBT_DEVICEQUERYREMOVE support Pin
64BitWho12-Oct-07 4:48
64BitWho12-Oct-07 4:48 
Since DBT_DEVICEQUERYREMOVE is sent when the 'safe removal' icon is clicked (before stopping a USB drive), it would allow one to react to a user who is going to unplug a drive. For example if you need to flush a file, (disclaimer: in my case, unload a virtual drive loaded from the USB drive) etc.

Unlike DBT_DEVICEREMOVECOMPLETE, one has to register to receive DBT_DEVICEQUERYREMOVE notifications. Here is how I am registering for the root of a drive, though I think one could pass any file handle on the drive.


BOOL CHelperFucntions::DoRegisterDeviceInterface(HWND hWnd, HANDLE hFile, bool bRegister)
{
	DEV_BROADCAST_HANDLE NotificationFilter;

	if(bRegister)
	{
		char szMsg[80];
		ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
		
		NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
		NotificationFilter.dbch_reserved = 0;
		NotificationFilter.dbch_nameoffset = 0;
		//NotificationFilter.dbch_data = NULL;
		//NotificationFilter.dbch_eventguid = 0;

		//This can be a file handle to the root of USB drive!
		NotificationFilter.dbch_handle = hFile;
		HDEVNOTIFY hDevNotify = NULL; //Perhaps this should be passed into the function by the caller?
		NotificationFilter.dbch_hdevnotify = hDevNotify;
	
		NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
		
		//hWnd is the handle to the main application window which will handle the incoming message.

		hDevNotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE); //or DEVICE_NOTIFY_ALL_INTERFACE_CLASSES


		if(!hDevNotify) 
		{
			wsprintf(szMsg, "Could not register to receive notifications about drive events: %d\n", 
				GetLastError());
			MessageBox(hWnd, szMsg, "RegisterDeviceNotification failed", MB_OK);        
			return FALSE;
		}
		return TRUE;
	}
}


Once registered, I am handling it just like any other in ::OnDeviceChange.


if (wParam == DBT_DEVICEQUERYREMOVE)
{
    PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam;
    //When an USB drive is inserted or removed, we get DBT_DEVTYP_DEVICEINTERFACE and DBT_DEVTYP_VOLUME.
    switch( pHdr->dbch_devicetype )
    {
    case DBT_DEVTYP_VOLUME:
        {
            PDEV_BROADCAST_VOLUME pDevVolume;
            ZeroMemory(&pDevVolume, sizeof(pDevVolume));
            pDevVolume = (PDEV_BROADCAST_VOLUME)pHdr;

            CString csMP = "";
            csMP = cHF.FirstDriveFromMask(pDevVolume->dbcv_unitmask);
            csMP = csMP + ":\\";

            // This is a drive we are interested in
            // because we have registered to receive notifications
            // for this drive.

            int iRet=0;
            iRet = MessageBox("The user wants to remove the " + csMP +" drive. Allow?", "Device Removal Permission", MB_YESNO + MB_ICONEXCLAMATION);
            if(iRet == IDYES || iRet == IDOK)
            {
                //Figure why we are interested in this drive
                //Do whatever is necessary to clean up our act
                //If we could not find any reason why, just return true.
                lpReturn = TRUE;

            }
            else
            {
                lpReturn = BROADCAST_QUERY_DENY;//if not ready to unload USB
            }


            break;
        }
    }
}


The FirstDriveFromMask( function is something I picked off MSDN

char CHelperFucntions::FirstDriveFromMask(ULONG unitmask)
{
   char i;

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

   return (i + 'A');
}


Please note this is a work in progress for me, I have been unable to get the correct drive letter out of the drive mask yet on the DBT_DEVICEQUERYREMOVE notification, though the fact that I receive the notification while removing the correct drive I have registered for tells me that the registration is correct and hence the drive mask should be correct.

As a programmer new to VC++ (just been coding for a couple month or so, and only a week of the MS IDE) I feel these things could do with better documentation in MSDN or elsewhere. Thanks for your efforts in that direction!


--
Omne Ignotum Pro Magnifico
GeneralRe: DBT_DEVICEQUERYREMOVE support Pin
64BitWho13-Oct-07 4:23
64BitWho13-Oct-07 4:23 
QuestionRe: DBT_DEVICEQUERYREMOVE support Pin
Vijay_Vijay4-Sep-08 2:10
Vijay_Vijay4-Sep-08 2:10 
GeneralRe: DBT_DEVICEQUERYREMOVE support Pin
Vijay_Vijay4-Sep-08 2:20
Vijay_Vijay4-Sep-08 2:20 
GeneralRe: DBT_DEVICEQUERYREMOVE support Pin
Iain Clarke, Warrior Programmer7-Sep-08 22:40
Iain Clarke, Warrior Programmer7-Sep-08 22:40 
GeneralRe: DBT_DEVICEQUERYREMOVE support Pin
Iain Clarke, Warrior Programmer7-Sep-08 22:43
Iain Clarke, Warrior Programmer7-Sep-08 22:43 
GeneralRe: DBT_DEVICEQUERYREMOVE support Pin
Vijay_Vijay7-Sep-08 23:52
Vijay_Vijay7-Sep-08 23:52 
JokeRe: DBT_DEVICEQUERYREMOVE support Pin
Member 229365217-Sep-09 18:23
Member 229365217-Sep-09 18:23 
QuestionWM_DEVICECHANGE Ok! But what about Linux? Pin
PeterShilkin12-Jul-07 1:11
PeterShilkin12-Jul-07 1:11 
AnswerRe: WM_DEVICECHANGE Ok! But what about Linux? Pin
Iain Clarke, Warrior Programmer14-Jul-07 3:53
Iain Clarke, Warrior Programmer14-Jul-07 3:53 
GeneralMissing file Pin
XaRoP4-Jul-07 7:08
XaRoP4-Jul-07 7:08 
GeneralRe: Missing file Pin
Iain Clarke, Warrior Programmer4-Jul-07 10:35
Iain Clarke, Warrior Programmer4-Jul-07 10:35 
GeneralRe: Missing file Pin
Iain Clarke, Warrior Programmer4-Jul-07 22:21
Iain Clarke, Warrior Programmer4-Jul-07 22:21 
QuestionWhat about insert devices Pin
VC Spark3-Jul-07 2:28
VC Spark3-Jul-07 2:28 
AnswerRe: What about insert devices Pin
Iain Clarke, Warrior Programmer3-Jul-07 4:16
Iain Clarke, Warrior Programmer3-Jul-07 4:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.