- 1. Описание проблемы
- 2. Решение
Удивительно, но в процессе своей профессиональной деятельности я наткнулся на одну интересную особенность QFile. С его помощью можно проверить, возможно ли чтение или запись информации в файл. Но в то же время QFile игнорирует права на чтение и запись файла, если файл открывается несколькими экземплярами одной и той же программы. Это означает, что если файл будет открыт в другом экземпляре программы, то QFile определит этот файл как доступный для чтения или записи, а также сможет успешно записать в файл.
Описание проблемы
Проверку на запрет записи можно выполнить следующим образом, но это не всегда работает, как я уже говорил выше. Но вы можете проверить это так
bool isFileWritable(const QString& fileName) { QFile file(fileName); bool isWritable = file.open(QFile::ReadWrite); file.close(); return isWritable; }
Однако вы можете проверить утверждение, сказанное в самом начале статьи, используя следующий код.
#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(); }
После сборки программы запустите несколько экземпляров вместе, и вы увидите, что содержимое файла будет соответствовать времени последнего запущенного экземпляра. То есть QFile полностью игнорирует права доступа и владения файлом другими процессами. Однако, если вы запустите эту программу, а затем попытаетесь перезаписать содержимое файла другой программой, например стандартным Блокнотом, вы получите следующее уведомление.
Решение
Я нашел выход из этой ситуации в использовании платформенно-зависимого функционала в WinAPI, возможно аналоги для других ОС.
Для этого нужно создать уникальный указатель на поток ввода/вывода
std::wofstream
, которому будет принадлежать файл, можно поместить его в какой-нибудь класс
MyFileBlocker
, например.
Вопрос организации места для файлового блокировщика останется на ваше усмотрение.
std::unique_ptr<std::wofstream> m_openedFile;
А затем напишите две функции для блокировки и разблокировки файла.
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); } }
А дальше уже можно проверить, и если указатель существует, то можно выполнить действия по записи в файл.
if (m_openedFile) { // You can write to file }
Также я предполагаю, что эту проблему можно решить путем унификации запущенного процесса программы, чтобы каждый экземпляр идентифицировался как уникальный в отношении прав доступа к файлам.
Но скажу честно, на момент решения этой проблемы мне это не приходило в голову, поэтому я решил эту проблему с помощью
std::wofstream
.