Работа с файловой системой в Symbian C++


Работа с файловой системой в Symbian C++

 

В Symbian OS операции с файловой системой выполняет так называемый
файловый сервер (File server,
исполнимый файл: efile.exe, UID: 0x100039e3).
Вся работа с файловой системой производиться через этот сервер - ни одна
операция не выполняется напрямую. Это позволяет реализовать в системе механизм
защиты данных и оптимизировать производительность операций. С другой стороны,
такой подход требует передачи команд и данных между сервером и клиентом, что
при неправильном проектировании может существенно снизить скорость выполнения
операций.

 

В статье не рассматривается ряд операций: к примеру,
отслеживание изменений в файловой системе и команда Subst(), так как они защищены capability, предоставляемыми
только по запросу производителями устройств (AllFiles и DiskAdmin).

 

Необходимо помнить, что доступ к файлам и папкам,
находящимся в директории \sys
и \resource требует
значительных capability.
Также как и содержимое \private
в большинстве случаев. Эти нюансы в статье не освещаются, так как
предполагается, что читатель знаком с платформой безопасности Symbian 9.x.

Сессия файлового сервера

 

Итак, все, что в Symbian OS делается с
файловой системой, делает файловый сервер. Для работы с ним используется R-класс RFs (заголовочный файл: f32file.h, библиотека: efsrv.lib). Перед началом выполнения операции экземпляр класса RFs должен
подключиться к серверу. Для этого используется метод Tint RFs::Connect(). После завершения работы с
файловым сервером сессию необходимо закрыть, вызвав RFs::Close(). Устанавливать и завершать сессию слишком часто не
следует. Обычно, R-класс
подключается в начале работы приложения (в методе ConstructL() C-класса) и закрывают при закрытии
приложения (в деструкторе), поэтому его часто объявляют в качестве члена С-класса.
Хорошей практикой считается передача экземпляров RFs по ссылке в
различные классы приложения, вместо того, чтобы устанавливать новые сессии.
Невозможность установить сессию с файловым сервером событие из ряда вон
выходящее, и обычно приводит к серьезным проблемам в работе приложения.
Поэтому, метод Connect()
часто можно увидеть вместе с проверкой, вызывающей сброс (leave) если он не завершился успешно.

 

// часть заголовочного файла

#include

class CTest : public CBase

      {

public:

      ~CTest();

// Мы опустили конструктор и

      // методы двухфазного конструктора

private:   

      void ConstructL();

RFs iFs;

      };

 

 

// часть реализации

void CTest::ConstructL()

      {

            User::LeaveIfError(iFs.Connect());

      }

 

CTest::~CTest()

      {

      iFs.Close();

      }

 

 Кроме того, в GUI приложениях
доступен фреймворк CONE
(заголовочный файл: coemain.h, библиотека: cone.lib), который
уже имеет установленную файловую сессию. Закрывать ее не нужно, а получить
можно следующим способом:

 

CCoeEnv::Static()->FsSession();

 

В классе удобно хранить ссылку на эту сессию:

 

// часть заголовочного файла

#include

#include

class CTest : public CBase

      {

public:

// Конструктор

      CTest();

private:   

RFs& iFs;

      };

 

 

// часть реализации

CTest::CTest():iFs(CCoeEnv::Static()->FsSession())

      {          

      }

 

В некоторых классах GUI приложения можно обратиться к iCoeEnv вместо CCoeEnv::Static().

 

Операции с файловой системой

 

Класс RFs позволяет
отправлять файловому серверу команды для выполнения следующих операций:

 

 

 

Создание папок. Удаление, перемещение и переименование файлов и папок

 

RFs предоставляет
два метода для создания папок:
MkDir(const
TDesC &aPath) и MkDirAll(const TDesC &aPath). Первый создает одну папку
в уже существующей. Второй способен создать сразу несколько папок в том случае,
если они не существовали. В обоих случаях аргументы
aPath должны содержать путь, оканчивающийся слешем (иначе
последний элемент будет воспринят как имя файла и проигнорирован).

 

            _LIT(KMyFolder, "c://myfolder//");

            iFs.MkDir(KMyFolder);

            _LIT(KMyFolder2,
"c://myfolder2//myfolder3//");

            iFs.MkDirAll(KMyFolder2);

 

 

Удаление файла выполняется с помощью метода Delete(const TDesC &aName), где aName – имя файла. Файл должен быть закрыт, а также не иметь атрибута “системный”
и “только для чтения” (атрибуты нужно снимать перед удалением).

            _LIT(KMyFile, "c://myfile");

            iFs.Delete(KMyFile);

Аналогичная операция
для папок вызывается методом RmDir (const TDesC &aPath). Папка должна быть
пустой (без подпапок), а аргумент
aPath должен оканчиваться слешем.

            iFs.RmDir(KMyFolder);

Ни Delete, ни RmDir не поддерживают маски в аргументах.

Наконец, переименование
файлов и папок. Эти операции одним методом Rename(const TDesC &anOldName,
const TDesC &aNewName); Где anOldName – имя файла или папки, а aNewName –
новое имя. На аргументы накладываются следующие ограничения – папка или файл
anOldName должны существовать, а aNewName – нет. Метод Rename ничего не
перезаписывает. Rename перемещает объекты вместе с атрибутами. Метод не может
применяться к нескольким объектам – маски в имени не поддерживаются. Если
Rename применяется к файлу – он должен быть закрыт, если к директории – то в
отличие от предыдущих методов, значение аргумента не должно содержать слеш в
конце. И, наконец, anOldName и aNewName должны быть на одном диске. Поэтому
операция переименования не может заменить перемещения.

 

            _LIT(KMyFolderOld, "c://myfolder");

            _LIT(KMyFolderNew,
"c://my_folder");

            iFs.Rename(KMyFolderOld,
KMyFolderNew);
//для папки

            _LIT(KMyFileOld,
"c://myfile");

            _LIT(KMyFileNew,
"c://my_file");

            iFs.Rename(KMyFileOld,
KMyFileNew);
//для файла

 

Все вышеприведенные
методы возвращают
KErrNone в
случае успеха, и один из общих кодов ошибки в случае неудачи.

 

Как вы видите, класс RFs позволяет осуществить многое, но если вы собираетесь
работать с большим числом объектов файловой системы, то неизбежно столкнетесь с
рядом проблем. Чего стоит одна только необходимость рекурсивной очистки папки
перед удалением... Неужели проектировщики
Symbian OS настолько ненавидели разработчиков приложений, что
заставят нас реализовывать это самостоятельно? Нет. Это невозможно по двум
причинам –
Symbian OS очень экономна в отношении памяти, и очень ревностно
относится к скорости исполнения приложений. Поэтому, реализовывать ненужный код
нас не заставят, да и не доверят.

 

SDK предлагает нам
еще один класс:
CFileMan (заголовочный
файл: f32file.h, библиотека: efsrv.lib) дополняющий функциональность
RFs. CFileMan позволяет копировать, удалять, перемещать и
переименовывать файлы и папки синхронно и асинхронно. Более того, он работает с
множеством объектов (по маске) и предлагает
M-класс обозреватель для отслеживания состояния этих
операций.

Как я уже отметил, с
файлами работает файловый сервер, поэтому и CFileMan требует передачи ссылки на
экземпляр
RFs сессии
при создании. Рассмотрим создание объекта CFileMan, и передачу ему файловой
сессии и обозревателя:

 

// объявление класса

class CTest : public CBase,

//наш класс
будет также обозревателем

public MFileManObserver

      {

public:

      ~CTest();

      static CTest* NewL();

      static CTest* NewLC();

private:

      CTest();

      void ConstructL();

private: //методы из