Introduction

This article describes my C# class called DriveDetector which will allow your program to receive a notification when a removable drive (such as a flash drive) is inserted or removed. It also supports notifications about pending removal of such a device and cancelling this removal.

This is an updated version of the article first published in March 2007. The class is now easier to use — there is no need to specify a file to open on the flash drive or override WndProc in your code. For details please see the What's new section at the bottom of this page.

Note that this class uses .NET Framework 2.0. It will not work with older versions. Also, it will only work in applications which have a window (not in console applications).

Recently, I wrote a program which allows the user to encrypt data on his/her flash drive. The program should decrypt and encrypt the data transparently when flash drive is inserted/removed. For this I needed to be informed when a flash drive is plugged in and when the user decides to remove it. I am quite new to C# so I started searching the internet for some solution. It didn't take me long to find out how to detect when a removable drive is inserted or removed, but I had a hard time trying to figure out how to get notified when the drive is about to be removed (when user clicks the remove hardware icon in system notification area). After making it all work I decided to put the code into a simple-to-use class and make it available to everyone. I hope you will find it useful. It's far from perfect but it works.

Background

In this section I will describe some of the principles on which the removable drive notifications work. If you just need to use this class without spending much time learning how it works, feel free to skip to the "Using the Code" section.

Windows will send WM_DEVICECHANGE message to all applications whenever some hardware change occurs, including when a flash drive (or other removable device) is inserted or removed. The WParam parameter of this message contains code which specifies exactly what event occurred. For our purpose only the following events are interesting:

DBT_DEVICEARRIVAL - sent after a device or piece of media has been inserted. Your program will receive this message when the device is ready for use, at about the time when Explorer displays the dialog which lets you choose what to do with the inserted media.

DBT_DEVICEQUERYREMOVE - sent when the system requests permission to remove a device or piece of media. Any application can deny this request and cancel the removal. This is the important event if you need to perform some action on the flash drive before it is removed, e.g. encrypt some files on it. Your program can deny this request which will cause Windows to display the well-known message saying that the device cannot be removed now.

DBT_DEVICEREMOVECOMPLETE - sent after a device has been removed. When your program receives this event, the device is no longer available — at the time when Windows display its "device has been removed" bubble to the user.

To handle these events in your program you need to be able to process the WM_DEVICECHANGE messages. In Windows Forms applications, you can override the WndProc function which is available in any class derived from Windows.Forms.Control. Since the Control class is the "great-grand-father" of your Form class, overriding it is simply a matter of adding these lines to the Form1.cs file in your project:

protectedoverridevoid WndProc(ref Message m)
{
base.WndProc(ref m);
}

This implementation does nothing, just calls the base class. But by adding some code, we can easily check out the messages which we receive in the ref Message m argument. Processing the messages is as easy as this:

Now maybe you are thinking if it is as easy as this, what's all this about. Well, there are two problems:

You will not only want to know that some device arrived, but also what device it is, if it's a removable drive what drive letter it got assigned and so on. For this you have to dig into the Win API structure, a pointer to which you receive in the m.LParam.

To make things more complicated, the most interesting event for us, DBT_DEVICEQUERYREMOVE, is not automatically sent to all applications — unlike DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE. If you try the above code and test the m.WParam it will never have the value DBT_DEVICEQUERYREMOVE. To also receive the DBT_DEVICEQUERYREMOVE event, you need to register with the system using RegisterDeviceNotification API. This function is defined as follows in the platform SDK:

Calling native API is not too hard but the tricky part for me was the NotificationFilter parameter about which SDK documentation says it is "pointer to a block of data... This block always begins with the DEV_BROADCAST_HDR structure. The data following this header is dependent on the value of the dbch_devicetype member...". Sounds scary, doesn't it? Well, the thing is if you want to receive the DBT_DEVICEQUERYREMOVE event for a removable drive, you need to open a file on the drive and pass a handle to this file to the RegisterDeviceNotification function. If you want to see the resulting code in C#, look at RegisterForDeviceChange in DriveDetector.cs.

To sum it all up, when a flash drive is inserted, Windows will send WM_DEVICECHANGE to your program with the wParam equal to DBT_DEVICEARRIVAL. Now you can open a file on the flash drive and call RegisterDeviceNotification native API function passing the handle to it. Only if you did this will your program will receive the DBT_DEVICEQUERYREMOVE event when the flash drive is about to be removed and you can respond to this. At least you have to close the file which you opened otherwise the removal attempt will fail and Windows will display a message saying that the drive cannot be removed now.

The DriveDetector class is intended to do most of the above-described work for you. It provides events which your program can handle for the device arrival, removal and query remove. All you have to do is to override WndProc in your program and call DriveDetector's WndProc method from there. The usable fields and methods of this class are described at the end of this article. Let's now look at simple example of use.

Using the Code

Here are the steps needed to add this functionality into your program without worrying about how it works:

Implement these handlers as shown here. You can simply copy and paste this block of source code to your Form class.

// Called by DriveDetector when removable device in inserted
privatevoid OnDriveArrived(object sender, DriveDetectorEventArgs e)
{
// e.Drive is the drive letter, e.g. "E:\\"
// If you want to be notified when drive is being removed (and be
// able to cancel it),
// set HookQueryRemove to true
e.HookQueryRemove = true;
}
// Called by DriveDetector after removable device has been unplugged
privatevoid OnDriveRemoved(object sender, DriveDetectorEventArgs e)
{
// TODO: do clean up here, etc. Letter of the removed drive is in
// e.Drive;
}
// Called by DriveDetector when removable drive is about to be removed
privatevoid OnQueryRemove(object sender, DriveDetectorEventArgs e)
{
// Should we allow the drive to be unplugged?
if (MessageBox.Show("Allow remove?", "Query remove",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) ==
DialogResult.Yes)
e.Cancel = false; // Allow removal
else
e.Cancel = true; // Cancel the removal of the device
}

That's all. Your event handlers should now be called whenever a flash drive is inserted or removed.

In this default implementation DriveDetector will create a hidden form, which it will use to receive notification messages from Windows. You could also pass your form to DriveDetector's constructor to avoid this extra (although invisible) form. To do this, just create your DriveDetector object using the second constructor:

driveDetector = new DriveDetector(this);

But then you also have to override WndPRoc method in your Form's code and call DriveDetector's WndProc from there. Again, you can just copy-paste the code below to your Form1.cs.

Even though DriveDetector hides all the background from you, it may be helpful if you know the following things:

Your program must register to receive a message from Windows when the drive is about to be removed (DBT_DEVICEQUERYREMOVE message). DriveDetector does this automatically if you set the HookQueryRemove flag to true in the DetectorEventArgs argument of your DeviceArrived handler (e.HookQueryRemove) — as is shown in the sample code above.

For this registration handle to a file located on the removable drive is required.

If you do not provide the name of the file to be opened, DriveDetector opens the root directory of the flash drive (e.g. E:\). This should work fine in most cases. If you for any reason want to specify your own file to be opened to obtain the notification handle, you can do so either in a DriveDetector constructor or using the EnableQueryRemove method as described below. Specify the file to be opened either in the DriveDetector constructor or using the EnableQueryRemove method as described below.

The demo project "Simple Detector" illustrates using the DriveDetector class. When a removable drive is inserted it will report this event in the list. If you check the "Ask me before drive can be disconnected" box, a message box will pop out asking whether the removal should be allowed. The code of this demo is pretty much the same as in the examples above. The event handler for the "Ask me..." checkbox demonstrates using EnableQueryRemove method to register for DBT_DEVICEQUERYREMOVE as an alternative to setting the e.HookQueryRemove flag in DeviceArrived event handler.

What's New

I decided to update Drivedetector after trying to use it in some real project and finding out that I needed something more usable. So I took the suggestions from the posts for this article and implemented two major improvements:

DriveDetector can now open the root directory instead of a file to obtain the handle required for query remove notification. You do not have to specify any file and you can be sure all files you need on the flash drive are accessible.

DriveDetector can now create a hidden form instead of relying on a Control object passed to the constructor. As a result it's not necessary to override WndProc in the client code.

The class should be backward compatible, so if you are using it in your project, you should be able to simply replace the DriveDetector.cs file and have it work as before. But if you decide to update your code, you will probably have some reason. Perhaps the same I had — opening some file on the flash drive to be notified about pending removal is annoying and causes trouble with sharing when you need to update the opened file. This new version which opens the root directory is much easier to use.

So what needs to be changed? Simply do not specify any file. If your code uses the constructor with file name, just replace it with the constructor without it. If you specify a file in the EnableQueryRemove call, use only the drive instead of your file path. That's all.

DriveDetector Quick Reference

Here is a summary of the fields and methods DriveDetector offers.

Events Signaled by the DriveDetector Class

DeviceArrived - removable drive has just been inserted.

DeviceRemoved - removable drive has just been removed.

QueryRemove - removable drive is about to be removed. Note that to receive this event you must set the HookQueryRemove flag to true in the argument in your DeviceArrived handler (e.HookQueryRemove) or call EnableQueryRemove method. See the sample code above.

All these events are of type DriveDetectorEventHandler and the event handlers receive DriveDetectorEventArgs argument which contains following fields:

string Drive - in the DeviceArrived and DeviceRemoved event handlers this string contains the drive letter of the drive which has been inserted or removed, e.g. "E:\\". In the QueryRemove handler this field will read an empty string.

bool HookQueryRemove - set to true in your DeviceArrived event handler if you want to receive the QueryRemove event for the drive which just arrived.

bool Cancel - set to true in your QueryRemove handler if you want to cancel the removal of the drive.

Public Properties

bool IsQueryHooked - Is true if any drive is currently "hooked" for the DBT_DEVICEQUERYREMOVE event. If this property is true, it means your QueryRemove event handler will be called. The drive which is hooked can be obtained from HookedDrive property.

string HookedDrive - drive letter of the drive which is currently hooked, e.g. "E:\\". Empty string if no drive is hooked.

FileStream OpenedFile - FileStream object for the file which is currently opened (and its handle used for DBT_DEVICEQUERYREMOVE notification). Note that this is normally null as DriveDetector opens the root directory of the flash drive unless you specify a valid file in the constructor or in call to EnableQueryRemove.

Public Methods

bool EnableQueryRemove(string fileOnDrive) - Hooks drive specified in the fileOnDrive argument to receive notification when it is being removed. This can also be achieved by setting e.HookQueryRemove to true in your DeviceArrived event handler. The fileOnDrive argument is drive letter or relative path to a file on the removable drive which should be opened (opening the file is required to obtain notifications about removal). If you specify drive letter only, DriveDetector will open the root directory of the drive and use its handle.

DisableQueryRemove() - Unregisters from the notification for currently registered drive, if any.

Possible Improvements

Extend the class to also handle the DBT_DEVICEQUERYREMOVEFAILED event. In the current version, if another program cancels the removal of a drive which we've already allowed, we will lose track of this drive.

Improve the class so that it can monitor several drives for query remove. Currently only one drive can be registered for this notification so if a new drive arrives and QueryRemove is requested for it, DriveDetector will unregister the previous one.

Points of Interest

There are actually two ways of denying removal of a removable drive. You can simply keep some file open on the drive in your program and system will not allow the drive to be removed. But if your program also returns the proper value in response to DBT_DEVICEQUERYREMOVE event, system will display a nice message including the name of the application which denies the removal. DriveDetector does set the proper response value in the message structure and that is why the DriveDetector's WndProc function is called only after the base class call in the sample code. If you called the base class last, it would reset the response in the message structure.

Comments and Discussions

Good afternoon Jan,My name is Jose and I am developing a tool for my company which analyses every USB device plugged to the system and if it doesn't fulfill some criteria , it is ejected automatically.Doing some search on internet I ended using two little projects which do exactly what I need.

- For USB plugging detection,your excellent code here.- For ejecting an USB drive, the code from other entry of codeprojecthttp://www.codeproject.com/Articles/13530/Eject-USB-disks-using-C">I just integrated both in a single .NET solution and I simply add an "eject" command every time I plugged a new USB device.

My problem appears when I plug 2 devices at the same time, the program only manages to eject the first device. It seems like that the ejection of the first USB drive also removes the "onArrrived" event coming from the second drive. As a result, the second drive keeps accessible what I don't want to happen.

Any idea of what might be happening?

I could send the .net solution in case someone want to reproduce the issue.

Hello Mr. Dolinay , I’ve recently downloaded from codeproject.com a class C# written by you, Driverdectector.cs, first of all, I must emphasize and recognize his great knowledge in programming, I am beginner but I am developing a small application to let me count the Megabyte copied to USB devices connected to a PC. The class written by you is very useful for me. I found it great to be able to write code on the OnDriveArrived, OnDriveRemoved, OnQueryRemove events. But as you yourself acknowledge in his article this class does not allow control of OnQueryRemove event for all detected devices even when the property HookQueryRemove is set to True, however I need to do this. What do you suggest me ?, I would greatly appreciate your comment ...Thanks a lot for your cooperation.

Hello, Thanks. If I understand your question well, you need to track several devices. This code only tracks one device. Please see the post "Can´t hook more than one device Pin" (from 5-Apr-09 5:03) in the discussions for this article. I hope it will help.

Thank you for your answer, I just have read your post "Can´t hook more than one device Pin", I have undertands but i recognize i will be very dificult to implement for me. Could you give me more details to help me to solve my problem. Thank

