Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / Win32

Capturing Device Events in a C# Windows Service

Rate me:
Please Sign up or sign in to vote.
4.82/5 (21 votes)
15 Mar 2009CPOL5 min read 126.9K   7.4K   79   22
Handling DBT_DEVICEQUERYREMOVE in a managed Windows Service

Introduction

I was writing a C# Windows Service, and I needed to capture some device events. While this may seem an easy task, strangely, it isn't. Here is what I needed and what I did.

Prerequisites

A Windows Service is nothing more than a Windows program which is started by the Service Control Manager (SCM). A service can be manually started or scheduled for the SCM to start immediately after user logon. If you are unfamiliar with them, you can get more information on MSDN.

I have tested the code in Visual Studio 2008 Professional SP1, .NET 3.5 SP1, Windows XP SP3. It should work on any framework above 2.0, and it just might work in 1.1. I would suggest you go through the code as you read the text as it will become a lot more clear to you. Jump through it and see what happens.

Problem Description

Communication in Windows is done via messages. When a device (like a USB stick) is plugged in a computer, Windows sends messages to all who would listen for such an event. A Windows Forms application, for example, could easily intercept Windows messages by just overriding the virtual WndProc from the Form base class and using its Message parameter to its needs. However, a Windows Service typically is not meant to interact with the desktop or IO devices, so there is no easy way of knowing when something interesting has happened. And, if you would like to handle the DBT_DEVICEQUERYREMOVE event, things get complicated.

Service Control Handler (SCH)

Services have a control handler which receives all messages from Windows. These might include codes to stop or pause the service, or as in our case, device events. A managed service will abstract this control handler and give you only the OnStart, OnStop, and so on methods, which you can implement in order to achieve your needed functionality. We need, however, to register our own service handler, so we could catch device events. Note that this would disable all callbacks like OnStop except OnStart, which is called before we tell Windows to use our handler.

The Windows API function for this is RegisterServiceCtrlHandlerEx, which accepts a service name and a callback function to call when a message is received. We will call it in the OnStart function in our service. The managed version returns an IntPtr which is a service handle, but we already have a property of our class called ServiceHandle, so we don't need to save it.

The service control handler's signature is like this:

C#
public delegate int ServiceControlHandlerEx(int control, 
                int eventType, IntPtr eventData, IntPtr context);

Now, we can go implement our "OnStop" callback by capturing the SERVICE_CONTROL_STOP event, which is received in the "control" parameter of the handler. But, in order to handle SERVICE_CONTROL_DEVICEEVENT, we need to do something else.

Register for Device Notifications

In the OnStart method, apart from registering for a control handler, we will register for device notifications by using the Win32 API function RegisterDeviceNotification. We give it our service's handle, a pointer to a DEV_BROADCAST_DEVICEINTERFACE struct (telling the function to register for a class of devices), and some flags, among which is the DEVICE_NOTIFY_SERVICE_HANDLE, which specifies that the caller is a service and not a window, for example. It returns a handle, which we must preserve in order to unregister when we don't need device messages anymore (for example, we could do this in the SERVICE_CONTROL_STOP event).

Using this function allows us to capture the DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE event types. We get them through the eventType parameter of our SCH. There, we can handle the SERVICE_CONTROL_DEVICEEVENT and do anything we like.

DBT_DEVICEQUERYREMOVE

So far so good. But I needed more. I needed to use the FileSystemWatcher control which Microsoft supplied with the .NET Framework on the newly plugged USB. I could start the watcher when I get the device arrive event, but then when I attempted to "safely remove hardware", it always said that it couldn't remove it. It was obviously because of the watcher. I searched and found that there was an event DBT_DEVICEQUERYREMOVE, which is being risen just before a device is about to be removed. The only problem was that I couldn't get it with what I have done so far. Interestingly enough, Windows Forms applications work the same, so everything below should be done for them too.

