Click here to Skip to main content
Licence CPOL
First Posted 19 Jun 2007
Views 50,841
Downloads 1,095
Bookmarked 51 times

Detecting when drives are added or removed

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.

ON_WM_DEVICECHANGE ()

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

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)

About the Author

Iain Clarke, Warrior Programmer

Software Developer (Senior)

Sweden Sweden

Member

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!

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 5 Pinmembermanoj kumar choubey0:02 18 Feb '12  
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? PinmemberlilyNaz20:15 25 Oct '11  
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? PinmemberIain Clarke, Warrior Programmer4:27 12 Dec '11  
GeneralShame : copy from MSDN ... Pinmemberkilt23:32 8 Oct '09  
GeneralRe: Shame : copy from MSDN ... PinmvpIain Clarke, Warrior Programmer4:31 13 Oct '09  
QuestionFound a bug on Win2000 Pinmemberwangk07052:54 29 Dec '07  
GeneralRe: Found a bug on Win2000 PinmemberIain Clarke3:55 1 Feb '08  
GeneralDBT_DEVICEQUERYREMOVE support Pinmember64BitWho1:13 12 Oct '07  
GeneralRe: DBT_DEVICEQUERYREMOVE support PinmemberIain Clarke4:12 12 Oct '07  
GeneralRe: DBT_DEVICEQUERYREMOVE support Pinmember64BitWho4:48 12 Oct '07  
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 Pinmember64BitWho4:23 13 Oct '07  
QuestionRe: DBT_DEVICEQUERYREMOVE support PinmemberMember 43522242:10 4 Sep '08  
GeneralRe: DBT_DEVICEQUERYREMOVE support PinmemberMember 43522242:20 4 Sep '08  
GeneralRe: DBT_DEVICEQUERYREMOVE support PinmemberIain Clarke22:40 7 Sep '08  
GeneralRe: DBT_DEVICEQUERYREMOVE support PinmemberIain Clarke22:43 7 Sep '08  
GeneralRe: DBT_DEVICEQUERYREMOVE support PinmemberMember 435222423:52 7 Sep '08  
JokeRe: DBT_DEVICEQUERYREMOVE support PinmemberMember 229365218:23 17 Sep '09  
QuestionWM_DEVICECHANGE Ok! But what about Linux? PinmemberPeterShilkin1:11 12 Jul '07  
AnswerRe: WM_DEVICECHANGE Ok! But what about Linux? PinmemberIain Clarke3:53 14 Jul '07  
GeneralMissing file PinmemberXaRoP7:08 4 Jul '07  
GeneralRe: Missing file PinmemberIain Clarke10:35 4 Jul '07  
GeneralRe: Missing file PinmemberIain Clarke22:21 4 Jul '07  
QuestionWhat about insert devices PinmemberVC Spark2:28 3 Jul '07  
AnswerRe: What about insert devices PinmemberIain Clarke4:16 3 Jul '07  
GeneralRe: What about insert devices PinmemberVC Spark2:32 4 Jul '07  

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

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120529.1 | Last Updated 5 Jul 2007
Article Copyright 2007 by Iain Clarke, Warrior Programmer
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid