Details
-
Bug
-
Resolution: Done
-
P3: Somewhat important
-
4.8.x, 5.6.2, 5.9.0
-
None
-
Windows 8.1, Qt 5.9.0, MingGW 5.3.0
-
e1d7f7dfbc0191f585fde8695d315e3ffb98ccc5 (qtbase/5.9, 7.7.2017, 5.9.2.)
Description
There is an issue whereby QFileSystemWatcher::removePath() will consistently fail to remove files or directories. This issue is exclusive to Windows and occurs on both Windows 8.1 and Windows 10 (1703), but does not occur on other operating systems (tested Linux Mint 18 and MacOS 10.12.5 without problems). I was using Qt 5.6.2, so I tested Qt 5.9.0, but found the issue is still present in that release. The bug can take two different forms depending on the order you add items to the QFileSystemWatcher.
1) QFileSystemWatcher::removePath() Fails to Remove Directories
Steps to recreate:
- Add a directory to a QFileSystemWatcher with addPath().
- Add some files from that directory to the same QFileSystemWatcher with addPath() or addPaths().
- Try to remove the directory with removePath() - the operation will fail every time.
- The files can be successfully removed, but even after removing the files, attempts to remove the directory will fail.
- The directory will still be present on the list returned by directories(), but no directoryChanged() signal will be emitted if the contents of the directory changes.
- If you try to re-add the directory with addPath(), the operation will fail because the directory is already on the watch list. This means that there's no way to get directoryChanged() signals working again after a failed attempt to remove a directory from the QFileSystemWatcher.
Sample Program to Illustrate Problem
#include <QCoreApplication> #include <QFileSystemWatcher> #include <QDebug> void RemoveItems(QFileSystemWatcher & fsWatcher, const char* desc, const QStringList & itemList); void PrintLists(const QStringList & dirList, const QStringList & fileList); void PrintList(const QStringList & itemList); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QFileSystemWatcher fsWatcher; fsWatcher.addPath("G:/TestFiles/Test1"); fsWatcher.addPath("G:/TestFiles/Test1/File 1-1.txt"); fsWatcher.addPath("G:/TestFiles/Test1/File 1-2.txt"); fsWatcher.addPath("G:/TestFiles/Test1/File 1-3.txt"); RemoveItems(fsWatcher, "Directories", fsWatcher.directories()); RemoveItems(fsWatcher, "Files", fsWatcher.files()); PrintLists(fsWatcher.directories(), fsWatcher.files()); fsWatcher.addPath("G:/TestFiles/Test2"); fsWatcher.addPath("G:/TestFiles/Test2/File 2-1.txt"); fsWatcher.addPath("G:/TestFiles/Test2/File 2-2.txt"); fsWatcher.addPath("G:/TestFiles/Test2/File 2-3.txt"); RemoveItems(fsWatcher, "Directories", fsWatcher.directories()); RemoveItems(fsWatcher, "Files", fsWatcher.files()); PrintLists(fsWatcher.directories(), fsWatcher.files()); return a.exec(); } void RemoveItems(QFileSystemWatcher & fsWatcher, const char* desc, const QStringList & itemList) { printf("Removing %d %s\n", itemList.size(), desc); QStringList failList = fsWatcher.removePaths(itemList); if (failList.isEmpty()) { printf("%s removed successfully\n", desc); } else { QStringList::const_iterator item; for (item = failList.constBegin() ; item != failList.constEnd() ; ++item) printf("Remove fail - %s\n", item->toLatin1().data()); } printf("\n"); } void PrintLists(const QStringList & dirList, const QStringList & fileList) { printf("Current Watch List\n"); if (dirList.isEmpty() && fileList.isEmpty()) { printf("[Empty]\n\n"); } else { PrintList(dirList); PrintList(fileList); printf("\n"); } } void PrintList(const QStringList & itemList) { if (itemList.isEmpty()) return; QStringList::const_iterator item; for (item = itemList.constBegin() ; item != itemList.constEnd() ; ++item) printf("%s\n", item->toLatin1().data()); }
Program Output
As you can see below, removal of the files succeeds, but attempts to remove the directories fail, and both directories remain on the QFileSystemWatcher::directories() list.
Removing 1 Directories Remove fail - G:/TestFiles/Test1 Removing 3 Files Files removed successfully Current Watch List G:/TestFiles/Test1 Removing 2 Directories Remove fail - G:/TestFiles/Test1 Remove fail - G:/TestFiles/Test2 Removing 3 Files Files removed successfully Current Watch List G:/TestFiles/Test1 G:/TestFiles/Test2
2) QFileSystemWatcher::removePath() Fails to Remove Files
Steps to recreate:
- Add some files from a directory to a QFileSystemWatcher with addPath() or addPaths().
- Add the directory in which the files are contained to the same QFileSystemWatcher with addPath().
- Try to remove the files with removePath() or removePaths() - the operation will fail every time.
- The directory can be successfully removed, but even after removing the directory, attempts to remove files will fail.
- The files will still be present on the list returned by files(), but no fileChanged() signal will be emitted if a file is modified.
- If you try to re-add the files with addPath() or addPaths(), the operation will fail because the files are already on the watch list. This means that there's no way to get fileChanged() signals working again after a failed attempt to remove a file from the QFileSystemWatcher.
Sample Program to Illustrate Problem
Same program as above, but with the below main() function. The only difference is that the order of adding the items has been changed so that the files are added first, then the directory.
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QFileSystemWatcher fsWatcher; fsWatcher.addPath("G:/TestFiles/Test1/File 1-1.txt"); fsWatcher.addPath("G:/TestFiles/Test1/File 1-2.txt"); fsWatcher.addPath("G:/TestFiles/Test1/File 1-3.txt"); fsWatcher.addPath("G:/TestFiles/Test1"); RemoveItems(fsWatcher, "Directories", fsWatcher.directories()); RemoveItems(fsWatcher, "Files", fsWatcher.files()); PrintLists(fsWatcher.directories(), fsWatcher.files()); fsWatcher.addPath("G:/TestFiles/Test2/File 2-1.txt"); fsWatcher.addPath("G:/TestFiles/Test2/File 2-2.txt"); fsWatcher.addPath("G:/TestFiles/Test2/File 2-3.txt"); fsWatcher.addPath("G:/TestFiles/Test2"); RemoveItems(fsWatcher, "Directories", fsWatcher.directories()); RemoveItems(fsWatcher, "Files", fsWatcher.files()); PrintLists(fsWatcher.directories(), fsWatcher.files()); return a.exec(); }
Program Output
As you can see below, removal of the now directories succeeds, but attempts to remove the files fail, and all six files remaining on the QFileSystemWatcher::files() list.
Removing 1 Directories Directories removed successfully Removing 3 Files Remove fail - G:/TestFiles/Test1/File 1-1.txt Remove fail - G:/TestFiles/Test1/File 1-2.txt Remove fail - G:/TestFiles/Test1/File 1-3.txt Current Watch List G:/TestFiles/Test1/File 1-1.txt G:/TestFiles/Test1/File 1-2.txt G:/TestFiles/Test1/File 1-3.txt Removing 1 Directories Directories removed successfully Removing 6 Files Remove fail - G:/TestFiles/Test1/File 1-1.txt Remove fail - G:/TestFiles/Test1/File 1-2.txt Remove fail - G:/TestFiles/Test1/File 1-3.txt Remove fail - G:/TestFiles/Test2/File 2-1.txt Remove fail - G:/TestFiles/Test2/File 2-2.txt Remove fail - G:/TestFiles/Test2/File 2-3.txt Current Watch List G:/TestFiles/Test1/File 1-1.txt G:/TestFiles/Test1/File 1-2.txt G:/TestFiles/Test1/File 1-3.txt G:/TestFiles/Test2/File 2-1.txt G:/TestFiles/Test2/File 2-2.txt G:/TestFiles/Test2/File 2-3.txt
Temporary Workaround
To avoid this problem use two separate QFileSystemWatchers, one for files and the for directories.
Sample Program To Illustrate Workaround
Same as the first program, but with the below main() function. The difference is it uses separate QFileSystemWatchers for files and directories.
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QFileSystemWatcher fsWatcherDir; QFileSystemWatcher fsWatcherFile; fsWatcherDir.addPath("G:/TestFiles/Test1"); fsWatcherFile.addPath("G:/TestFiles/Test1/File 1-1.txt"); fsWatcherFile.addPath("G:/TestFiles/Test1/File 1-2.txt"); fsWatcherFile.addPath("G:/TestFiles/Test1/File 1-3.txt"); RemoveItems(fsWatcherDir, "Directories", fsWatcherDir.directories()); RemoveItems(fsWatcherFile, "Files", fsWatcherFile.files()); PrintLists(fsWatcherDir.directories(), fsWatcherFile.files()); fsWatcherDir.addPath("G:/TestFiles/Test2"); fsWatcherFile.addPath("G:/TestFiles/Test2/File 2-1.txt"); fsWatcherFile.addPath("G:/TestFiles/Test2/File 2-2.txt"); fsWatcherFile.addPath("G:/TestFiles/Test2/File 2-3.txt"); RemoveItems(fsWatcherDir, "Directories", fsWatcherDir.directories()); RemoveItems(fsWatcherFile, "Files", fsWatcherFile.files()); PrintLists(fsWatcherDir.directories(), fsWatcherFile.files()); return a.exec(); }
Program Output
As you can see below, using separate QFileSystemWatchers for files and directories allows the program to work as intended.
Removing 1 Directories Directories removed successfully Removing 3 Files Files removed successfully Current Watch List [Empty] Removing 1 Directories Directories removed successfully Removing 3 Files Files removed successfully Current Watch List [Empty]