Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C++

Hardware Change Detection

,
Rate me:
Please Sign up or sign in to vote.
4.78/5 (34 votes)
18 Oct 2010CPOL4 min read 122.8K   7.7K   102   21
How to detect device changes in the user-mode applications for Windows

Introduction

In this article, I will describe how to detect device changes in your user-mode applications on the Windows operating system, IOW how to detect the situation when new devices are plugged or removed from the PC (i.e. plug-in USB stick, modem device or mobile phone, mount/unmount the new disk). The basic approach is receiving notification messages from the system.

Hardware Change Detecting

Windows sends events to top-level windows about arriving and removing a USB device interface. All we need to do is to add a handler to handle this event. First of all, we should register our windows to receive device notifications by calling RegisterDeviceNotification function.

This function is defined as follows:

C++
HDEVNOTIFY WINAPI RegisterDeviceNotification(
  __in  HANDLE hRecipient,
  __in  LPVOID NotificationFilter,
  __in  DWORD Flags
);

For the NotificationFilter parameter, we will use constant DBT_DEVTYP_DEVICEINTERFACE:

C++
GUID guidForModemDevices = {0x2c7089aa, 0x2e0e, 0x11d1, 
	{0xb1, 0x14, 0x00, 0xc0, 0x4f, 0xc2, 0xaa, 0xe4}};
    DEV_BROADCAST_DEVICEINTERFACE notificationFilter;
    ZeroMemory( &notificationFilter, sizeof(notificationFilter) );
    notificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    notificationFilter.dbcc_classguid = guid;

Window Creating

The hRecipient parameter in described above. RegisterDeviceNotification function is a window handle or a service status handle. In our sample, we will use window handle.

You can use existed handle of your top-level window in your UI application. If your application doesn't have a user interface, you can create a hidden window by registering instance of the WNDCLASSEX class and calling RegisterClassEx function by Windows API. Then create window and show it with parameter SW_HIDE:

C++
const wchar_t winClass[] = L"MyNotifyWindow";
const wchar_t winTitle[] = L"WindowTitle";
HINSTANCE hInstance = ::GetModuleHandle(NULL);
HWND hWnd = ::CreateWindow(winClass, winTitle, WS_ICONIC, 0, 0,
       CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

ShowWindow(hWnd, SW_HIDE);

Event Handling

In this article, we will handle only DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE types of device changes. First, we should handle WM_DEVICECHANGE event in WndProc method by analyzing message parameter. Then, filter our two types of events by analyzing wParam parameter:

C++
LRESULT CALLBACK NotifyWindow::WndProc
	(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_DEVICECHANGE:
        {
            if ((wParam == DBT_DEVICEARRIVAL) || (wParam == DBT_DEVICEREMOVECOMPLETE))
           …
        }
      …
     }
…
}

The parameter lParam gives us information about the type and wParam about the state (arrival or removed) of the device:

C++
DEV_BROADCAST_HDR* header = reinterpret_cast<DEV_BROADCAST_HDR*>(lParam);
C++
DEV_BROADCAST_HDR* header = reinterpret_cast<DEV_BROADCAST_HDR*>(lParam);