The solution is to create a handle to the device itself, use it in a (this time) DEV_BROADCAST_HANDLE structure, and register with it to our SCH. All of this we could do in the DEVICEARRIVAL event. In order to accomplish all this, it takes a couple of things - find which drive letter the device got with the GetVolumeNameForVolumeMountPoint (which gets its parameters from the name field in the DEVICEINTERFACE struct) and GetVolumePathNamesForVolumeName in order to get a device handle using the CreateFile function. Only after that are we able to get the QUERYREMOVE event, disable the FileSystemWatcher, and allow the USB to be freely removed.

Conclusion

There are a ton of other small things that when not written exactly the way I have written them, do not work. If you need this kind of functionality, I would strongly suggest that you browse through the code I have supplied, and if do not want to understand it, at least just copy and paste. :) It will save you a lot of headaches.

History

  • 25th December, 2008: Initial post
  • 14th March, 2009: Updated source code

License

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


Written By
Bulgaria Bulgaria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionUSB insertion event is fired many times Pin
Member 123993707-Jun-16 1:20
Member 123993707-Jun-16 1:20 
QuestionTrying your demo service on Vista and Windows 7 Pin
bontas3-Sep-15 21:27
bontas3-Sep-15 21:27 
QuestionDon't need to have a file watcher to monitor when volume is ready. Pin
csuo31-Mar-15 12:43
csuo31-Mar-15 12:43 
GeneralSizeConst for dbcc_name should be longer Pin
West Dakota18-Mar-11 18:14
West Dakota18-Mar-11 18:14 
QuestionHow to capture new device info inside private void RegisterDeviceNotification()? Pin
avishekrc27-Feb-11 19:06
avishekrc27-Feb-11 19:06 
GeneralMy vote of 4 Pin
CSharpner.com19-Aug-10 2:47
CSharpner.com19-Aug-10 2:47 
GeneralOne potential gotcha Pin
izogi8-Apr-10 13:57
izogi8-Apr-10 13:57 
GeneralRe: One potential gotcha Pin
kiquenet.com23-Jul-10 6:49
professionalkiquenet.com23-Jul-10 6:49 
GeneralRe: One potential gotcha Pin
izogi23-Jul-10 11:50
izogi23-Jul-10 11:50 
GeneralProblems with ServiceControlHandler-Function Pin
Leo Tiger2-Aug-09 22:27
Leo Tiger2-Aug-09 22:27 
GeneralRe: Problems with ServiceControlHandler-Function Pin
Alien28214-Aug-09 23:46
Alien28214-Aug-09 23:46 
GeneralRe: Problems with ServiceControlHandler-Function Pin
Member 804220729-Jun-11 7:10
Member 804220729-Jun-11 7:10 
GeneralRe: Problems with ServiceControlHandler-Function Pin
nearperfect17-Jan-12 19:00
nearperfect17-Jan-12 19:00 
GeneralRe: Problems with ServiceControlHandler-Function Pin
mktbellevue22-Oct-16 7:35
mktbellevue22-Oct-16 7:35 
Questionrunning on Vista ok? Pin
User 608693924-Apr-09 17:08
User 608693924-Apr-09 17:08 
GeneralGood article Pin
Plamen Velikov22-Mar-09 2:11
Plamen Velikov22-Mar-09 2:11 
GeneralError with UnregisterDeviceNotification Pin
Spooler12-Mar-09 4:11
Spooler12-Mar-09 4:11 
GeneralRe: Error with UnregisterDeviceNotification Pin
Alien28214-Mar-09 1:47
Alien28214-Mar-09 1:47 
GeneralAttachments corrupt Pin
huwsimpson12-Jan-09 18:55
huwsimpson12-Jan-09 18:55 
GeneralRe: Attachments corrupt Pin
Alien28219-Jan-09 9:20
Alien28219-Jan-09 9:20 
GeneralRe: Attachments corrupt PinPopular
huwsimpson19-Jan-09 21:21
huwsimpson19-Jan-09 21:21 
GeneralRe: Attachments corrupt Pin
colinmeier31-Aug-15 11:52
professionalcolinmeier31-Aug-15 11:52 

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.