QFile::copy returns true even though copy failed in Windows - windows

I have QString variable named "src", which holds a filename. Operation QFile::copy(src, target) works Ok, until target is "C:" or "C:/" (I have the problem in Windows 10). In this case the operation returns true, even though I do not see any files actually copied to C:/ (in fact, normally I cannot copy anything to C:/ without administrator rights). Moreover, when I debug, I see that it says it has copied to C:// (two slashes). Is it a Qt bug or do I miss something?
UPD: Copying, e.g., to C:/Users which also requires administrator rights fails, as it should (returns false). Qt version is 5.7.
UPD:
QString src = "C:/Stuff/somefile.pdf";
QString target = "C:/somefile.pdf";
if (QFile::copy(src, target)) qDebug() << "Copy successful";
else qDebug() << "Copy failed";
This code yields "Copy succeful", whereas neither do I have write access to C:/ nor actually file somefile.pdf appears there.

When your application does not have permissions for writing to some directories, the files are stored into the Windows Virtual Store located at C:\Users\%USERNAME%\AppData\Local\VirtualStore. So in fact your file is successfully saved (albeit the path is redirected), that is why QFile::copy() returns true.
This redirection is called UAC Virtualization, and it works for C:, C:\Program Files, C:\Windows and HKLM\Software.
I guess the most genuine way to avoid such problems is to stick to saving configuration data in the designated locations such as application data (which in the context of the Qt framework will also meet the terms of cross-platform code).

Related

Qt loading deleted/renamed file with Windows 10

I'm seeing some weird behavior when running a test install of my Qt program (tried using qt 5.5.1 and 7.0). I haven't noticed the issue when running in a debug/development environment - but see the issue when installed into "Program Files (x86)".
The issue is: I'm using QDirIterator to find database files within the "QStandardPaths::DataLocation" locations and loading them in via sqlite. The phantom files are located in Program Files (x86)//Library/.ndat What I'm seeing is that files from a previous install (which have been deleted) and ones that have been renamed, then deleted, still show up and are readable in the program. These "phantom" files have been blocking loading of the up-to-date file. It's really strange - I wonder if anyone has seen the issue?
I'm running Windows 10 Home on an SSD-based machine (if it matters). Same issue with Qt 5.5.1 and 5.7. I've replicated it on a different machine with similar configuration.
Any ideas?
Here's a summary of my code:
QStringList standardPaths = QStandardPaths::locateAll(QStandardPaths::DataLocation, "Library", QStandardPaths::LocateDirectory);
QStringList fileFilters;
fileFilters << "*.ndat";
foreach (const QString &dir, standardPaths) {
QDirIterator iterator (dir, fileFilters);
while (iterator.hasNext()) {
const QString &filePath = iterator.next();
QString databaseName = QFileInfo(filePath).baseName();
database_->open(filePath, baseName); // my function
}
}
boolDataManager::open (const QString &filePath, const QString &connectionName) {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
db.setDatabaseName (filePath);
if (!db.open()) {
ERROR(QString("Cannot open database %1 with error %2")
.arg(QFileInfo(filePath).baseName())
.arg(db.lastError().text()));
printError();
return false;
}
databaseNames_.append(connectionName);
return true;
}
This code seems to read in files that don't exist anymore - and strangely, reads contents of old files that have been overwritten in the same spot. It only seems to happen when the files are located within the "Program Files" directory; not in a user directory or what-not.
For example, version 1 of my code had a database called "database.dat" with 10 entries. Version 2 of my install overwrote the file with a file of the same name with 20 entries. Version 2 of my code finds the database.dat file but only reads in the older version with 10 entries - Really weird!
Update
It appears that these "phantom" files are stored at:
C:\Users/USERNAME/AppData/Local/VirtualStore/Program Files (x86)/PROGRAM NAME/database.dat
My guess is that I'm opening the file in my program not as read-only so Windows creates a working copy in a user-writable location. Will investigate.
The issue is Windows caching - I think - One cant really tell with software that doesn't provide any way to debug it - such as Windows.
I've heard that this solution can also be solved (or at least decreased) by turning on the "Application Experience" Service -- I still run into it from time to time, typically when doing too many Filesystem writes in too short of a time.
I dont know exactly what the cause is -- and I'm pretty sure nobody else does or it would have been fixed.. but as far as I know there is no fix for that (as of this answer's date)
--
Here's my solution to problems like this that works 100% of the time:
To avoid this problem, append the version number to the end of your database's filename each time you compile, in fact apppend it to all your files by using a
#define VERSION 4.22.21
and then just adding .append(QString("%1").arg(VERSION)); or something.
All you have to really do then is write up some quick code to import all the necessary data from an old database or from wherever, which you should have more or less anyways just from using the database.
Better to avoid situations like that than to try and figure them out -- not to mention you now have a perfect revision system without even trying.
UPDATE
Since theres no use case, no code, and no information about the project, I would have to guess at what you were trying to do -
QList<DataDir*> dataDirectories;
DataDir* new_dataDir;
QStringList standardPaths = QStandardPaths::locateAll(QStandardPaths::DataLocation, "Library", QStandardPaths::LocateDirectory);
QStringList fileFilters;
fileFilters << "*.ndat";
foreach (const QString &dir, standardPaths) {
QDirIterator iterator (dir, fileFilters);
while (iterator.hasNext()) {
const QString &filePath = iterator.next();
QString databaseName = QFileInfo(filePath).baseName();
database_->open(filePath, baseName); // my function
/* Do your database reading or writing and save the results
* into a QHash or something then do this: */
database_->close(); // super important
}
}
After a bunch of poking around, I found the source of the problem (and solution).
Windows (for backwards compatibility) has a VirtualStore function where if the program tries to write to a unwritable file (based on permissions, e.g. Program Files/Progname/test.txt), it'll copy that file into USER/AppData/Local/VirtualStore/Program Files/.... This new file is not deleted when the program is uninstalled, but looks to the QT program as residing at its original location.
The solution is to open the Sqlite database in read only mode:
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
if (!writable_)
db.setConnectOptions(QLatin1String("QSQLITE_OPEN_READONLY"));
db.setDatabaseName (filePath);
Now, I'm running into a problem determining whether the file is writable. This:
writable_ = fInfo.isWritable();
always returns true, even for files in Program Files. Even when enabling NTFS permissions checking:
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
qt_ntfs_permission_lookup++; // turn permisssions checking on
the permissions check doesn't work. So now I'm simply doing this:
QString appDir = gApp->applicationDirPath();
QString relFilepath = QDir(appDir).relativeFilePath(filePath);
if (!relFilepath.startsWith(".."))
writable_ = false;
Database is read only (OK for my application) and no longer creates anything within VirtualStore

