- 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 повністю ігнорує права доступу і володіння файлом іншими процесами. Однак якщо запустити цю програму, а потім спробувати переписати вміст файлу іншою програмою, наприклад стандартним Notepad, то ви отримаєте наступне повідомлення.
Рішення
Я знайшов рішення даної ситуації у використанні платформозавісімого функціоналу в 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
.