Monitoring Removable Storage Devices in Qt on Windows
Developing a custom file browser requires real-time awareness of system storage changes, such as USB flash drive insertions or remvoals. On Windows, this is achieved by interceptign system-level device notifications. The most effective way to monitor these changes in a Qt application is by handling the WM_DEVICECHANGE message.
1. Handling WM_DEVICECHANGE
When a hardware configuration change occurs, Windows broadcasts the WM_DEVICECHANGE message to top-level windows. Key event codes include:
DBT_DEVICEARRIVAL: A new device has been connected and is ready for use.DBT_DEVICEREMOVECOMPLETE: A device has been physically removed or disconnected.DBT_DEVNODES_CHANGED: Triggered whenever a device is added or removed, though it may fire multiple times per action.
2. Registering for Device Notifications
To monitor specific volumes, use the RegisterDeviceNotification Win32 API. This registers your application to receive notifications for specific device handles or interface classes.
bool DeviceMonitor::registerDriveForNotification(const QString &drivePath) {
// Ensure the drive exists before registration
DWORD mask = GetLogicalDrives();
int index = drivePath.at(0).toUpper().toLatin1() - 'A';
if (!(mask & (1 << index))) return false;
// Open a handle to the volume
HANDLE hDrive = CreateFile(
(L"\\\\.\\" + drivePath.left(2)).toStdWString().c_str(),
GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL
);
if (hDrive == INVALID_HANDLE_VALUE) return false;
DEV_BROADCAST_HANDLE filter = {0};
filter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
filter.dbch_devicetype = DBT_DEVTYP_HANDLE;
filter.dbch_handle = hDrive;
HDEVNOTIFY hNotify = RegisterDeviceNotification(
(HWND)this->winId(), &filter, DEVICE_NOTIFY_WINDOW_HANDLE
);
CloseHandle(hDrive);
return (hNotify != NULL);
}
3. Intercepting Events in Qt
The most efficient way to capture these native Windows events within a Qt application is by implementing the QAbstractNativeEventFilter interface. By installing this filter onto the QCoreApplication, you can examine every incoming message before it is processed by Qt's event loop.
bool DeviceEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result) {
if (eventType == "windows_dispatcher_MSG" || eventType == "windows_generic_MSG") {
MSG *msg = static_cast<msg>(message);
if (msg->message == WM_DEVICECHANGE) {
PDEV_BROADCAST_HDR header = reinterpret_cast<pdev_broadcast_hdr>(msg->lParam);
switch (msg->wParam) {
case DBT_DEVICEARRIVAL:
if (header->dbch_devicetype == DBT_DEVTYP_VOLUME) {
emit deviceInserted();
}
break;
case DBT_DEVICEREMOVECOMPLETE:
if (header->dbch_devicetype == DBT_DEVTYP_VOLUME) {
emit deviceRemoved();
}
break;
}
}
}
return false; // Continue event propagation
}
</pdev_broadcast_hdr></msg>
To activate the filter, call qApp->installNativeEventFilter(filterInstance) during application initialization. This architecture ensures that your UI remains responsive and automatically detects changes in connected external storage devices without requiring polling mechanisms.