Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qfilesystemwatcher_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#include <qdebug.h>
8#include <qfileinfo.h>
9#include <qstringlist.h>
10#include <qset.h>
11#include <qscopeguard.h>
12#include <qdatetime.h>
13#include <qdir.h>
14#include <qtextstream.h>
15#include <private/qlocking_p.h>
16
17#include <qt_windows.h>
18
20# include <qcoreapplication.h>
21# include <qdir.h>
22# include <private/qeventdispatcher_win_p.h>
23# include <private/qthread_p.h>
24# include <dbt.h>
25# include <algorithm>
26# include <vector>
27
29
30using namespace Qt::StringLiterals;
31
32// #define WINQFSW_DEBUG
33#ifdef WINQFSW_DEBUG
34# define DEBUG qDebug
35#else
36# define DEBUG if (false) qDebug
37#endif
38
40{
41 // Volume and folder paths need a trailing slash for proper notification
42 // (e.g. "c:" -> "c:/").
44 if ((flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) == 0 && !nativePath.endsWith(u'\\'))
45 nativePath.append(u'\\');
46 const HANDLE result = FindFirstChangeNotification(reinterpret_cast<const wchar_t *>(nativePath.utf16()),
47 FALSE, flags);
48 DEBUG() << __FUNCTION__ << nativePath << Qt::hex << Qt::showbase << flags << "returns" << result;
49 return result;
50}
51
53// QWindowsRemovableDriveListener
54// Listen for the various WM_DEVICECHANGE message indicating drive addition/removal
55// requests and removals.
58{
60public:
61 // Device UUids as declared in ioevent.h (GUID_IO_VOLUME_LOCK, ...)
64
66 HDEVNOTIFY devNotify;
67 wchar_t drive;
68 };
69
72
73 // Call from QFileSystemWatcher::addPaths() to set up notifications on drives
74 void addPath(const QString &path);
75
76 bool nativeEventFilter(const QByteArray &, void *messageIn, qintptr *) override;
77
79 void driveAdded();
80 void driveRemoved(); // Some drive removed
81 void driveRemoved(const QString &); // Watched/known drive removed
84
85private:
86 static VolumeUuid volumeUuid(const UUID &needle);
87 void handleDbtCustomEvent(const MSG *msg);
88 void handleDbtDriveArrivalRemoval(const MSG *msg);
89
90 std::vector<RemovableDriveEntry> m_removableDrives;
91 quintptr m_lastMessageHash;
92};
93
95 : QObject(parent)
96 , m_lastMessageHash(0)
97{
98}
99
101{
102 UnregisterDeviceNotification(e.devNotify);
103 e.devNotify = 0;
104}
105
106template <class Iterator> // Search sequence of RemovableDriveEntry for HDEVNOTIFY.
107static inline Iterator findByHDevNotify(Iterator i1, Iterator i2, HDEVNOTIFY hdevnotify)
108{
109 return std::find_if(i1, i2,
110 [hdevnotify] (const QWindowsRemovableDriveListener::RemovableDriveEntry &e) { return e.devNotify == hdevnotify; });
111}
112
114{
115 std::for_each(m_removableDrives.begin(), m_removableDrives.end(), stopDeviceNotification);
116}
117
119{
120 QString path = QStringLiteral("A:/");
121 path[0] = QChar::fromLatin1(re.drive);
122 return path;
123}
124
125// Handle WM_DEVICECHANGE+DBT_CUSTOMEVENT, which is sent based on the registration
126// on the volume handle with QEventDispatcherWin32's message window in the class.
127// Capture the GUID_IO_VOLUME_LOCK indicating the drive is to be removed.
128QWindowsRemovableDriveListener::VolumeUuid QWindowsRemovableDriveListener::volumeUuid(const UUID &needle)
129{
130 static const struct VolumeUuidMapping // UUIDs from IoEvent.h (missing in MinGW)
131 {
132 VolumeUuid v;
133 UUID uuid;
134 } mapping[] = {
135 { UuidIoVolumeLock, // GUID_IO_VOLUME_LOCK
136 {0x50708874, 0xc9af, 0x11d1, {0x8f, 0xef, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} },
137 { UuidIoVolumeLockFailed, // GUID_IO_VOLUME_LOCK_FAILED
138 {0xae2eed10, 0x0ba8, 0x11d2, {0x8f, 0xfb, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} },
139 { UuidIoVolumeUnlock, // GUID_IO_VOLUME_UNLOCK
140 {0x9a8c3d68, 0xd0cb, 0x11d1, {0x8f, 0xef, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} },
141 { UuidIoMediaRemoval, // GUID_IO_MEDIA_REMOVAL
142 {0xd07433c1, 0xa98e, 0x11d2, {0x91, 0x7a, 0x0, 0xa0, 0xc9, 0x06, 0x8f, 0xf3}} }
143 };
144
145 static const VolumeUuidMapping *end = mapping + sizeof(mapping) / sizeof(mapping[0]);
146 const VolumeUuidMapping *m =
147 std::find_if(mapping, end, [&needle] (const VolumeUuidMapping &m) { return IsEqualGUID(m.uuid, needle); });
148 return m != end ? m->v : UnknownUuid;
149}
150
151inline void QWindowsRemovableDriveListener::handleDbtCustomEvent(const MSG *msg)
152{
153 const DEV_BROADCAST_HDR *broadcastHeader = reinterpret_cast<const DEV_BROADCAST_HDR *>(msg->lParam);
154 if (broadcastHeader->dbch_devicetype != DBT_DEVTYP_HANDLE)
155 return;
156 const DEV_BROADCAST_HANDLE *broadcastHandle = reinterpret_cast<const DEV_BROADCAST_HANDLE *>(broadcastHeader);
157 const auto it = findByHDevNotify(m_removableDrives.cbegin(), m_removableDrives.cend(),
158 broadcastHandle->dbch_hdevnotify);
159 if (it == m_removableDrives.cend())
160 return;
161 switch (volumeUuid(broadcastHandle->dbch_eventguid)) {
162 case UuidIoVolumeLock: // Received for removable USB media
164 break;
167 break;
169 break;
170 case UuidIoMediaRemoval: // Received for optical drives
171 break;
172 default:
173 break;
174 }
175}
176
177// Handle WM_DEVICECHANGE+DBT_DEVICEARRIVAL/DBT_DEVICEREMOVECOMPLETE which are
178// sent to all top level windows and cannot be registered for (that is, their
179// triggering depends on top level windows being present)
180inline void QWindowsRemovableDriveListener::handleDbtDriveArrivalRemoval(const MSG *msg)
181{
182 const DEV_BROADCAST_HDR *broadcastHeader = reinterpret_cast<const DEV_BROADCAST_HDR *>(msg->lParam);
183 switch (broadcastHeader->dbch_devicetype) {
184 case DBT_DEVTYP_HANDLE: // WM_DEVICECHANGE/DBT_DEVTYP_HANDLE is sent for our registered drives.
185 if (msg->wParam == DBT_DEVICEREMOVECOMPLETE) {
186 const DEV_BROADCAST_HANDLE *broadcastHandle = reinterpret_cast<const DEV_BROADCAST_HANDLE *>(broadcastHeader);
187 const auto it = findByHDevNotify(m_removableDrives.begin(), m_removableDrives.end(),
188 broadcastHandle->dbch_hdevnotify);
189 // Emit for removable USB drives we were registered for.
190 if (it != m_removableDrives.end()) {
193 m_removableDrives.erase(it);
194 }
195 }
196 break;
197 case DBT_DEVTYP_VOLUME: {
198 const DEV_BROADCAST_VOLUME *broadcastVolume = reinterpret_cast<const DEV_BROADCAST_VOLUME *>(broadcastHeader);
199 // WM_DEVICECHANGE/DBT_DEVTYP_VOLUME messages are sent to all toplevel windows. Compare a hash value to ensure
200 // it is handled only once.
201 const quintptr newHash = reinterpret_cast<quintptr>(broadcastVolume) + msg->wParam
202 + quintptr(broadcastVolume->dbcv_flags) + quintptr(broadcastVolume->dbcv_unitmask);
203 if (newHash == m_lastMessageHash)
204 return;
205 m_lastMessageHash = newHash;
206 // Check for DBTF_MEDIA (inserted/Removed Optical drives). Ignore for now.
207 if (broadcastVolume->dbcv_flags & DBTF_MEDIA)
208 return;
209 // Continue with plugged in USB media where dbcv_flags=0.
210 switch (msg->wParam) {
211 case DBT_DEVICEARRIVAL:
213 break;
214 case DBT_DEVICEREMOVECOMPLETE: // See above for handling of drives registered with watchers
216 break;
217 }
218 }
219 break;
220 }
221}
222
224{
225 const MSG *msg = reinterpret_cast<const MSG *>(messageIn);
226 if (msg->message == WM_DEVICECHANGE) {
227 switch (msg->wParam) {
228 case DBT_CUSTOMEVENT:
229 handleDbtCustomEvent(msg);
230 break;
231 case DBT_DEVICEARRIVAL:
232 case DBT_DEVICEREMOVECOMPLETE:
233 handleDbtDriveArrivalRemoval(msg);
234 break;
235 }
236 }
237 return false;
238}
239
240// Set up listening for WM_DEVICECHANGE+DBT_CUSTOMEVENT for a removable drive path,
242{
243 const wchar_t drive = p.size() >= 2 && p.at(0).isLetter() && p.at(1) == u':'
244 ? wchar_t(p.at(0).toUpper().unicode()) : L'\0';
245 if (!drive)
246 return;
247 // Already listening?
248 if (std::any_of(m_removableDrives.cbegin(), m_removableDrives.cend(),
249 [drive](const RemovableDriveEntry &e) { return e.drive == drive; })) {
250 return;
251 }
252
253 wchar_t devicePath[8] = L"\\\\.\\A:\\";
254 devicePath[4] = drive;
256 re.drive = drive;
257 if (GetDriveTypeW(devicePath + 4) != DRIVE_REMOVABLE)
258 return;
259 const HANDLE volumeHandle =
260 CreateFile(devicePath, FILE_READ_ATTRIBUTES,
261 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0,
262 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, // Volume requires BACKUP_SEMANTICS
263 0);
264 if (volumeHandle == INVALID_HANDLE_VALUE) {
265 qErrnoWarning("CreateFile %ls failed.", devicePath);
266 return;
267 }
268
269 DEV_BROADCAST_HANDLE notify;
270 ZeroMemory(&notify, sizeof(notify));
271 notify.dbch_size = sizeof(notify);
272 notify.dbch_devicetype = DBT_DEVTYP_HANDLE;
273 notify.dbch_handle = volumeHandle;
274 QThreadData *currentData = QThreadData::current();
275 QEventDispatcherWin32 *winEventDispatcher = static_cast<QEventDispatcherWin32 *>(currentData->ensureEventDispatcher());
276 re.devNotify = RegisterDeviceNotification(winEventDispatcher->internalHwnd(),
277 &notify, DEVICE_NOTIFY_WINDOW_HANDLE);
278 // Empirically found: The notifications also work when the handle is immediately
279 // closed. Do it here to avoid having to close/reopen in lock message handling.
280 CloseHandle(volumeHandle);
281 if (!re.devNotify) {
282 qErrnoWarning("RegisterDeviceNotification %ls failed.", devicePath);
283 return;
284 }
285
286 m_removableDrives.push_back(re);
287}
288
290// QWindowsFileSystemWatcherEngine
293 : handle(INVALID_HANDLE_VALUE), flags(0u)
294{
295}
296
299{
301 m_driveListener = new QWindowsRemovableDriveListener(this);
302 eventDispatcher->installNativeEventFilter(m_driveListener);
303 parent->setProperty("_q_driveListener",
304 QVariant::fromValue(static_cast<QObject *>(m_driveListener)));
309 QObject::connect(m_driveListener,
312 } else {
313 qWarning("QFileSystemWatcher: Removable drive notification will not work"
314 " if there is no QCoreApplication instance.");
315 }
316}
317
319{
320 for (auto *thread : std::as_const(threads))
321 thread->stop();
322 for (auto *thread : std::as_const(threads))
323 thread->wait();
324 qDeleteAll(threads);
325}
326
329 QStringList *directories)
330{
331 DEBUG() << "Adding" << paths.count() << "to existing" << (files->count() + directories->count()) << "watchers";
332 QStringList unhandled;
333 for (const QString &path : paths) {
334 auto sg = qScopeGuard([&] { unhandled.push_back(path); });
335 QFileInfo fileInfo(path);
336 fileInfo.stat();
337 if (!fileInfo.exists())
338 continue;
339
340 bool isDir = fileInfo.isDir();
341 if (isDir) {
342 if (directories->contains(path))
343 continue;
344 } else {
345 if (files->contains(path))
346 continue;
347 }
348
349 DEBUG() << "Looking for a thread/handle for" << fileInfo.path();
350
351 const QString absolutePath = isDir ? fileInfo.absoluteFilePath() : fileInfo.absolutePath();
352 const uint flags = isDir
353 ? (FILE_NOTIFY_CHANGE_DIR_NAME
354 | FILE_NOTIFY_CHANGE_ATTRIBUTES
355 | FILE_NOTIFY_CHANGE_FILE_NAME)
356 : (FILE_NOTIFY_CHANGE_DIR_NAME
357 | FILE_NOTIFY_CHANGE_FILE_NAME
358 | FILE_NOTIFY_CHANGE_ATTRIBUTES
359 | FILE_NOTIFY_CHANGE_SIZE
360 | FILE_NOTIFY_CHANGE_LAST_WRITE
361 | FILE_NOTIFY_CHANGE_SECURITY);
362
364 pathInfo.absolutePath = absolutePath;
365 pathInfo.isDir = isDir;
366 pathInfo.path = path;
367 pathInfo = fileInfo;
368
369 // Look for a thread
373 end = threads.constEnd();
374 for(jt = threads.constBegin(); jt != end; ++jt) {
375 thread = *jt;
376 const auto locker = qt_scoped_lock(thread->mutex);
377
378 const auto hit = thread->handleForDir.find(QFileSystemWatcherPathKey(absolutePath));
379 if (hit != thread->handleForDir.end() && hit.value().flags < flags) {
380 // Requesting to add a file whose directory has been added previously.
381 // Recreate the notification handle to add the missing notification attributes
382 // for files (FILE_NOTIFY_CHANGE_ATTRIBUTES...)
383 DEBUG() << "recreating" << absolutePath << Qt::hex << Qt::showbase << hit.value().flags
384 << "->" << flags;
386 if (fileHandle != INVALID_HANDLE_VALUE) {
387 const int index = thread->handles.indexOf(hit.value().handle);
388 const auto pit = thread->pathInfoForHandle.find(hit.value().handle);
389 Q_ASSERT(index != -1);
390 Q_ASSERT(pit != thread->pathInfoForHandle.end());
391 FindCloseChangeNotification(hit.value().handle);
392 thread->handles[index] = hit.value().handle = fileHandle;
393 hit.value().flags = flags;
394 auto value = std::move(*pit);
395 thread->pathInfoForHandle.erase(pit);
396 thread->pathInfoForHandle.insert(fileHandle, std::move(value));
397 }
398 }
399 // In addition, check on flags for sufficient notification attributes
400 if (hit != thread->handleForDir.end() && hit.value().flags >= flags) {
401 handle = hit.value();
402 // found a thread now insert...
403 DEBUG() << "Found a thread" << thread;
404
406 thread->pathInfoForHandle[handle.handle];
407 const QFileSystemWatcherPathKey key(fileInfo.absoluteFilePath());
408 if (!h.contains(key)) {
409 thread->pathInfoForHandle[handle.handle].insert(key, pathInfo);
410 if (isDir)
411 directories->append(path);
412 else
413 files->append(path);
414 }
415 sg.dismiss();
416 thread->wakeup();
417 break;
418 }
419 }
420
421 // no thread found, first create a handle
422 if (handle.handle == INVALID_HANDLE_VALUE) {
423 DEBUG() << "No thread found";
425 handle.flags = flags;
426 if (handle.handle == INVALID_HANDLE_VALUE)
427 continue;
428
429 // now look for a thread to insert
430 bool found = false;
431 for (QWindowsFileSystemWatcherEngineThread *thread : std::as_const(threads)) {
432 const auto locker = qt_scoped_lock(thread->mutex);
433 if (thread->handles.count() < MAXIMUM_WAIT_OBJECTS) {
434 DEBUG() << "Added handle" << handle.handle << "for" << absolutePath << "to watch" << fileInfo.absoluteFilePath()
435 << "to existing thread " << thread;
436 thread->handles.append(handle.handle);
437 thread->handleForDir.insert(QFileSystemWatcherPathKey(absolutePath), handle);
438
439 thread->pathInfoForHandle[handle.handle].insert(QFileSystemWatcherPathKey(fileInfo.absoluteFilePath()), pathInfo);
440 if (isDir)
441 directories->append(path);
442 else
443 files->append(path);
444
445 sg.dismiss();
446 found = true;
447 thread->wakeup();
448 break;
449 }
450 }
451 if (!found) {
453 DEBUG() << " ###Creating new thread" << thread << '(' << (threads.count()+1) << "threads)";
454 thread->handles.append(handle.handle);
455 thread->handleForDir.insert(QFileSystemWatcherPathKey(absolutePath), handle);
456
457 thread->pathInfoForHandle[handle.handle].insert(QFileSystemWatcherPathKey(fileInfo.absoluteFilePath()), pathInfo);
458 if (isDir)
459 directories->append(path);
460 else
461 files->append(path);
462
464 this, SIGNAL(fileChanged(QString,bool)));
466 this, SIGNAL(directoryChanged(QString,bool)));
467
468 thread->msg = '@';
469 thread->start();
470 threads.append(thread);
471 sg.dismiss();
472 }
473 }
474 }
475
476 if (Q_LIKELY(m_driveListener)) {
477 for (const QString &path : paths) {
478 if (!unhandled.contains(path))
479 m_driveListener->addPath(path);
480 }
481 }
482 return unhandled;
483}
484
487 QStringList *directories)
488{
489 DEBUG() << "removePaths" << paths;
490 QStringList unhandled;
491 for (const QString &path : paths) {
492 auto sg = qScopeGuard([&] { unhandled.push_back(path); });
493 QFileInfo fileInfo(path);
494 DEBUG() << "removing" << fileInfo.path();
495 QString absolutePath = fileInfo.absoluteFilePath();
497 end = threads.end();
498 for(jt = threads.begin(); jt!= end; ++jt) {
500 if (*jt == 0)
501 continue;
502
503 auto locker = qt_unique_lock(thread->mutex);
504
506 if (handle.handle == INVALID_HANDLE_VALUE) {
507 // perhaps path is a file?
508 absolutePath = fileInfo.absolutePath();
509 handle = thread->handleForDir.value(QFileSystemWatcherPathKey(absolutePath));
510 }
511 if (handle.handle != INVALID_HANDLE_VALUE) {
513 thread->pathInfoForHandle[handle.handle];
514 if (h.remove(QFileSystemWatcherPathKey(fileInfo.absoluteFilePath()))) {
515 // ###
516 files->removeAll(path);
517 directories->removeAll(path);
518 sg.dismiss();
519
520 if (h.isEmpty()) {
521 DEBUG() << "Closing handle" << handle.handle;
522 FindCloseChangeNotification(handle.handle); // This one might generate a notification
523
524 int indexOfHandle = thread->handles.indexOf(handle.handle);
525 Q_ASSERT(indexOfHandle != -1);
526 thread->handles.remove(indexOfHandle);
527
528 thread->handleForDir.remove(QFileSystemWatcherPathKey(absolutePath));
529 // h is now invalid
530
531 if (thread->handleForDir.isEmpty()) {
532 DEBUG() << "Stopping thread " << thread;
533 locker.unlock();
534 thread->stop();
535 thread->wait();
536 locker.lock();
537 // We can't delete the thread until the mutex locker is
538 // out of scope
539 }
540 }
541 }
542 // Found the file, go to next one
543 break;
544 }
545 }
546 }
547
548 // Remove all threads that we stopped
550 end = threads.end();
551 for(jt = threads.begin(); jt != end; ++jt) {
552 if (!(*jt)->isRunning()) {
553 delete *jt;
554 *jt = 0;
555 }
556 }
557
558 threads.removeAll(nullptr);
559 return unhandled;
560}
561
563// QWindowsFileSystemWatcherEngineThread
565
567 : msg(0)
568{
569 if (HANDLE h = CreateEvent(0, false, false, 0)) {
570 handles.reserve(MAXIMUM_WAIT_OBJECTS);
572 }
573}
574
575
577{
578 CloseHandle(handles.at(0));
579 handles[0] = INVALID_HANDLE_VALUE;
580
581 for (HANDLE h : std::as_const(handles)) {
582 if (h == INVALID_HANDLE_VALUE)
583 continue;
584 FindCloseChangeNotification(h);
585 }
586}
587
590{
591 QString str;
592 str += "QFileSystemWatcher: FindNextChangeNotification failed for"_L1;
593 for (const QWindowsFileSystemWatcherEngine::PathInfo &pathInfo : pathInfos)
594 str += " \""_L1 + QDir::toNativeSeparators(pathInfo.absolutePath) + u'"';
595 str += u' ';
596 return str;
597}
598
600{
601 auto locker = qt_unique_lock(mutex);
602 forever {
603 QList<HANDLE> handlesCopy = handles;
604 locker.unlock();
605 DEBUG() << "QWindowsFileSystemWatcherThread" << this << "waiting on" << handlesCopy.count() << "handles";
606 DWORD r = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(), false, INFINITE);
607 locker.lock();
608 do {
609 if (r == WAIT_OBJECT_0) {
610 int m = msg;
611 msg = 0;
612 if (m == 'q') {
613 DEBUG() << "thread" << this << "told to quit";
614 return;
615 }
616 if (m != '@')
617 DEBUG() << "QWindowsFileSystemWatcherEngine: unknown message sent to thread: " << char(m);
618 break;
619 }
620 if (r > WAIT_OBJECT_0 && r < WAIT_OBJECT_0 + uint(handlesCopy.count())) {
621 int at = r - WAIT_OBJECT_0;
622 Q_ASSERT(at < handlesCopy.count());
623 HANDLE handle = handlesCopy.at(at);
624
625 // When removing a path, FindCloseChangeNotification might actually fire a notification
626 // for some reason, so we must check if the handle exist in the handles vector
627 if (handles.contains(handle)) {
628 DEBUG() << "thread" << this << "Acknowledged handle:" << at << handle;
630 bool fakeRemove = false;
631
632 if (!FindNextChangeNotification(handle)) {
633 const DWORD error = GetLastError();
634
635 if (error == ERROR_ACCESS_DENIED) {
636 // for directories, our object's handle appears to be woken up when the target of a
637 // watch is deleted, before the watched thing is actually deleted...
638 // anyway.. we're given an error code of ERROR_ACCESS_DENIED in that case.
639 fakeRemove = true;
640 }
641
643 }
644 for (auto it = h.begin(); it != h.end(); /*erasing*/ ) {
645 QString absolutePath = it.value().absolutePath;
646 QFileInfo fileInfo(it.value().path);
647 DEBUG() << "checking" << it.key();
648
649 // i'm not completely sure the fileInfo.exist() check will ever work... see QTBUG-2331
650 // ..however, I'm not completely sure enough to remove it.
651 if (fakeRemove || !fileInfo.exists()) {
652 DEBUG() << it.key() << "removed!";
653 if (it.value().isDir)
654 emit directoryChanged(it.value().path, true);
655 else
656 emit fileChanged(it.value().path, true);
657 it = h.erase(it);
658
659 // close the notification handle if the directory has been removed
660 if (h.isEmpty()) {
661 DEBUG() << "Thread closing handle" << handle;
662 FindCloseChangeNotification(handle); // This one might generate a notification
663
664 int indexOfHandle = handles.indexOf(handle);
665 Q_ASSERT(indexOfHandle != -1);
666 handles.remove(indexOfHandle);
667
669 // h is now invalid
670 break;
671 }
672 continue;
673 } else if (it.value().isDir) {
674 DEBUG() << it.key() << "directory changed!";
675 emit directoryChanged(it.value().path, false);
676 it.value() = fileInfo;
677 } else if (it.value() != fileInfo) {
678 DEBUG() << it.key() << "file changed!";
679 emit fileChanged(it.value().path, false);
680 it.value() = fileInfo;
681 }
682 ++it;
683 }
684 }
685 } else {
686 // qErrnoWarning("QFileSystemWatcher: error while waiting for change notification");
687 break; // avoid endless loop
688 }
689 handlesCopy = handles;
690 r = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(), false, 0);
691 } while (r != WAIT_TIMEOUT);
692 }
693}
694
695
697{
698 msg = 'q';
699 SetEvent(handles.at(0));
700}
701
703{
704 msg = '@';
705 SetEvent(handles.at(0));
706}
707
709
710# include "qfilesystemwatcher_win.moc"
711# include "moc_qfilesystemwatcher_win_p.cpp"
static QAbstractEventDispatcher * instance(QThread *thread=nullptr)
Returns a pointer to the event dispatcher object for the specified thread.
\inmodule QtCore
Definition qbytearray.h:57
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
bool exists() const
Returns true if the file system entry this QFileInfo refers to exists; otherwise returns false.
void directoryChanged(const QString &path, bool removed)
void fileChanged(const QString &path, bool removed)
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
iterator end()
Definition qlist.h:626
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
const_iterator constBegin() const noexcept
Definition qlist.h:632
void remove(qsizetype i, qsizetype n=1)
Definition qlist.h:794
qsizetype removeAll(const AT &t)
Definition qlist.h:592
qsizetype count() const noexcept
Definition qlist.h:398
iterator begin()
Definition qlist.h:625
void reserve(qsizetype size)
Definition qlist.h:753
void append(parameter_type t)
Definition qlist.h:458
const_iterator constEnd() const noexcept
Definition qlist.h:633
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
bool setProperty(const char *name, const QVariant &value)
Sets the value of the object's name property to value.
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
iterator erase(const_iterator i)
Definition qset.h:145
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static Q_AUTOTEST_EXPORT QThreadData * current(bool createIfNecessary=true)
Definition qthread.cpp:1087
QAbstractEventDispatcher * ensureEventDispatcher()
Definition qthread_p.h:300
void start(Priority=InheritPriority)
Definition qthread.cpp:996
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
Definition qthread.cpp:1023
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
void fileChanged(const QString &path, bool removed)
void directoryChanged(const QString &path, bool removed)
QHash< Qt::HANDLE, PathInfoHash > pathInfoForHandle
QHash< QFileSystemWatcherPathKey, QWindowsFileSystemWatcherEngine::PathInfo > PathInfoHash
void driveLockForRemoval(const QString &)
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories) override
void driveLockForRemovalFailed(const QString &)
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories) override
void driveRemoved(const QString &)
void driveLockForRemovalFailed(const QString &)
void driveLockForRemoval(const QString &)
void driveRemoved(const QString &)
bool nativeEventFilter(const QByteArray &, void *messageIn, qintptr *) override
This method is called for every native event.
QWindowsRemovableDriveListener(QObject *parent=nullptr)
QString str
[2]
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & showbase(QTextStream &stream)
Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::ShowBase) on stream and r...
void * HANDLE
#define Q_LIKELY(x)
#define Q_DECL_COLD_FUNCTION
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static Qt::HANDLE createChangeNotification(const QString &path, uint flags)
static Q_DECL_COLD_FUNCTION QString msgFindNextFailed(const QWindowsFileSystemWatcherEngineThread::PathInfoHash &pathInfos)
static Iterator findByHDevNotify(Iterator i1, Iterator i2, HDEVNOTIFY hdevnotify)
#define DEBUG
static void stopDeviceNotification(QWindowsRemovableDriveListener::RemovableDriveEntry &e)
static QString pathFromEntry(const QWindowsRemovableDriveListener::RemovableDriveEntry &re)
#define forever
Definition qforeach.h:78
#define qWarning
Definition qlogging.h:166
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLsizei const GLfloat * v
[13]
GLuint64 GLenum void * handle
const GLfloat * m
GLuint64 key
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLsizei const GLuint * paths
GLbitfield flags
GLfloat GLfloat GLfloat GLfloat h
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLenum GLenum GLenum GLenum mapping
GLfloat GLfloat p
[1]
static QString absolutePath(const QString &path)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
#define qUtf16Printable(string)
Definition qstring.h:1543
#define QStringLiteral(str)
#define Q_OBJECT
#define signals
#define emit
size_t quintptr
Definition qtypes.h:167
unsigned int uint
Definition qtypes.h:34
ptrdiff_t qintptr
Definition qtypes.h:166
struct tagMSG MSG
QStringList files
[8]
QAction * at
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:962
bool contains(const AT &t) const noexcept
Definition qlist.h:45