Sorry, but the explanation I wrote in the older post is about all support I can provide for this. The article is rather old, I now don't even have the Visual C# IDE installed... It should not be too difficult to modify the code if you give yourself some time to understand how it works. Another option is to look for a different solution. The .NET framework is still improved and perhaps there is now some "native" way to detect USB drives without the need for the solution described in this article.

Hi, the file is on the server for several years and there was no virus so far. So I think, either somebody hacked the codeproject server and infected the file or the virus sits in your computer and infects files which you download. The latter seems more likely.

Hi, U r Article is working in a domain where usb is unblocked by symantec antivirus system but in blocked systems when i insert the usb pendrive and u r application is simulataneously running its not displaying any device arrival/removed or any messages .can u help me to sort this problem ASAP.Urgent.

Hi,Sorry, I think I cannot help you much. I can see 2 posibilities: a) the system does not send the messages to the application at all - this would be strange, could be something with the antivirus.b) the application gets the message but does not handle it. You can debug the application and see if you get any message from system when the usb is connected. If yes, you can look at the parameters of the message and step through the code to see why the application does not display any message. For example, if the usb is not a disk drive, it will not be reported by the application.

If I keep the break point, I can see the WndProc method is hit multiple times at the launch of the application as well as when even I bring back the application in focus. Is there any way to handle it better than this?

The WndProc is responsible for handling all the messages sent to your window, including those about the USB devices (http://en.wikipedia.org/wiki/WindowProc). So this is expected behaviour. The switch for various messages then decides what to do. I don't think there is any other way to handle this. For easier debugging you can place the breakpoint(s) into the switch, or inside the "if" in the switch, if you mean multiple messages of the DBT_DEVICEARRIVAL type.

Sorry, but I do not understand the question. Are you asking a) about how is something done in the existing code or b) how to do your own version without using a form.If b) then I think it is not easy, because the notifications about USB devices are sent by Windows only to "windows", so program without a window (a form) will not receive them. There should be some way, which can be used in services, but I do not know the details. I think it was discussed somewhere in this forum long time ago - please have a look. It was probably related to detecting USB devices from Windows services.

Hello,In the older question I wasn't able to reproduce the problem, but I had only 64-bit Windows 7 at that time. Now I tried it on 32-bit system and it works OK. Here is what I did:1) downloaded Visual C# express 2010 from Microsoft website and install it.2) download the demo project (simple detector) from this article and extract it into visual studio projects folder.3) Opened the project in VC# 2010 - it asked for conversion because the project was created with older version. 4) I allowed the conversion and it ended successfully5) I started the program6) I tried my flash disk (16 GB) and a card reader with SD card, both worked as expected including the option to deny removal of the drive.

So, it works for me. I can think of some possible problems:1) diffferent version of Visual Studio (2013?) - I didn't try it, my computer is rather old 2) Too new version of the .NET framework - I used version 4, default in VC# 2010. Try to use some older version in the project properties.3) using the demo with something else than mass storage device.

If you provide some more information than that it just "does not work", maybe I or someone else can help If not, please don't be angry . The article is more than 6 years old; I don't use C# for long time now and the .Net framework surely also evolved a lot. Maybe there is now native and comfortable way to do the same thing in the framework itself.

Thank you for coming back to me so quickly! Sorry for not replying sooner, I was working on a different project the last couple of days!I also guess it should be a version problem!! I'll make it run on my PC!Thanks!

i am accessing my database from pendrive so i wants to do is if pendrive not attached then its display message "insert pendrive" and if pendrive inserted then its access from that pendrive so how to do that please guide me.

A very good article. My vote is 5 I know its an very old article and i am very late to ask the question but i didnt find any other blog or post on internet that matches with my requirement.

I need your help to implement some additional features in my application My requirement is -If someone copy some data to USB then what are the different events that we can write to capture the copied dataand the event by which we can track the data transferred from USB.

I have 2 versions of smart cards I need to detect insertion and removal for. One version is integrated into a 'dongle' and the other is a reader into which a 'real' smart card is inserted.On the second version the card being inserted into the reader is the most important event, not the insertion of the reader.

Most of the info I have found only deals with drives. There must be something more generic around.

I just tested it on Windows 7 (home premium 64 bit) and it works. I imported the project to Visual C# 2010 Express first, so this could be one reason - maybe it does not work if built with older version. Or it could be in the flash drive... if it is recognized by windows as a disk, it should also be detected by DriveDetector.

Hi, I'm pretty new to the scene and I am slightly nervous and curious about licensing. I am making a program that is going to be used by a business, however I am not being paid for it. I was just curious how I would go about giving you the credit for the great work that you have done. ThanksChris