244 const wchar_t drive = p.size() >= 2 && p.at(0).isLetter() && p.at(1) == u':'
245 ?
wchar_t(p.at(0).toUpper().unicode()) : L'\0';
249 if (std::any_of(m_removableDrives.cbegin(), m_removableDrives.cend(),
250 [drive](
const RemovableDriveEntry &e) {
return e.drive == drive; })) {
254 wchar_t devicePath[8] = L"\\\\.\\A:\\";
255 devicePath[4] = drive;
258 if (GetDriveTypeW(devicePath + 4) != DRIVE_REMOVABLE)
260 const HANDLE volumeHandle =
261 CreateFile(devicePath, FILE_READ_ATTRIBUTES,
262 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0,
263 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
265 if (volumeHandle == INVALID_HANDLE_VALUE) {
266 qErrnoWarning(
"CreateFile %ls failed.", devicePath);
270 DEV_BROADCAST_HANDLE notify;
271 ZeroMemory(¬ify,
sizeof(notify));
272 notify.dbch_size =
sizeof(notify);
273 notify.dbch_devicetype = DBT_DEVTYP_HANDLE;
274 notify.dbch_handle = volumeHandle;
275 QThreadData *currentData = QThreadData::current();
276 QEventDispatcherWin32 *winEventDispatcher =
static_cast<QEventDispatcherWin32 *>(currentData->ensureEventDispatcher());
277 re.devNotify = RegisterDeviceNotification(winEventDispatcher->internalHwnd(),
278 ¬ify, DEVICE_NOTIFY_WINDOW_HANDLE);
281 CloseHandle(volumeHandle);
283 qErrnoWarning(
"RegisterDeviceNotification %ls failed.", devicePath);
287 m_removableDrives.push_back(re);
299 : QFileSystemWatcherEngine(parent)
301 if (QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance()) {
302 m_driveListener =
new QWindowsRemovableDriveListener(
this);
303 eventDispatcher->installNativeEventFilter(m_driveListener);
304 parent->setProperty(
"_q_driveListener",
305 QVariant::fromValue(
static_cast<QObject *>(m_driveListener)));
306 QObject::connect(m_driveListener, &QWindowsRemovableDriveListener::driveLockForRemoval,
307 this, &QWindowsFileSystemWatcherEngine::driveLockForRemoval);
308 QObject::connect(m_driveListener, &QWindowsRemovableDriveListener::driveLockForRemovalFailed,
309 this, &QWindowsFileSystemWatcherEngine::driveLockForRemovalFailed);
310 QObject::connect(m_driveListener,
311 QOverload<
const QString &>::of(&QWindowsRemovableDriveListener::driveRemoved),
312 this, &QWindowsFileSystemWatcherEngine::driveRemoved);
314 qWarning(
"QFileSystemWatcher: Removable drive notification will not work"
315 " if there is no QCoreApplication instance.");
330 QStringList *directories)
332 DEBUG() <<
"Adding" << paths.count() <<
"to existing" << (files->count() + directories->count()) <<
"watchers";
333 QStringList unhandled;
334 for (
const QString &path : paths) {
335 auto sg = qScopeGuard([&] { unhandled.push_back(path); });
336 QFileInfo fileInfo(path);
338 if (!fileInfo.exists())
341 bool isDir = fileInfo.isDir();
343 if (directories->contains(path))
346 if (files->contains(path))
350 DEBUG() <<
"Looking for a thread/handle for" << fileInfo.path();
352 const QString absolutePath = isDir ? fileInfo.absoluteFilePath() : fileInfo.absolutePath();
353 const uint flags = isDir
354 ? (FILE_NOTIFY_CHANGE_DIR_NAME
355 | FILE_NOTIFY_CHANGE_ATTRIBUTES
356 | FILE_NOTIFY_CHANGE_FILE_NAME)
357 : (FILE_NOTIFY_CHANGE_DIR_NAME
358 | FILE_NOTIFY_CHANGE_FILE_NAME
359 | FILE_NOTIFY_CHANGE_ATTRIBUTES
360 | FILE_NOTIFY_CHANGE_SIZE
361 | FILE_NOTIFY_CHANGE_LAST_WRITE
362 | FILE_NOTIFY_CHANGE_SECURITY);
364 QWindowsFileSystemWatcherEngine::PathInfo pathInfo;
365 pathInfo.absolutePath = absolutePath;
366 pathInfo.isDir = isDir;
367 pathInfo.path = path;
371 QWindowsFileSystemWatcherEngineThread *thread =
nullptr;
372 QWindowsFileSystemWatcherEngine::Handle handle;
373 QList<QWindowsFileSystemWatcherEngineThread *>::const_iterator jt, end;
374 end = threads.constEnd();
375 for(jt = threads.constBegin(); jt != end; ++jt) {
377 const auto locker = qt_scoped_lock(thread->mutex);
379 const auto hit = thread->handleForDir.find(QFileSystemWatcherPathKey(absolutePath));
380 if (hit != thread->handleForDir.end() && hit.value().flags < flags) {
384 DEBUG() <<
"recreating" << absolutePath << Qt::hex << Qt::showbase << hit.value().flags
386 const Qt::HANDLE fileHandle = createChangeNotification(absolutePath, flags);
387 if (fileHandle != INVALID_HANDLE_VALUE) {
388 const int index = thread->handles.indexOf(hit.value().handle);
389 const auto pit = thread->pathInfoForHandle.find(hit.value().handle);
390 Q_ASSERT(index != -1);
391 Q_ASSERT(pit != thread->pathInfoForHandle.end());
392 FindCloseChangeNotification(hit.value().handle);
393 thread->handles[index] = hit.value().handle = fileHandle;
394 hit.value().flags = flags;
395 auto value = std::move(*pit);
396 thread->pathInfoForHandle.erase(pit);
397 thread->pathInfoForHandle.insert(fileHandle, std::move(value));
401 if (hit != thread->handleForDir.end() && hit.value().flags >= flags) {
402 handle = hit.value();
404 DEBUG() <<
"Found a thread" << thread;
406 QWindowsFileSystemWatcherEngineThread::PathInfoHash &h =
407 thread->pathInfoForHandle[handle.handle];
408 const QFileSystemWatcherPathKey key(fileInfo.absoluteFilePath());
409 if (!h.contains(key)) {
410 thread->pathInfoForHandle[handle.handle].insert(key, pathInfo);
412 directories->append(path);
423 if (handle.handle == INVALID_HANDLE_VALUE) {
424 DEBUG() <<
"No thread found";
425 handle.handle = createChangeNotification(absolutePath, flags);
426 handle.flags = flags;
427 if (handle.handle == INVALID_HANDLE_VALUE)
432 for (QWindowsFileSystemWatcherEngineThread *thread : std::as_const(threads)) {
433 const auto locker = qt_scoped_lock(thread->mutex);
434 if (thread->handles.count() < MAXIMUM_WAIT_OBJECTS) {
435 DEBUG() <<
"Added handle" << handle.handle <<
"for" << absolutePath <<
"to watch" << fileInfo.absoluteFilePath()
436 <<
"to existing thread " << thread;
437 thread->handles.append(handle.handle);
438 thread->handleForDir.insert(QFileSystemWatcherPathKey(absolutePath), handle);
440 thread->pathInfoForHandle[handle.handle].insert(QFileSystemWatcherPathKey(fileInfo.absoluteFilePath()), pathInfo);
442 directories->append(path);
453 QWindowsFileSystemWatcherEngineThread *thread =
new QWindowsFileSystemWatcherEngineThread();
454 DEBUG() <<
" ###Creating new thread" << thread <<
'(' << (threads.count()+1) <<
"threads)";
455 thread->handles.append(handle.handle);
456 thread->handleForDir.insert(QFileSystemWatcherPathKey(absolutePath), handle);
458 thread->pathInfoForHandle[handle.handle].insert(QFileSystemWatcherPathKey(fileInfo.absoluteFilePath()), pathInfo);
460 directories->append(path);
464 connect(thread, SIGNAL(fileChanged(QString,
bool)),
465 this, SIGNAL(fileChanged(QString,
bool)));
466 connect(thread, SIGNAL(directoryChanged(QString,
bool)),
467 this, SIGNAL(directoryChanged(QString,
bool)));
471 threads.append(thread);
477 if (Q_LIKELY(m_driveListener)) {
478 for (
const QString &path : paths) {
479 if (!unhandled.contains(path))
480 m_driveListener->addPath(path);
488 QStringList *directories)
490 DEBUG() <<
"removePaths" << paths;
491 QStringList unhandled;
492 for (
const QString &path : paths) {
493 auto sg = qScopeGuard([&] { unhandled.push_back(path); });
494 QFileInfo fileInfo(path);
495 DEBUG() <<
"removing" << fileInfo.path();
496 QString absolutePath = fileInfo.absoluteFilePath();
497 QList<QWindowsFileSystemWatcherEngineThread *>::iterator jt, end;
499 for(jt = threads.begin(); jt!= end; ++jt) {
500 QWindowsFileSystemWatcherEngineThread *thread = *jt;
504 auto locker = qt_unique_lock(thread->mutex);
506 QWindowsFileSystemWatcherEngine::Handle handle = thread->handleForDir.value(QFileSystemWatcherPathKey(absolutePath));
507 if (handle.handle == INVALID_HANDLE_VALUE) {
509 absolutePath = fileInfo.absolutePath();
510 handle = thread->handleForDir.value(QFileSystemWatcherPathKey(absolutePath));
512 if (handle.handle != INVALID_HANDLE_VALUE) {
513 QWindowsFileSystemWatcherEngineThread::PathInfoHash &h =
514 thread->pathInfoForHandle[handle.handle];
515 if (h.remove(QFileSystemWatcherPathKey(fileInfo.absoluteFilePath()))) {
517 files->removeAll(path);
518 directories->removeAll(path);
522 DEBUG() <<
"Closing handle" << handle.handle;
523 FindCloseChangeNotification(handle.handle);
525 int indexOfHandle = thread->handles.indexOf(handle.handle);
526 Q_ASSERT(indexOfHandle != -1);
527 thread->handles.remove(indexOfHandle);
529 thread->handleForDir.remove(QFileSystemWatcherPathKey(absolutePath));
532 if (thread->handleForDir.isEmpty()) {
533 DEBUG() <<
"Stopping thread " << thread;
550 QList<QWindowsFileSystemWatcherEngineThread *>::iterator jt, end;
552 for(jt = threads.begin(); jt != end; ++jt) {
553 if (!(*jt)->isRunning()) {
559 threads.removeAll(
nullptr);
602 auto locker = qt_unique_lock(mutex);
604 QList<HANDLE> handlesCopy = handles;
606 DEBUG() <<
"QWindowsFileSystemWatcherThread" <<
this <<
"waiting on" << handlesCopy.count() <<
"handles";
607 DWORD r = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(),
false, INFINITE);
610 if (r == WAIT_OBJECT_0) {
614 DEBUG() <<
"thread" <<
this <<
"told to quit";
618 DEBUG() <<
"QWindowsFileSystemWatcherEngine: unknown message sent to thread: " <<
char(m);
621 if (r > WAIT_OBJECT_0 && r < WAIT_OBJECT_0 + uint(handlesCopy.count())) {
622 int at = r - WAIT_OBJECT_0;
623 Q_ASSERT(at < handlesCopy.count());
624 HANDLE handle = handlesCopy.at(at);
628 if (handles.contains(handle)) {
629 DEBUG() <<
"thread" <<
this <<
"Acknowledged handle:" << at << handle;
630 QWindowsFileSystemWatcherEngineThread::PathInfoHash &h = pathInfoForHandle[handle];
631 bool fakeRemove =
false;
633 if (!FindNextChangeNotification(handle)) {
634 const DWORD error = GetLastError();
636 if (error == ERROR_ACCESS_DENIED) {
643 qErrnoWarning(error,
"%ls", qUtf16Printable(msgFindNextFailed(h)));
645 for (
auto it = h.begin(); it != h.end(); ) {
646 QString absolutePath = it.value().absolutePath;
647 QFileInfo fileInfo(it.value().path);
648 DEBUG() <<
"checking" << it.key();
652 if (fakeRemove || !fileInfo.exists()) {
653 DEBUG() << it.key() <<
"removed!";
654 if (it.value().isDir)
655 emit directoryChanged(it.value().path,
true);
657 emit fileChanged(it.value().path,
true);
662 DEBUG() <<
"Thread closing handle" << handle;
663 FindCloseChangeNotification(handle);
665 int indexOfHandle = handles.indexOf(handle);
666 Q_ASSERT(indexOfHandle != -1);
667 handles.remove(indexOfHandle);
669 handleForDir.remove(QFileSystemWatcherPathKey(absolutePath));
674 }
else if (it.value().isDir) {
675 DEBUG() << it.key() <<
"directory changed!";
676 emit directoryChanged(it.value().path,
false);
677 it.value() = fileInfo;
678 }
else if (it.value() != fileInfo) {
679 DEBUG() << it.key() <<
"file changed!";
680 emit fileChanged(it.value().path,
false);
681 it.value() = fileInfo;
690 handlesCopy = handles;
691 r = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(),
false, 0);
692 }
while (r != WAIT_TIMEOUT);