if (header->dbch_devicetype == DBT_DEVTYP_VOLUME)
{
	if (DBT_DEVICEARRIVAL == wParam)
      {
		// volume arrival
      } 
      else
      {
		// volume removed
      }
}
if (header->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
{…}

For mass-storage devices, we should handle DBT_DEVTYP_VOLUME device type because it means that a label has already been assigned to the volume and volume is ready to use.

Globally Unique Identifiers (GUIDs)

Plug-and-Play device can be associated with one or more different GUIDs. One device can have several interfaces. There are device class GUIDs and device interface GUIDs.

Some of GUIDs for device interface classes are listed below:

  1. GUID_DEVINTERFACE_DISK ={ 53F56307- B6BF -11D0 -94F200A0C91EFB8B}
  2. GUID modemDevice = {2C7089AA-2E0E-11D1-B11400C04FC2AAE4};
  3. GUID GarminGPSDevice = {2C9C45C2-8E7D-4C08-A12D816BBAE722C0};

For handling all interfaces you want, you should call RegisterDeviceNotification for each GUID.

Windows Services

You can handle device notifications in your Windows Service. To do this, you should make changes in the code given above. When you register for notification, you should use DEVICE_NOTIFY_SERVICE_HANDLE constant in RegisterDeviceNotification function and handle SERVICE_CONTROL_DEVICEEVENT service control message, which is similar to WM_DEVICECHANGE event. Also you can use the technique with hidden windows described above.

Retrieving of Information About Already Connected Devices

After implementing everything mentioned above, you will be notified about device changes. It will be possible only if your user-mode application had been started before the device was plugged in or removed. But for different reasons, we should enumerate existing devices in the system after launching of our application. Windows has a set of APIs that give us a possibility to retrieve hardware device information. For example, we can get the device description or device friendly name. We will need to use such functions as SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces, SetupDiGetDeviceInterfaceDetail, SetupDiGetDeviceRegistryProperty, etc. These methods are defined in setupapi.h. First, we should call SetupDiGetClassDevs function to retrieve a handle of device info HDEVINFO set. Then, we should use SetupDiEnumDeviceInterfaces function to enumerate interfaces:

C++
GUID guid = {…}	// some device interface GUID
HDEVINFO hDevInfo = ::SetupDiGetClassDevsW
	(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
SP_DEVICE_INTERFACE_DATA devInterfaceData;
::ZeroMemory(&devInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA));
devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
for ( DWORD dwCount = 0; ::SetupDiEnumDeviceInterfaces
	(hDevInfo, NULL, guid, dwCount, &devInterfaceData); ++dwCount )
{
…
}
::SetupDiDestroyDeviceInfoList(hDevInfo);

After retrieving device interface data, we can call SetupDiGetDeviceInterfaceDetail function and retrieve such information as friendly name, device description, hardware id, device path.

Sample

In the source code for this article, there is functionality to detect device changes for mass-storage devices, modem devices (for Motorola) and BlackBerry smartphone. To add more interfaces support, you should change the code (define new GUIDs or write filters to detect specific devices).

The source code contains a static library DeviceDetectLibrary and console application DeviceDetectConsole. The main class is DeviceWatcher with such methods as Start(), Stop(), Found(), Lost(). By subclassing this class, you can use these methods to write a handler for them.

Let's look at how it works. First, we launch our DeviceDetectConsole. Then, we plug-in USB Flash drive to our PC and open Device Manager. Here we can see JetFlash Trancend USB device:

1_DeviceManager_Flash.png

Take a look at our application and see the detection process with the new appeared device:

2_Console_FlashDrive.png

The test application just shows the status of the new devices (appeared or disappeared). This sample can show you devices that have been attached before you launched an application too.

Now let’s test the modem device. For test, we take mobile phone Motorola V3C and BlackBerry smartPhone and connect them to PC:

3_DeviceManager_Motorola.png

Let’s take a look at our application:

4_Console_Motorola.png

For BlackBerry device:

5_Console_BlackBerry.png

The application successfully detected the new devices.

Conclusion

For different purposes, we need to receive system notifications about device changes. In this article, I show some basic methods for doing this. In the source code of the sample, you can find a lot of useful functions that you can use for receiving notifications, some low-level information and description of devices.

History

  • 18th October, 2010: Initial post

License

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


Written By
Chief Technology Officer Apriorit Inc.
United States United States
ApriorIT is a software research and development company specializing in cybersecurity and data management technology engineering. We work for a broad range of clients from Fortune 500 technology leaders to small innovative startups building unique solutions.

As Apriorit offers integrated research&development services for the software projects in such areas as endpoint security, network security, data security, embedded Systems, and virtualization, we have strong kernel and driver development skills, huge system programming expertise, and are reals fans of research projects.

Our specialty is reverse engineering, we apply it for security testing and security-related projects.

A separate department of Apriorit works on large-scale business SaaS solutions, handling tasks from business analysis, data architecture design, and web development to performance optimization and DevOps.

Official site: https://www.apriorit.com
Clutch profile: https://clutch.co/profile/apriorit
This is a Organisation

33 members

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

Comments and Discussions

 
QuestionI Like It Pin
Dan Bloomquist7-Sep-16 5:28
Dan Bloomquist7-Sep-16 5:28 
AnswerWorks like a charm Pin
Member 24433063-Nov-15 2:05
Member 24433063-Nov-15 2:05 
QuestionSample is too complex for the desired goal, why? Pin
asdf50021-Jun-15 22:40
asdf50021-Jun-15 22:40 
GeneralPerfecly Work !! Pin
Haris Muhamad Zaien23-Apr-15 8:49
Haris Muhamad Zaien23-Apr-15 8:49 
QuestionVisual Studio 2013 Pin
Haris Muhamad Zaien17-Apr-15 22:39
Haris Muhamad Zaien17-Apr-15 22:39 
AnswerUsefull for testing. But not for industrial programming Pin
heretic1311-Nov-14 4:07
heretic1311-Nov-14 4:07 
GeneralRe: Usefull for testing. But not for industrial programming Pin
row_5819-Nov-14 5:37
row_5819-Nov-14 5:37 
AnswerRe: Usefull for testing. But not for industrial programming Pin
heretic132-Dec-14 4:43
heretic132-Dec-14 4:43 
QuestionHardware (USB) Change Detection Pin
AhsinChoudhry20-Oct-13 22:11
AhsinChoudhry20-Oct-13 22:11 
GeneralMy vote of 5 Pin
Michael Haephrati18-Sep-12 10:40
professionalMichael Haephrati18-Sep-12 10:40 
GeneralMy vote of 5 Pin
gndnet29-Aug-12 1:24
gndnet29-Aug-12 1:24 
GeneralMy vote of 5 Pin
zhuziyu48-Aug-12 23:31
zhuziyu48-Aug-12 23:31 
GeneralMy vote of 5 Pin
gndnet31-Jul-12 14:04
gndnet31-Jul-12 14:04 
BugNo Detecting :( Pin
suluvai22-Nov-11 19:46
suluvai22-Nov-11 19:46 
GeneralRe: No Detecting :( Pin
Member 295917218-Feb-12 8:30
Member 295917218-Feb-12 8:30 
GeneralNo boost, no fun! Pin
Tefik Becirovic18-Oct-10 22:34
Tefik Becirovic18-Oct-10 22:34 
GeneralRe: No boost, no fun! Pin
Eugene Kordin18-Oct-10 22:43
Eugene Kordin18-Oct-10 22:43 
GeneralRe: No boost, no fun! Pin
Tefik Becirovic18-Oct-10 22:51
Tefik Becirovic18-Oct-10 22:51 
GeneralRe: No boost, no fun! Pin
Cristian Adam18-Oct-10 22:49
Cristian Adam18-Oct-10 22:49 
GeneralRe: No boost, no fun! Pin
Tefik Becirovic18-Oct-10 23:00
Tefik Becirovic18-Oct-10 23:00 
GeneralGood Pin
cccfff77718-Oct-10 12:05
cccfff77718-Oct-10 12:05 
I like it! Well done.

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.