Surprisingly, in the course of my professional activity, I stumbled upon one interesting feature of QFile. With it, you can check whether it is possible to read or write information to a file. But at the same time, QFile ignores read and write permissions of the file if the file is opened by several instances of the same program. This means that if the file is opened in another instance of the program, then QFile will determine this file as readable or writable, and will also be able to write to the file successfully.
Description of the problem
The write ban check can be performed as follows, but this does not always work, as I said above. But you can check it like this
bool isFileWritable(const QString& fileName) { QFile file(fileName); bool isWritable = file.open(QFile::ReadWrite); file.close(); return isWritable; }
However, you can verify the statement said at the very beginning of the article using the following code.
#include <QCoreApplication> #include <QFile> #include <QDebug> #include <QDateTime> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // Подставьте любой удобный для вас путь к файлу QFile file("D:/check.txt"); file.write(QDateTime::currentDateTime().toString().toLatin1()); file.flush(); return a.exec(); }
After building the program, run several instances together and you will see that the contents of the file will correspond to the time of the last launched instance. That is, QFile completely ignores the access rights and ownership of the file by other processes. However, if you run this program, and then try to overwrite the contents of the file with another program, for example standard Notepad, you will receive the following notification.
Decision
I found a solution to this situation in the use of platform-dependent functionality in WinAPI, analogues for other OS are probably possible.
To do this, you need to create a unique pointer to the input/output stream
std::wofstream
, which will own the file, you can place it in some class
MyFileBlocker
, for example.
The question of organizing a place for a file blocker will be left to your discretion.
std::unique_ptr<std::wofstream> m_openedFile;
And then write two functions to lock and unlock the file.
void MyFileBlocker::unlockFile() { m_openedFile.reset(nullptr); } void MyFileBlocker::relockFile(std::wstring fileName) { m_openedFile.reset(new std::wofstream()); // _SH_DENYWR is WinAPI dependent deny write mode m_openedFile->open(fileName, std::ios_base::app, _SH_DENYWR); if (!m_openedFile->is_open()) { m_openedFile.reset(nullptr); } }
And then you can already check, and if the pointer exists, then you can perform the actions of writing to the file.
if (m_openedFile) { // You can write to file }
Also, I suppose that this problem can be solved by unifying the running process of the program so that each instance is identified as unique with respect to file permissions.
But I will be honest, at the time of solving this problem it did not occur to me, so I solved this problem using
std::wofstream
.