Processing 3.0 launch() function doesn't launch my .exe

Processing 3.0 launch function doesn't launch my .exe.
I am using the Launch() function (https://processing.org/reference/launch_.html)
launch("C:/Program Files (x86)/Google/Chrome/Application/chrome.exe");
Or
launch("C:/app/keyboard.exe");
Result: Chrome browser will open. keyboard.exe will not. I've tryed different locations and relative paths.
I only get a windows loader when the link is correct. So that is correct.
The function discriptions says this:
"Be sure to make the file executable before attempting to open it (chmod +x). "
https://superuser.com/questions/106181/equivalent-of-chmod-to-change-file-permissions-in-windows
I also made a .bat file to execute the .exe but the launch() function only works on exe files.
but that didnt work either.
System:
Processing 3.0
Java 8
Windows 10, 64 bit
So what am I missing?
It is a bit dodgy but works in windows 8:
PrintWriter output=null;
output = createWriter("myfile.bat");
output.println("cd "+sketchPath(""));
output.println("start archivo.exe");
output.flush();
output.close();
output=null;
launch(sketchPath("")+"myfile.bat");
And you can choose another relative or absolute path
for instance
output.println("cd ..");
output.println("cd directoriy");
...
As Samuil advises, Windows uses \ instead of a / as a separator character, which you'll need to escape, hence \\: launch("C:\\app\\keyboard.exe");
I recommend using File.separator:
launch("C:"+File.separator+"app"+File.separator+"keyboard.exe");
It's a bit longer, but will work regardless of the operating system(Linux/OSX/Windows/etc.).
Aside launch(), also try exec():
exec(new String[]{"start","C:"+File.separator+"app"+File.separator+"keyboard.exe");
also Process. (If you need to check the output, you may need to write your own thread that will pipe the output)

Trying to open a file in C++, but the file cannot be found

I have an algorithm in C++ (main.cpp) and I use CLion to compile and run it. Algorithm would read strings from text file, but there is a mistake:
Could not open data.txt (file exists and placed in one folder with main.cpp)
How can I fix it and make this file "visible" to CLion?
If you are using fopen or something similar and just passing "data.txt", it is assumed that that file is in the current working directory of the running program (the one you just compiled).
So, either
Give a full path instead, like fopen("/full/path/to/data.txt"), where you use the actual full path
(not preferable), Move data.txt to the directory where CLion runs its compiled programs from.
(for #2, here's a hacky way to get that directory)
char buf[1024]; // hack, but fine for this
printf("%s\n", getcwd(buf, 1024));
Run/Edit configurations...
Select your application (on the lefthandside of the window)
Specify Working directory
Apply
Now you can fopen relatively from working directory.
I found another way to solve this problem.
#Lou Franco's solution may affect the project structure. For example, if I deploy code on a server, I should move the resource file to specific directory.
What I do is modify the CmakeLists.txt, on Windows, using
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "D:\\science\\code\\English-Prediction")
CMAKE_RUNTIME_OUTPUT_DIRECTORY is a CMake variable, it assigns the work directory of CLion work directory.
Continuing with the CMAKE_RUNTIME_OUTPUT_DIRECTORY CMakeLists variables, I do the following. In the root directory of my project, I create a directory, e.g., out. Then, in my CMakeLists.txt I set the CMAKE_RUNTIME_OUTPUT_DIRECTORY to that directory:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/out)
Note, that must come before you have
add_executable(YourProject ${SOURCE_FILES})
I might also add that instead of using fopen() I would keep it more object-oriented by using std::ifstream:
std::ifstream inFile("data.txt");
// check if it opened without issue...
if (!inFile) {
processError(); // a user-defined function to deal with the issue
} else {
// All is good, carry on...
// and when you're done don't forget
inFile.close();
}

What determines the location of the 'pathdef.m' file on Windows?

http://www.mathworks.com/support/solutions/en/data/1-5YQCPR/index.html?product=ML says:
By default, the 'pathdef.m' file may be located in either the
'$MATLABROOT/toolbox/local' directory or the '$USERPATH' directory,
where $MATLABROOT and $USERPATH are the directories displayed after
entering the commands matlabroot (e.g. C:\Program Files\MATLAB\R2013b) and userpath (e.g. C:\Users\francky\Documents\MATLAB)
So, what determines the location of the pathdef.m file on Windows (matlabroot vs. userpath)?
According to this help page:
By default, pathdef.m is in matlabroot/toolbox/local.
However, there is apparently more to it than that.
If we add matlabpath to the top of matlabrc.m, it will tell use the search path before it has even "set up" the search path:
MATLABPATH
C:\Program Files (x86)\MATLAB\R2013a\toolbox\local
>>
So the only thing on the path is matlabroot/toolbox/local and that's where MATLAB will find pathdef.m by default. Right? I thought so, but a simple test with a pathdef.m in userpath proved that in fact userpath was the first priority for pathdef.m. Why? Because in MATLAB, the working directory takes priority over anything on the matlabpath, and the startup folder is determined by userpath!
There are multiple ways to specify the startup working directory, with and without the use of userpath's functional form. I just verified that changing the "Start in:" property of the Windows shortcut will prevent the pathdef.m in the default userpath from running. You can achieve the same change in startup folder with the userpath(path) syntax, but then what would be the difference between the startup path and userpath unless you use the shortcut "Start in:" method?
To add to the confusion, the last line of the default pathdef.m under matlabroot/toolbox/local is p = [userpath,p];, so after matlabrc.m adds this to the path on startup, MATLAB will then give userpath precedence over matlabroot, if ther is a pathdef.m under userpath.

Checking if a Folder/File is Hidden/System in Windows C/C++

I am writing a Cross platform application using C++/STL/Boost and I realized they do not provide a way to check if a folder or file is hidden or is a system file in Windows.
What's the simplest way to do this in C/C++ for Windows ?
Ideally I have a std::string with the path (either to a file or folder), and would return if it's hidden or is a system file. best if it works across all windows versions. I am using MinGW g++ to compile this as well.
GetFileAttributes will work for this.
It takes a path to either a file or a directory as a parameter and returns set of flags including FILE_ATTRIBUTE_HIDDEN and FILE_ATTRIBUTE_SYSTEM.
DWORD attributes = GetFileAttributes(path);
if (attributes & FILE_ATTRIBUTE_HIDDEN) ...
if (attributes & FILE_ATTRIBUTE_SYSTEM) ...

Resources