15#include <private/qlocking_p.h>
22# include <private/qeventdispatcher_win_p.h>
23# include <private/qthread_p.h>
36# define DEBUG if (false) qDebug
44 if ((
flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) == 0 && !nativePath.
endsWith(u
'\\'))
46 const HANDLE result = FindFirstChangeNotification(
reinterpret_cast<const wchar_t *
>(nativePath.
utf16()),
86 static VolumeUuid volumeUuid(
const UUID &needle);
87 void handleDbtCustomEvent(
const MSG *msg);
88 void handleDbtDriveArrivalRemoval(
const MSG *msg);
90 std::vector<RemovableDriveEntry> m_removableDrives;
96 , m_lastMessageHash(0)
102 UnregisterDeviceNotification(
e.devNotify);
106template <
class Iterator>
109 return std::find_if(i1, i2,
130 static const struct VolumeUuidMapping
136 {0x50708874, 0xc9af, 0x11d1, {0x8f, 0xef, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} },
138 {0xae2eed10, 0x0ba8, 0x11d2, {0x8f, 0xfb, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} },
140 {0x9a8c3d68, 0xd0cb, 0x11d1, {0x8f, 0xef, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} },
142 {0xd07433c1, 0xa98e, 0x11d2, {0x91, 0x7a, 0x0, 0xa0, 0xc9, 0x06, 0x8f, 0xf3}} }
146 const VolumeUuidMapping *
m =
147 std::find_if(
mapping,
end, [&needle] (
const VolumeUuidMapping &
m) {
return IsEqualGUID(
m.uuid, needle); });
151inline void QWindowsRemovableDriveListener::handleDbtCustomEvent(
const MSG *msg)
153 const DEV_BROADCAST_HDR *broadcastHeader =
reinterpret_cast<const DEV_BROADCAST_HDR *
>(msg->lParam);
154 if (broadcastHeader->dbch_devicetype != DBT_DEVTYP_HANDLE)
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())
161 switch (volumeUuid(broadcastHandle->dbch_eventguid)) {
180inline void QWindowsRemovableDriveListener::handleDbtDriveArrivalRemoval(
const MSG *msg)
182 const DEV_BROADCAST_HDR *broadcastHeader =
reinterpret_cast<const DEV_BROADCAST_HDR *
>(msg->lParam);
183 switch (broadcastHeader->dbch_devicetype) {
184 case DBT_DEVTYP_HANDLE:
185 if (msg->wParam == DBT_DEVICEREMOVECOMPLETE) {
186 const DEV_BROADCAST_HANDLE *broadcastHandle =
reinterpret_cast<const DEV_BROADCAST_HANDLE *
>(broadcastHeader);
188 broadcastHandle->dbch_hdevnotify);
190 if (
it != m_removableDrives.end()) {
193 m_removableDrives.erase(
it);
197 case DBT_DEVTYP_VOLUME: {
198 const DEV_BROADCAST_VOLUME *broadcastVolume =
reinterpret_cast<const DEV_BROADCAST_VOLUME *
>(broadcastHeader);
201 const quintptr newHash =
reinterpret_cast<quintptr>(broadcastVolume) + msg->wParam
202 +
quintptr(broadcastVolume->dbcv_flags) +
quintptr(broadcastVolume->dbcv_unitmask);
203 if (newHash == m_lastMessageHash)
205 m_lastMessageHash = newHash;
207 if (broadcastVolume->dbcv_flags & DBTF_MEDIA)
210 switch (msg->wParam) {
211 case DBT_DEVICEARRIVAL:
214 case DBT_DEVICEREMOVECOMPLETE:
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);
231 case DBT_DEVICEARRIVAL:
232 case DBT_DEVICEREMOVECOMPLETE:
233 handleDbtDriveArrivalRemoval(msg);
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';
248 if (std::any_of(m_removableDrives.cbegin(), m_removableDrives.cend(),
253 wchar_t devicePath[8] = L
"\\\\.\\A:\\";
254 devicePath[4] = drive;
257 if (GetDriveTypeW(devicePath + 4) != DRIVE_REMOVABLE)
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,
264 if (volumeHandle == INVALID_HANDLE_VALUE) {
269 DEV_BROADCAST_HANDLE notify;
270 ZeroMemory(¬ify,
sizeof(notify));
271 notify.dbch_size =
sizeof(notify);
272 notify.dbch_devicetype = DBT_DEVTYP_HANDLE;
273 notify.dbch_handle = volumeHandle;
277 ¬ify, DEVICE_NOTIFY_WINDOW_HANDLE);
280 CloseHandle(volumeHandle);
282 qErrnoWarning(
"RegisterDeviceNotification %ls failed.", devicePath);
286 m_removableDrives.push_back(re);
302 eventDispatcher->installNativeEventFilter(m_driveListener);
313 qWarning(
"QFileSystemWatcher: Removable drive notification will not work"
314 " if there is no QCoreApplication instance.");
320 for (
auto *
thread : std::as_const(threads))
322 for (
auto *
thread : std::as_const(threads))
331 DEBUG() <<
"Adding" <<
paths.count() <<
"to existing" << (
files->count() + directories->count()) <<
"watchers";
337 if (!fileInfo.exists())
340 bool isDir = fileInfo.isDir();
342 if (directories->contains(
path))
349 DEBUG() <<
"Looking for a thread/handle for" << fileInfo.path();
351 const QString absolutePath = isDir ? fileInfo.absoluteFilePath() : fileInfo.absolutePath();
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);
365 pathInfo.
isDir = isDir;
376 const auto locker = qt_scoped_lock(
thread->mutex);
379 if (hit !=
thread->handleForDir.end() && hit.value().flags <
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);
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));
400 if (hit !=
thread->handleForDir.end() && hit.value().flags >=
flags) {
408 if (!
h.contains(
key)) {
411 directories->append(
path);
422 if (
handle.handle == INVALID_HANDLE_VALUE) {
423 DEBUG() <<
"No thread found";
426 if (
handle.handle == INVALID_HANDLE_VALUE)
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;
441 directories->append(
path);
453 DEBUG() <<
" ###Creating new thread" <<
thread <<
'(' << (threads.
count()+1) <<
"threads)";
459 directories->append(
path);
478 if (!unhandled.contains(
path))
494 DEBUG() <<
"removing" << fileInfo.path();
498 for(jt = threads.
begin(); jt!=
end; ++jt) {
503 auto locker = qt_unique_lock(
thread->mutex);
506 if (
handle.handle == INVALID_HANDLE_VALUE) {
511 if (
handle.handle != INVALID_HANDLE_VALUE) {
517 directories->removeAll(
path);
522 FindCloseChangeNotification(
handle.handle);
524 int indexOfHandle =
thread->handles.indexOf(
handle.handle);
526 thread->handles.remove(indexOfHandle);
531 if (
thread->handleForDir.isEmpty()) {
551 for(jt = threads.
begin(); jt !=
end; ++jt) {
552 if (!(*jt)->isRunning()) {
569 if (
HANDLE h = CreateEvent(0,
false,
false, 0)) {
579 handles[0] = INVALID_HANDLE_VALUE;
582 if (
h == INVALID_HANDLE_VALUE)
584 FindCloseChangeNotification(
h);
592 str +=
"QFileSystemWatcher: FindNextChangeNotification failed for"_L1;
601 auto locker = qt_unique_lock(
mutex);
605 DEBUG() <<
"QWindowsFileSystemWatcherThread" <<
this <<
"waiting on" << handlesCopy.
count() <<
"handles";
606 DWORD
r = WaitForMultipleObjects(handlesCopy.
count(), handlesCopy.
constData(),
false, INFINITE);
609 if (
r == WAIT_OBJECT_0) {
613 DEBUG() <<
"thread" <<
this <<
"told to quit";
617 DEBUG() <<
"QWindowsFileSystemWatcherEngine: unknown message sent to thread: " << char(
m);
620 if (
r > WAIT_OBJECT_0 &&
r < WAIT_OBJECT_0 +
uint(handlesCopy.
count())) {
621 int at =
r - WAIT_OBJECT_0;
628 DEBUG() <<
"thread" <<
this <<
"Acknowledged handle:" <<
at <<
handle;
630 bool fakeRemove =
false;
632 if (!FindNextChangeNotification(
handle)) {
633 const DWORD
error = GetLastError();
635 if (
error == ERROR_ACCESS_DENIED) {
647 DEBUG() <<
"checking" <<
it.key();
651 if (fakeRemove || !fileInfo.
exists()) {
652 DEBUG() <<
it.key() <<
"removed!";
653 if (
it.value().isDir)
662 FindCloseChangeNotification(
handle);
673 }
else if (
it.value().isDir) {
674 DEBUG() <<
it.key() <<
"directory changed!";
676 it.value() = fileInfo;
677 }
else if (
it.value() != fileInfo) {
678 DEBUG() <<
it.key() <<
"file changed!";
680 it.value() = fileInfo;
690 r = WaitForMultipleObjects(handlesCopy.
count(), handlesCopy.
constData(),
false, 0);
691 }
while (
r != WAIT_TIMEOUT);
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.
static constexpr QChar fromLatin1(char c) noexcept
Converts the Latin-1 character c to its equivalent QChar.
static QString toNativeSeparators(const QString &pathName)
\inmodule QtCore \reentrant
bool exists() const
Returns true if the file 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.
const_pointer constData() const noexcept
const_reference at(qsizetype i) const noexcept
const_iterator constBegin() const noexcept
void remove(qsizetype i, qsizetype n=1)
qsizetype removeAll(const AT &t)
qsizetype count() const noexcept
void reserve(qsizetype size)
void append(parameter_type t)
const_iterator constEnd() const noexcept
QObject * parent() const
Returns a pointer to the parent object.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
QThread * thread() const
Returns the thread in which the object lives.
bool setProperty(const char *name, const QVariant &value)
Sets the value of the object's name property to value.
iterator erase(const_iterator i)
\macro QT_RESTRICTED_CAST_FROM_ASCII
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
QString & append(QChar c)
static Q_AUTOTEST_EXPORT QThreadData * current(bool createIfNecessary=true)
QAbstractEventDispatcher * ensureEventDispatcher()
void start(Priority=InheritPriority)
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
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 >
void fileChanged(const QString &path, bool removed)
~QWindowsFileSystemWatcherEngineThread()
HandleForDirHash handleForDir
void directoryChanged(const QString &path, bool removed)
QList< Qt::HANDLE > handles
QHash< Qt::HANDLE, PathInfoHash > pathInfoForHandle
QWindowsFileSystemWatcherEngineThread()
void driveLockForRemoval(const QString &)
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories) override
~QWindowsFileSystemWatcherEngine()
void driveLockForRemovalFailed(const QString &)
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories) override
void driveRemoved(const QString &)
QWindowsFileSystemWatcherEngine(QObject *parent)
void addPath(const QString &path)
void driveLockForRemovalFailed(const QString &)
void driveLockForRemoval(const QString &)
void driveRemoved(const QString &)
~QWindowsRemovableDriveListener()
bool nativeEventFilter(const QByteArray &, void *messageIn, qintptr *) override
This method is called for every native event.
QWindowsRemovableDriveListener(QObject *parent=nullptr)
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...
#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)
static void stopDeviceNotification(QWindowsRemovableDriveListener::RemovableDriveEntry &e)
static QString pathFromEntry(const QWindowsRemovableDriveListener::RemovableDriveEntry &re)
GLsizei const GLfloat * v
[13]
GLuint64 GLenum void * handle
GLsizei const GLuint * paths
GLfloat GLfloat GLfloat GLfloat h
GLsizei const GLchar *const * path
GLenum GLenum GLenum GLenum mapping
static QString absolutePath(const QString &path)
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
#define qUtf16Printable(string)
#define QStringLiteral(str)
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
bool contains(const AT &t) const noexcept
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent