Code:
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <dbt.h>
#include <string>
#include <algorithm>
#include <iostream>
#include <iomanip>
using namespace std;
// uncomment to listen for device interface changes
//#define LISTEN_TO_DEVICEINTERFACE_CHANGES
// uncomment to listen for a specific device interface
// leave commented to listen for all interfaces
//#define DEVICEINTERFACE_TO_LISTEN4 usbDevIntGuid
//-----------------------------------------------------------------------------
const GUID volumeDevIntGuid =
{0x53F5630D, 0xB6BF, 0x11D0,
{0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B}};
const GUID diskDevIntGuid =
{0x53f56307, 0xb6bf, 0x11d0,
{0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b}};
const GUID partitionDevIntGuid =
{0x53f5630a, 0xb6bf, 0x11d0,
{0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b}};
const GUID usbDevIntGuid =
{0xa5dcbf10, 0x6530, 0x11d2,
{0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed}};
struct DevIntGuidNamePair
{
const GUID *pguid;
const char *name;
};//DevIntGuidNamePair
const DevIntGuidNamePair knownGUIDs[] =
{
{&volumeDevIntGuid, "Volume"},
{&diskDevIntGuid, "Disk"},
{&partitionDevIntGuid, "Partition"},
{&usbDevIntGuid, "USB"},
};//knownGUIDs
const size_t num_knownGUIDs = sizeof(knownGUIDs) / sizeof(*knownGUIDs);
//-----------------------------------------------------------------------------
void PrintVolumeChange(DEV_BROADCAST_VOLUME *pbv, bool bArrival)
{
size_t mask = 1;
char c = 'A';
for (; c <= 'Z'; ++c, mask <<= 1)
{
if (!(mask & pbv->dbcv_unitmask))
continue;
if (bArrival)
cout << "New media, drive ";
else
cout << "Removed media, drive ";
char path[] = "x:\\";
path[0] = c;
cout << path << ' ';
switch (GetDriveTypeA(path))
{
case DRIVE_REMOVABLE: cout << "[Removable Media]"; break;
case DRIVE_FIXED: cout << "[Fixed Media]"; break;
case DRIVE_REMOTE: cout << "[Network Drive]"; break;
case DRIVE_CDROM: cout << "[CD-ROM Drive]"; break;
case DRIVE_RAMDISK: cout << "[RAM Disk]"; break;
case DRIVE_UNKNOWN: cout << "[Unknown Type]"; break;
case DRIVE_NO_ROOT_DIR: cout << "[No Root]"; break;
}//switch
cout << endl;
}//for
cout << endl;
}//PrintVolumeChange
//-----------------------------------------------------------------------------
void PrintDevIntNames(DEV_BROADCAST_DEVICEINTERFACE_A *pbdi)
{
string name = pbdi->dbcc_name;
string::size_type pos = name.rfind('#');
if (pos == string::npos)
{
cout << "\n Error";
return;
}//if
// device interface guid follows last #
// this is the text representation of pbdi->dbcc_classguid
string devint_guid = name.substr(pos + 1);
// remove trailing guid and leading "\\?\"
name.erase(pos);
name.erase(0, 4);
// replace remaining # to \ to form the full PNP enumerator name
replace(name.begin(), name.end(), '#', '\\');
for (size_t n = 0; n < num_knownGUIDs; ++n)
{
if (*knownGUIDs[n].pguid == pbdi->dbcc_classguid)
{
cout << "\n Device Interface Name = " << knownGUIDs[n].name;
break;
}//if
}//for
cout << "\n Device Interface GUID = " << devint_guid
<< "\n Device PNP Enum Name = " << name;
}//PrintDevIntNames
//-----------------------------------------------------------------------------
LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_DEVICECHANGE)
{
if ((wParam == DBT_DEVICEARRIVAL) ||
(wParam == DBT_DEVICEREMOVECOMPLETE))
{
bool bArrival = wParam == DBT_DEVICEARRIVAL;
DEV_BROADCAST_HDR *pbh = (DEV_BROADCAST_HDR*)lParam;
if (pbh->dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PrintVolumeChange((DEV_BROADCAST_VOLUME*)pbh, bArrival);
}//if
else
{
if (bArrival)
cout << "Arrival of ";
else
cout << "Removal of ";
if (pbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
PrintDevIntNames((DEV_BROADCAST_DEVICEINTERFACE_A*)pbh);
else
cout << "devicetype " << pbh->dbch_devicetype;
cout << '\n' << endl;
}//else
}//if
else if (wParam == DBT_DEVNODES_CHANGED)
{
// ignore
}//else if
else
cout << "WM_DEVICECHANGE, wParam = 0x"
<< setw(4) << hex << setfill('0') << int(wParam) << dec
<< endl;
}//if
else
{
/*
cout << "Got msg " << msg << ", " << int(wParam)
<< ", " << int(lParam) << endl;
*/
}//else
return 1;
}//WinProc
//-----------------------------------------------------------------------------
int main()
{
const char * const className = "DevNotifyTest";
WNDCLASSA wincl = {0};
wincl.hInstance = GetModuleHandleA(0);
wincl.lpszClassName = className;
wincl.lpfnWndProc = WinProc;
if (!RegisterClassA(&wincl))
{
DWORD le = GetLastError();
cout << "RegisterClassA() failed, le = " << le << endl;
return 1;
}//if
HWND hwnd = CreateWindowExA(0, className, className,
0, 0, 0, 0, 0, 0, 0, 0, 0);
if (!hwnd)
{
DWORD le = GetLastError();
cout << "CreateWindowExA() failed, le = " << le << endl;
return 1;
}//if
#ifdef LISTEN_TO_DEVICEINTERFACE_CHANGES
DEV_BROADCAST_DEVICEINTERFACE_A notifyFilter = {0};
notifyFilter.dbcc_size = sizeof(notifyFilter);
notifyFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
#ifdef DEVICEINTERFACE_TO_LISTEN4
notifyFilter.dbcc_classguid = DEVICEINTERFACE_TO_LISTEN4;
#endif
HDEVNOTIFY hDevNotify =
RegisterDeviceNotificationA(hwnd, ¬ifyFilter,
#ifndef DEVICEINTERFACE_TO_LISTEN4
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES |
#endif
DEVICE_NOTIFY_WINDOW_HANDLE);
if (!hDevNotify)
{
DWORD le = GetLastError();
cout << "RegisterDeviceNotificationA() failed, le = " << le << endl;
return 1;
}//if
#endif //ifdef LISTEN_TO_DEVICEINTERFACE_CHANGES
cout << "Waiting for device change notifications, "
"Ctrl-C to exit.\n" << endl;
for (;;)
{
MSG msg;
BOOL bRet = GetMessage(&msg, hwnd, 0, 0);
if ((bRet == 0) || (bRet == -1))
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}//while
return 0;
}//main
Be sure to check the drive-type. If auto-play is enabled at the driver level, then the ejection and insertion of a CDROM/DVDROM disc will show as a Volume removal and arrival.