Файловые системы Работа с файлами в Windows api. CreateFile winapi функции Чтение информации из файла

WinAPI. Функции работы с файлами. Часть 4 :

Заканчиваем изучать WinAPI работы с фалами и дисками. Это последняя статья, потому что в следующий раз мы перейдём к рассмотрению другой группы WinAPI функций. Я постарался дать тебе максимум инфе о основных API для работы с файлами. Надеюсь, что я работал не зря.

Сегодня ты узнаешь про:

ReadFile

Функция читает из файла блок данных начиная с текущей позиции. После прочтения блока, позиция переносится в конец прочитанного блока.

Объявление:

Для С/С++ BOOL ReadFile(HANDLE hFile, // Указатель на открытый файл LPVOID lpBuffer, // Указатель на буфер, куда поместится прочитанный блок DWORD nNumberOfBytesToRead, // количество прочитанных байтов LPDWORD lpNumberOfBytesRead,// указатель на число прочитанных данных LPOVERLAPPED lpOverlapped // указатель на структуру OVERLAPPED); Для Delphi function ReadFile(hFile: THandle; // Указатель на открытый файл var Buffer; // Указатель на буфер, куда поместится прочитанный блок nNumberOfBytesToRead: DWORD; // количество прочитанных байтов var lpNumberOfBytesRead: DWORD; // указатель на число прочитанных данных lpOverlapped: POverlapped // указатель на структуру OVERLAPPED): BOOL; stdcall;

RemoveDirectory

Функция удаляет директорию. Если внутри есть хотя бы один файл или другая директория, то произойдёт ошибка. Удаление возможно только пустой директории.

Существует в: Win NT, Win9x, Win32s.

Для С/С++ объявлена в winbase.h. Для Delphi в модуле windows.

Объявление:

Для С/С++ BOOL RemoveDirectory(LPCTSTR lpPathName // Указатель на путь директории); Для Delphi function RemoveDirectory(lpPathName: PChar// Указатель на путь директории): BOOL; stdcall;

Если всё ничтяк, то функция вернёт TRUE.

Пример использования: RemoveDirectory("с:\Windows");

SetCurrentDirectory

Функция изменяет текущую директорию на указанную. Это значит, что когда ты будешь использовать другие функции работы с файлами, и не указывать путь, то ОС будет искать файл в этой директории.

Существует в: Win NT, Win9x, Win32s.

Для С/С++ объявлена в winbase.h. Для Delphi в модуле windows.

Объявление:

Для С/С++ BOOL SetCurrentDirectory(LPCTSTR lpPathName // Указатель на строку, содержащую путь); Для Delphi function SetCurrentDirectory(lpPathName: PChar // Указатель на строку, содержащую путь): BOOL; stdcall;

Если всё ничтяк, то функция вернёт TRUE.

SetEndOfFile

Функция делает текущую позицию файла концом файла. Если тебе нужно сократить размер файла, то вызови эту функцию, установи курсор в необходимую позицию и вызови эту функцию.

Существует в: Win NT, Win9x, Win32s.

Для С/С++ объявлена в winbase.h. Для Delphi в модуле windows.

Объявление:

Для С/С++ BOOL SetEndOfFile(HANDLE hFile // Указатель на открытый файл); Для Delphi function SetEndOfFile(hFile: THandle // Указатель на открытый файл): BOOL; stdcall;

Если всё ничтяк, то функция вернёт TRUE.

SetFileAttributes

Функция позволяет устанавливать на файл атрибуты.

Существует в: Win NT, Win9x, Win32s.

Для С/С++ объявлена в winbase.h. Для Delphi в модуле windows.

Объявление:

Для С/С++ BOOL SetFileAttributes(LPCTSTR lpFileName, // Путь к файлу DWORD dwFileAttributes // Будущие атрибуты файла); Для Delphi function SetFileAttributes(lpFileName: PChar; // Путь к файлу dwFileAttributes: DWORD // Будущие атрибуты файла): BOOL; stdcall;

С первым параметром всё ясно. Это просто путь к файлу. А вот второй параметр это атрибуты. Они могут быть в виде сочетания следующих флагов:

  • FILE_ATTRIBUTE_ARCHIVE - атрибут архивного файла.
  • FILE_ATTRIBUTE_HIDDEN - атрибут спрятанного файла.
  • FILE_ATTRIBUTE_NORMAL - атрибут нормального файла.
  • FILE_ATTRIBUTE_OFFLINE - указывает на то, что данные файла не доступны и находятся на отключённом устройстве
  • FILE_ATTRIBUTE_READONLY - атрибут файла только для чтения.
  • FILE_ATTRIBUTE_SYSTEM - атрибут системного файла.
  • FILE_ATTRIBUTE_TEMPORARY - атрибут временного файла.

Если всё ничтяк, то функция вернёт TRUE.

SetFilePointer

Функция устанавливает позицию в файле. Например, если ты хочешь прочитать данные из файла начиная с какой-то позиции, то ты должен воспользоваться этой функцией, чтобы выставить позицию, а потом прочитать данные начиная с этой позиции.

Существует в: Win NT, Win9x, Win32s.

Для С/С++ объявлена в winbase.h. Для Delphi в модуле windows.

Объявление:

Для С/С++ DWORD SetFilePointer(HANDLE hFile, // Указатель на открытый файл LONG lDistanceToMove, // количество байт, на которые надо передвинуться PLONG lpDistanceToMoveHigh, // Второй байт, указывающий количество байт DWORD dwMoveMethod //Откуда нужно начинать двигаться); Для Delphi function SetFilePointer(hFile: THandle; // Указатель на открытый файл lDistanceToMove: Longint; // количество байт, на которые надо передвинуться lpDistanceToMoveHigh: Pointer; // Второй байт, указывающий количество байт dwMoveMethod: DWORD //Откуда нужно начинать двигаться): DWORD; stdcall;

Первый параметр - указатель на открытый файл. Второй и третий параметры определяют количество байт, на которые надо передвинуться. Последний параметр - это флаг определяющий откуда надо начинать двигаться:

  • FILE_BEGIN - Передвинуться от начала файла вперёд.
  • FILE_CURRENT - Передвинуться начиная от текущей позиции вперёд.
  • FILE_END - Передвинуться от конца файла к началу.

Если всё ничтяк, то функция вернёт младший байт установленной позиции.

Если ты хочешь узнать текущую позицию файла, то можешь смухлевать, вызвав функцию с такими параметрами: SetFilePointer(h,0,0, FILE_CURRENT). Этим ты заставляешь передвинутся на 0 байтов от текущей позиции. Передвижения не произойдёт, но функция вернёт новую позицию файла, то есть текущую.

SetVolumeLabel

Функция устанавливает метку тома

Существует в: Win NT, Win9x.

Для С/С++ объявлена в winbase.h. Для Delphi в модуле windows.

Объявление:

Для С/С++ BOOL SetVolumeLabel(LPCTSTR lpRootPathName, // Указатель на имя тома LPCTSTR lpVolumeName // Метка тома); Для Delphi function SetVolumeLabel(lpRootPathName: PChar; // Указатель на имя тома lpVolumeName: PAnsiChar// Метка тома): BOOL; stdcall;

Если всё ничтяк, то функция вернёт TRUE.

WriteFile

Функция производит запись блока данных начиная с текущей позиции в файле.

Существует в: Win NT, Win9x, Win32s.

Для С/С++ объявлена в winbase.h. Для Delphi в модуле windows.

Объявление:

Для С/С++ BOOL WriteFile(HANDLE hFile, // Указатель на открытый файл LPCVOID lpBuffer, // Указатель на данные для записи DWORD nNumberOfBytesToWrite, // Количество записанных байт LPDWORD lpNumberOfBytesWritten,// Указатель на количество записанных байт LPOVERLAPPED lpOverlapped // Указатель на структуру OVERLAPPED); Для Delphi function WriteFile(hFile: THandle; // Указатель на открытый файл const Buffer; // Указатель на данные для записи nNumberOfBytesToWrite: DWORD; // Количество записанных байт var lpNumberOfBytesWritten: DWORD; // Указатель на количество записанных байт lpOverlapped: POverlapped // Указатель на структуру OVERLAPPED): BOOL; stdcall;

Если всё ничтяк, то функция вернёт TRUE.

На этом я заканчиваю рассмотрения функций для работы с файлами.

Для создания файла используется функция WinApi CreateFile().

System::AnsiString vasFileName; HANDLE hFile; hFile = CreateFile(vasFileName.c_str(), // имя файла, преобразуемое к типу char* GENERIC_READ|GENERIC_WRITE, // доступ для чтения и записи 0, // файл не может быть разделяемым NULL, // дескриптор файла не наследуется CREATE_NEW, // создать новый если не существует FILE_ATTRIBUTE_READONLY, // файл имеет атрибут "только для чтения" NULL // всегда NULL для Windows); if(hFile != INVALID_HANDLE_VALUE) { //Файл создан CloseHandle(hFile); return; }else { //Файл не создан CloseHandle(hFile); //Здесь можно поместить сообщение об ошибке return; }

FILE_ATTRIBUTE определены следующим образом:

#define FILE_ATTRIBUTE_READONLY 0x00000001 #define FILE_ATTRIBUTE_HIDDEN 0x00000002 #define FILE_ATTRIBUTE_SYSTEM 0x00000004 #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define FILE_ATTRIBUTE_ARCHIVE 0x00000020 #define FILE_ATTRIBUTE_DEVICE 0x00000040 #define FILE_ATTRIBUTE_NORMAL 0x00000080 #define FILE_ATTRIBUTE_TEMPORARY 0x00000100 #define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 #define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 #define FILE_ATTRIBUTE_COMPRESSED 0x00000800 #define FILE_ATTRIBUTE_OFFLINE 0x00001000 #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 #define FILE_ATTRIBUTE_ENCRYPTED 0x00004000

С помощью этой функции можно открывать уже существующие файлы и консоли для консольных приложений, усекать их, открывать каталоги. Это все задается пятым параметром. Для создания файла этот параметр задается как CREATE_NEW. Вновь создаваемый файл открывается как для чтения, так и для записи, о чем свидетельствует второй параметр функции. Третий и четвертый параметры редко имеют какое-либо практическое применение в Windows 9x и требуются в основном при создании систем с разделением доступа на базе Windows NT. Шестой параметр определяет, какие атрибуты будут установлены для создаваемого файла. В данном случае будет присвоен атрибут Read-Only, позволяющий только чтение файла без записи в него. Windows самостоятельно закрывает все файлы и освобождает дескрипторы, как только завершается выполнение программы, но правило чистоты программирования требует не заставлять думать систему, когда сам знаешь как выполнить то или иное действие.

Открытие существующего файла

Функция WinApi OpenFile() открывает файл. Помимо этого, OpenFile() умеет создавать и удалять файлы. Во многом назначения этой функции пересекаются с CreateFile(), и существует в последних версиях WinApi для совместимости с ранними версиями Windows. Поэтому везде, где это возможно, лучше пользовать CreateFile().

System::AnsiString vasFileName="a.000"; HFILE hFile; OFSTRUCT tOfStr; tOfStr.cBytes = sizeof tOfStr; hFile = OpenFile(vasFileName.c_str(), //имя файла, преобразуемое к типу char* &tOfStr, //указатель на буфер с информацией о файле OF_READ); // файл открыт для чтения if(hFile != HFILE_ERROR) { //Файл открыт CloseHandle(HANDLE(hFile)); return; } else { CloseHandle(HANDLE(hFile)); //Здесь может быть помещено сообщение об ошибке return; }

Функция OpenFile() принимает всего три аргумента: имя открываемого файла, указатель на структуру OFSTRUCT и флаг режима открытия файла. Структура OFSTRUCT заполняется данными об открытом файле. Она предоставляет такую информацию, как свойства файла, его размер и т. д.

Typedef struct _OFSTRUCT { BYTE cBytes; BYTE fFixedDisk; WORD nErrCode; WORD Reserved1; WORD Reserved2; CHAR szPathName; }OFSTRUCT, *LPOFSTRUCT, *POFSTRUCT;

Третий параметр, флаг открытия файла, устанавливается в режим чтения, чтобы избежать случайного повреждения данных. Разумеется, можно установить и другие режимы:

  • Удаление файлов

    Для удаления файлов используется функция WinApi DeleteFile().

    System::AnsiString vasFileName; DeleteFile(vasFileName.c_str()); // имя удаляемого файла

    DeleteFile() не удаляет защищенные от записи файлы (READONLY).

    Копирование и перемещение файлов

    Для копирования используется функция WinApi CopyFile, для перемещения MoveFile().

    Определим имена файлов:

    System::AnsiString vasNameFileFrom; System::AnsiString vasNameFileTo;

    Функция CopyFile():

    If(CopyFile(vasNameFileFrom.c_str(), // имя копируемого файла vasNameFileTo.c_str(), // имя нового файла TRUE)) // если файл уже существует не копировать - true { //Файл скопирован } else { //Сообщение об ошибке копирования }

    Аналогично CopyFile() действует функция API для переноса файла MoveFile().

    If(MoveFile(vasNameFileFrom.c_str(), // имя переносимого файла vasNameFileTo.c_str()) // имя нового файла { //Файл перемещен } else { //Сообщение об ошибке переноса файла }

    Единственное отличие MoveFile() от CopyFile() - отсутствие третьего параметра, отвечающего за блокировку процесса переноса в случае, если файл уже существует.

    Чтение информации из файла

    Для чтения информации из файла используется функцию Win API ReadFile(), и ряд функций, обеспечивающих ее информацией:

    HFILE hFile; //Имя читаемого файла System::AnsiString vasFileName="a.000"; DWORD vwCounter; int *virgRDWRBuff; BY_HANDLE_FILE_INFORMATION bhFileInformation; OFSTRUCT tOfStr; tOfStr.cBytes = sizeof tOfStr; //Открываем файл и заполняем информацией tOfStr hFile = OpenFile(vasFileName.c_str(), // имя файла, преобразуемое к типу char* &tOfStr, //указатель на буфер с информацией о файле OF_READWRITE); // файл открыт для чтения и записи if(hFile == HFILE_ERROR) { //Здесь может быть сообщение об ощибке CloseHandle(HANDLE(hFile)); return; } //Теперь можно получить информацию о файле GetFileInformationByHandle (HANDLE(hFile), // дескриптор файла &bhFileInformation //адрес структуры, в которой сохраняется информация); //Резервируем память для всего файла обычно размер файла //не больше размера nFileSizeLow объявленного как DWORD int viSize=bhFileInformation.nFileSizeLow/sizeof(int); virgRDWRBuff = (int*) new int; //Сдвигаем указатель на нужный байт - сдесь на начало if(_llseek(hFile,0*sizeof(int),0) != (long)(0*sizeof(int))) { CloseHandle(HANDLE(hFile)); //Здесь может быть сообщение об ошибке return; } //Считать данные из файла if(!ReadFile(HANDLE(hFile),virgRDWRBuff, (DWORD)viSize*sizeof(int),&vwCounter,NULL)) { //Здесь может быть сообщение об ошибке } else { if(vwCounter == (DWORD)viSize*sizeof(int)) { //Используем информацию int viRez=virgRDWRBuff; }else { //Здесь может быть сообщение об ошибке } } CloseHandle(HANDLE(hFile)); delete virgRDWRBuff;

    В тексте объявляются две структуры. Первая, BY_HANDLE_FILE_INFORMATION, нужна для хранения полезной информации о файле. Вторая, OFSTRUCT требуется для работы функции API OpenFile(). Далее идет инициализация поля размера этой структуры.

    Далее - создание буфера, в который будут скопированы данные, считанные из файла. Можно проделать расчет, основанный на размере файла, но можно поступить проще: задать размер принудительно, например 64 Кбайт. Для создания такого буфера можно использовать задание типа new char, выделяющей блок памяти подходящего размера. В конце обработчика необходимо освободить блок памяти операцией delete.

    Само чтение происходит в несколько этапапов: открытие файла, получение его размера, сдвиг на нужную позицию и собственно чтение.

    Полученный дескриптор функцией OpenFile() открытого файла необходимо передать в качестве параметра для другой функции API - GetFileInformationByHandle(), которая заполняет структуру bhFileInformation типа BY_HANDLE_FILE_INFORMATION, переданную в качестве второго параметра, данными об открытом файле. В этой структуре имеются два поля, хранящие старшие и младшие четыре байта размера файла. Для малого файла хватает и младших четырех байтов.

    Первые два параметра - это дескриптор читаемого файла и адрес буфера, в который будут считаны данные. Четвертый параметр функции - счетчик байтов, в который ReadFile() записывает количество байтов, считанных из файла.

    Пятый параметр игнорируется.

    Обратим внимание на то, что для правильной работы дескриптор файла надо преобразовывать к типу HANDLE.

    При работе в переменной появятся данные из файла.

    Запись информации в файл

    Функция WinApi WriteFile() записывает данные в файл. Она полностью аналогична по используемым параметрам функции ReadFile().

    HFILE hFile; System::AnsiString vasFileName="a.000"; DWORD vwCounter; int *virgRDWRBuff; BY_HANDLE_FILE_INFORMATION bhFileInformation; OFSTRUCT tOfStr; tOfStr.cBytes = sizeof tOfStr; //Открываем файл и заполняем информацией tOfStr hFile = OpenFile(vasFileName.c_str(), // имя файла, преобразуемое к типу char* &tOfStr, //указатель на буфер с информацией о файле OF_READWRITE); // файл открыт для чтения и записи if(hFile == HFILE_ERROR) { //Здесь может быть сообщение об ощибке CloseHandle(HANDLE(hFile)); return; } //Теперь можно получить информацию о файле, но понадобиться она //может только для того, чтобы не записать за пределы файла, хотя и //это не страшно, файл просто увеличится в размере GetFileInformationByHandle (HANDLE(hFile), // дескриптор файла &bhFileInformation //адрес структуры, в которой сохраняется информация); //Резервировать память для всего файла обычно при записи нет необходимости //так как известно сколько будем писать, например 100 чисел int viSize=100; virgRDWRBuff = (int*) new int; //Сдвигаем указатель на нужный байт - сдесь на начало if(_llseek(hFile,0*sizeof(int),0) != (long)(0*sizeof(int))) { CloseHandle(HANDLE(hFile)); //Здесь может быть сообщение об ошибке return; } //Пишем данные в буфер for(int i=0; i

    Пример универсальной функции работы с файлами

    Следующая функция читает информацию типа int или char из файла, имя которого и директория передается в функцию, или записывает информацию в файл в соответствии с передаваемыми в функцию параметрами. Функция не предполагает чтение всего файла, и написана для случаев когда известно сколько байт предполагается прчитать или записать.

    ////////////////////////////////////////////////////////////////////// // Функция чтения записи байт из (в) virgRDWRBuff // или из (в) vchrgRDWRBuf // int virgRDWRBuff[n]; и char vchrgRDWRBuf[n]; должны быть объявлены // вне функции // Функция принимает: // имя директории vasFileDir // имя файла vasFileName // режим чтения записи viRegime 0 - чтение 1 - запись // смещение в элементах int viSeek // число байт для записи/чтения viNumber // что пишем viType 0 - целое 1 - char ///////////////////////////////////////////////////////////////////// int __fastcall TForm1::iRDWR(AnsiString &vasFileDir,AnsiString vasFileName, int viRegime,int viSeek,int viNumber,int viType) { HFILE hFile; AnsiString vasFileName=vasFileDir+"\\"+vasFileName; DWORD dwCounter; OFSTRUCT tOfStr; tOfStr.cBytes = sizeof tOfStr; // Открыть файл hFile = OpenFile(vasFileName.c_str(),&tOfStr,OF_READWRITE); if(hFile == HFILE_ERROR) { CloseHandle(HANDLE(hFile)); //Сообщение об ошибке return 1; } BY_HANDLE_FILE_INFORMATION bhFileInformation; GetFileInformationByHandle (HANDLE(hFile), // Дескриптор файла // Адрес структуры, в которой сохраняется информация &bhFileInformation); viRazmer=bhFileInformation.nFileSizeLow; //сдвинуться switch(viType) { case 0: if(_llseek(hFile,viSeek*sizeof(int),0) != (long)(viSeek*sizeof(int))) { CloseHandle(HANDLE(hFile)); //Сообщение об ошибке return 1; } break; case 1: if(_llseek(hFile,viSeek*sizeof(char),0) != (long)(viSeek*sizeof(char))) { CloseHandle(HANDLE(hFile)); //Сообщение об ошибке return 1; } break; } // считать (записать)данные из (в) файл switch(viRegime) { case 1: if(viType == 0) { if(!WriteFile(HANDLE(hFile),virgRDWRBuff, (DWORD)(viNumber*sizeof(int)),&dwCounter,0)) { //Сообщение об ошибке CloseHandle(HANDLE(hFile)); return 1; } }else { if(!WriteFile(HANDLE(hFile),(char far*) vchrgRDWRBuf, (DWORD)(viNumber*sizeof(char)),&dwCounter,0)) { //Сообщение об ошибке CloseHandle(HANDLE(hFile)); return 1; } }//if(viType == 0) break; case 0: if(viType == 0) { if(!ReadFile(HANDLE(hFile),virgRDWRBuff, (DWORD)(viNumber*sizeof(int)),&dwCounter,0)) { //Сообщение об ошибке CloseHandle(HANDLE(hFile)); return 1; } }else { if(!ReadFile(HANDLE(hFile),(char far*) vchrgRDWRBuf, (DWORD)(viNumber*sizeof(char)),&dwCounter,0)) { //Сообщение об ошибке CloseHandle(HANDLE(hFile)); return 1; } }//if(viType == 0) break; }//switch(viRegime) CloseHandle(HANDLE(hFile)); return 0; }

FAT_2006 Файловые системы

Работа с файлами в Windows API


  • Principle Win32 API functions for file I/O

  • Second column gives nearest UNIX equivalent
File System API Calls in Windows 2000


  • Principle Win32 API functions for directory management

  • Second column gives nearest UNIX equivalent, when one exists
Работа с томами

  • Для выяснения того, какие логические диски существуют в системе, используется функция
DWORD GetLogicalDrives(void)

Каждый установленный бит возвращаемого значения соответствует существующему в системе логическому устройству. Например, если в системе существуют диски A:, C: и D:, то возвращаемое функцией значение равно 13(10).


  • Функция
DWORD GetLogicalDrivesStrings(DWORD cchBuffer, LPTSTR lpszBuffer)

заполняет lpszBuffer информацией о корневом каталоге каждого логического диска в системе. В приведенном выше примере буфер будет заполнен символами

параметр cchBuffer определяет длину буфера. Функция возвращает реальную длину буфера, необходимую для размещения всей информации.


  • Для определения типа диска предназначена функция
UINT GetDriveType(LPTSTR lpszRootPathName)

В качестве параметра ей передается символическое имя корневого каталога (напр. A:\ ), а возвращаемое значение может быть одно из следующих:



Идентификатор

Описание

0

Тип устройства определить нельзя

1

Корневой каталог не существует

DRIVE_REMOVABLE

Гибкий диск

DRIVE_FIXED

Жесткий диск

DRIVE_REMOTE

Сетевой диск

DRIVE_CDROM

Компакт диск

DRIVE_RAMDISK

RAM диск

  • Для получения подробной информации о носителе используется функция GetVolumeInformation . Она заполняет параметры информацией об имени тома, названии файловой структуры, максимальной длине имени файла, дополнительных атрибутах тома, специфических для файловой структуры.

  • Функция GetDiskFreeSpace сообщает информацию о размерах сектора и кластера и о наличии свободных кластеров.
Работа с каталогами и файлами

Функция

Выполняемое действие

GetCurrentDirectory

Получение текущего каталога

SetCurrentDirectory

Смена текущего каталога

GetSystemDirectory

Получение системного каталога

GetWindowsDirectory

Получение основного каталога системы

CreateDirectory

Создание каталога

RemoveDirectory

Удаление каталога

CopyFile

Копирование файла

MoveFile

Перемещение или переименование файла

DeleteFile

Удаление файла

Синхронная работа с файлами

HANDLE CreateFile (

LPCTSTR lpFileName , // pointer to name of the file

DWORD dwDesiredAccess , // access (read-write) mode

DWORD dwShareMode , // share mode

LPSECURITY_ATTRIBUTES lpSecurityAttributes , // pointer to security // descriptor

DWORD dwCreationDistribution ,// how to create

DWORD dwFlagsAndAttributes , // file attributes

HANDLE hTemplateFile // handle to file with attributes to copy


В случае удачи функция CreateFile возвращает описатель открытого файла как объекта ядра. Существенно, что в противном случае она возвращает не NULL, а INVALID_HANDLE_VALUE.

При синхронной работе с файлами прикладная программа, запустив операцию ввода вывода, переходит в состояние блокировки до ее окончания (т.е. ожидает завершения операции ввода вывода).

В Win32 перед принятием решения об использовании операций синхронной работы с файлами настоятельно рекомендуется рассмотреть вопрос о возможности использования файлов, отображаемых в память.


  • Параметр lpFileName определяет имя файла.

  • Параметр dwDesiredAccess задает тип доступа к файлу. Можно определить флаги GENERIC_READ и GENERIC_WRITE а так же их комбинацию для разрешения чтения или записи в файл.

  • Параметр dwShareMode определяет режим совместного использования файла различными процессами. Если этот параметр равен нулю, то никакой другой поток не сможет открыть этот же файл. Флаги FILE_SHARE_READ и FILE_SHARE_WRITE а так же их комбинация разрешают другим потокам осуществлять доступ к файлу для чтения или записи.

  • Параметр lpSecurityAttributes указывает на структуру, описывающую защиту создаваемого объекта ядра. Ему может быть присвоено значение NULL.

  • Параметр dwCreationDistribution

  • Параметр dwCreationDistribution определяет действия функции в зависимости от того, существует ли уже файл с указанным именем.
CREATE_NEW - Создает файл, если файл существует, то ошибка.

CREATE_ALWAYS - Создает файл, если файл существует, то старый файл удаляется и новый создается.

OPEN_EXISTING - Открывает существующий файл.

OPEN_ALWAYS - Создает файл, если файл не существует, то создается новый файл.

TRUNCATE_EXISTING - Открывает файл и урезает его до нулевой длины


  • Параметр dwFlagsAndAttributes определяет атрибуты файла, если он создается и задает режим работы с файлом.
FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_TEMPORARY

Атрибуты файла могут комбинироваться за исключением FILE_ATTRIBUTE_NORMAL, который всегда используется в одиночестве.

Вместе с атрибутами могут комбинироваться и флаги, задающие режим работы с файлом.

FILE_FLAG_NO_BUFFERING - Не осуществлять кэширование и опережающее чтение

FILE_FLAG_RANDOM_ACCESS - Кэшировать как файл произвольного доступа

FILE_FLAG_SEQUENTIAL_SCAN - Кэшировать как файл последовательного доступа

FILE_FLAG_WRITE_TROUGH - Не буферизовать операцию записи. Производить запись на диск немедленно.

FILE_FLAG_DELETE_ON_CLOSE - Уничтожить файл при закрытии. Полезно комбинировать с атрибутом FILE_ATTRIBUTE_TEMPORARY.

FILE_FLAG_OVERLAPPED - Работа с файлом будет осуществляться асинхронно.


  • Параметр hTemplateFile либо равен NULL, либо задает описатель открытого файла. Во втором случае параметр dwFlagsAndAttributes игнорируется, а указанный файл используется в качестве шаблона, т.е. используются его флаги и атрибуты.
File System API Calls in Windows 2000

A program fragment for copying a file using the Windows 2000 API functions

Асинхронная работа с файлами

BOOL ReadFile (

HANDLE hFile, // handle of file to read

LPVOID lpBuffer, // address of buffer that receives data

DWORD nNumberOfBytesToRead,// number of bytes to read

LPDWORD lpNumberOfBytesRead,// address of number of bytes read

LPOVERLAPPED lpOverlapped // address of structure needed for // overlapped I/O

BOOL WriteFile (

HANDLE hFile, // handle to file to write to

LPCVOID lpBuffer, // pointer to data to write to file

DWORD nNumberOfBytesToWrite, // number of bytes to write

LPDWORD lpNumberOfBytesRead,// pointer to number of bytes written

LPOVERLAPPED lpOverlapped // address of structure needed for //overlapped I/O

При асинхронной работе с файлами прикладная программа, запустив операцию ввода вывода, не ожидает ее завершения а продолжает исполняться.

Windows’95 не позволяет организовать асинхронную работу с файлами, но тот же самый асинхронный механизм может использоваться при работе с последовательными портами, транспортерами и почтовыми ящиками.

Для организации асинхронной работы с файлами необходимо при вызове функции CreateFile установить флаг FILE_FLAG_OVERLAPPED в параметре dwFlagsAndAttributes. После этого функции ReadFile и WriteFile будут работать асинхронно, т.е. только запускать операции ввода вывода и не ожидать их завершения.

Параметры функции ReadFile имеют следующее предназначение:


  • hFile – описатель объекта ядра “файл”, полученный в результате вызова функции CreateFile

  • LpBuffer – адрес буфера, в который будет производиться чтение

  • nNumberOfBytesToRead – количество байт, которые необходимо прочитать

  • lpNumberOfBytesRead – адрес переменной, в которой будет размещено количество реально прочитанных байт. Существенно, что сразу после выполнения функции ReadFile, этот параметр не может быть установлен, так как операция чтения только началась.

  • lpOverlapped – указатель на структуру OVERLAPPED, управляющую асинхронным вводом выводом.
Параметры функции WriteFile аналогичны параметрам функции ReadFile.

typedef struct _OVERLAPPED {

DWORD Internal ; //Используется операционной системой. //Хранит статус завершения операции.

DWORD InternalHigh ; //Используется ОС. Хранит //количество переданных байт.

DWORD Offset ; //Позиция в файле, начиная с которой //необходимо производить операцию

//чтения (записи).

DWORD OffsetHigh ;//Количество байт для передачи.

HANDLE hEvent ; //Описатель события, которое произойдет //при завершении операции чтения //(записи).

В отличие от синхронных операций, при организации асинхронного чтения (записи) необходимо явно указать позицию, начиная с которой производится операция. Это связано с тем, что текущей позиции не существует, так как несколько операций чтения и записи могут производиться одновременно с разных позиций в одном файле.

Схема организации асинхронных чтения и записи


  • Перед запуском операции создается объект ядра “событие” и его описатель передается в функцию ReadFile (WriteFile) в качестве элемента hEvent параметра lpOverlapped. Программа, выполнив необходимые действия одновременно с операцией передачи данных, вызывает одну из функций ожидания (напр. WaitForSingleObject), передавая ей в качестве параметра описатель события. Выполнение программы при этом приостанавливается до завершения операции ввода-вывода.

  • Событие не создается. В качестве ожидаемого объекта выступает сам файл. Его описатель передается в функцию WaitForSingleObject. Этот метод прост и корректен, но не позволяет производить параллельно несколько операций ввода-вывода с одним и тем же файлом.

  • “Тревожный” асинхронный ввод-вывод. Схема построена на использовании функций ReadFileEx и WriteFileEx. В качестве дополнительного параметра в эти функции передается адрес функции завершения, которая будет вызываться всякий раз при завершении операции ввода-вывода. Существенно, что эти функции выполняются в том же самом потоке что и функции файлового ввода/вывода. Это значит, что поток, запустивший операции чтения записи должен обратиться к функции ожидания, чтобы разрешить системе вызвать функцию завершения.
Файловые системы фирмы Microsoft

Магнитный диск

МД представляет собой один или несколько объединенных дисков (блинов), по поверхностям (Sides) которых перемещаются головки (Heads).

Головки "бегают" по круговым дорожкам (Tracks), каждая дорожка разделена на сектора (Sectors). Дорожки, равноудаленные от центра диска и образующие цилиндрическую поверхность, называют цилиндрами (Cilinders).

Форматирование дисков. Для того чтобы на диске можно было хранить информацию, диск должен быть отформатирован, то есть должна быть создана физическая и логическая структура диска.

Форматирование физической структуры диска состоит в создании на диске концентрических дорожек, которые, в свою очередь, делятся на секторы. Для этого в процессе форматирования магнитная головка дисковода расставляет в определенных местах диска метки дорожек и секторов.

Логическая структура дисков . Логическая структура магнитного диска представляет собой совокупность секторов, каждый из которых имеет свой порядковый номер. Сектора нумеруются в линейной последовательности от первого сектора нулевой дорожки до последнего сектора последней дорожки.

Строение жесткого диска


Кластеры

В ОС фирмы Windows основной единицей хранения информации является кластер – группа смежных секторов. Число секторов в кластере всегда равно степени двойки.

Увеличение размера кластера приводит к нерациональному использованию пространства МД для мелких файлов.

Фрагментация и дефрагментация

Файл, который занимает на диске более одного непрерывного участка, называется фрагментированным.

Фрагментация диска - это появление на диске множества свободных участков, разделенных занятыми участками.

Дефрагментация диска - это перемещение данных на разделе, после которого, кластеры содержащие части одного файла, размещаются последовательно.

В процессе работы с диском при записи и удалении файлов разного размера на диске появятся свободные и занятые области разной длины.

Такой метод хранения файлов позволяет использовать всё имеющееся на диске свободное место, т.к. если длина записываемого файла больше, чем размеры непрерывных свободных участков, то файл просто расположится в нескольких несмежных участках.

Однако при использовании описанного выше метода файл становится фрагментированным - он как бы "размазан" по диску. К чему это может привести?

К тому, что для доступа к файлу необходимо перемещать магнитные головки от одного участка файла к другому. А на это требуется время, и весьма значительное.

Реально время чтения сильно фрагментированного файла по сравнению с файлом, занимающим непрерывную область на диске, может отличаться в несколько раз! Внешне это выглядит так, как будто все программы стали работать в несколько раз медленнее, при этом наблюдается интенсивное перемещение головок диска от одного участка файла к другому.


  • Файловые системы фирмы Microsoft выделяют для записываемых на диск файлов некоторое количество кластеров, в зависимости от размера файла.

  • В процессе работы с диском при записи и удалении файлов разного размера на диске появятся свободные и занятые области разной длины.

  • Такой метод хранения файлов позволяет использовать всё имеющееся на диске свободное место, т.к. если длина записываемого файла больше, чем размеры непрерывных свободных участков, то файл просто расположится в нескольких несмежных участках.

  • Реально время чтения сильно фрагментированного файла по сравнению с файлом, занимающим непрерывную область на диске, может отличаться в несколько раз! Внешне это выглядит так, как будто все программы стали работать в несколько раз медленнее, при этом наблюдается интенсивное перемещение головок диска от одного участка файла к другому.
Физические и логические диски

Основные причины разбиения физического диска на несколько логических:


  • ограничения файловых систем на максимальный размер физического диска;

  • повышение надежности файловой системы;

  • поддержка нескольких ОС.
Файловая система FAT для MS DOS

История FAT 16


  • Файловая система FAT (File Allocation Table) была разработана Биллом Гейтсом и Марком МакДональдом в 1977 году и первоначально использовалась в операционной системе 86-DOS. Чтобы добиться переносимости программ из операционной системы CP/M в 86-DOS, в ней были сохранены ранее принятые ограничения на имена файлов.

  • В дальнейшем 86-DOS была приобретена Microsoft и стала основой для ОС MS-DOS 1.0, выпущенной в августе 1981 года.

  • FAT была вначале предназначена для работы с гибкими дисками размером менее 1 Мбайт и не предусматривала поддержки жестких дисков.
Структура FAT 16

  • Первый сектор жёсткого диска (сектор 1, дорожка 0) содержит так называемую главную загрузочную запись (Master Boot Record = MBR), которая загружается в память под управлением BIOS и выполняется.

  • В конце первого сектора HDD находится таблица разделов диска (Partition table). Эта таблица содержит до четырёх элементов, описывающих разделы диска.

  • Разделы могут трех типов: первичными (1), расширенными (1), не-DOS разделами (2-3). Основной раздел может содержать код загрузки операционной системы. Расширенный раздел может быть дополнительно разбит на подразделы. Каждому разделу и подразделу операционная система ставит в соответствие логический диск и назначает свое имя (C:, D:, E: и т.д.)
Таблица разделов логического диска

Самый первый сектор НЖМД содержит Главную корневую запись, которая загружается в память под управлением BIOS и выполняется. Последняя часть этого сектора содержит таблицу разделов: 4-элементную таблицу с 16-байтовыми элементами. Этой таблицей манипулирует программа FDISK (или эквивалентная ей в иной операционной системе).

Во время загрузки BIOS загружает главную корневую запись и передает ей управление. Она считывает таблицу разделов, чтобы определить раздел, помеченный как активный. Затем в память считывается корректный загрузочный сектор и выполняется.

Таблица логических дисков имеет формат, аналогичный формату таблицы разделов диска, но содержит только два элемента. Один из них указывает на первый сектор логического диска, он имеет код системы, соответствующий типу FAT логического диска. Второй элемент может иметь код системы равный 5 или 0. Если он равен пяти, то элемент указывает на следующую таблицу логических дисков, если код системы равен нулю, то раздел не используется.

Расширенный раздел DOS является необязательным, такой раздел используется обычно для создания дополнительных логических дисков.

Структура логического диска FAT


  • Загрузочная запись (первый сектор диска) – служит для загрузки ОС и организация хранения данных.

  • FAT (File Allocation Table) – таблица размещения файлов.

  • Корневой каталог – для FAT16 512 записей о файлах и каталогах, расположенных в корне файловой системы.
Структура раздела FAT изображена на слайде.

Файловая система FAT имеет древовидную структуру. В корневом каталоге располагаются 32-байтовые элементы, которые содержат информацию о файлах и других каталогах.

Корневой каталог занимает непрерывную область фиксированного размера. Размер корневого каталога задаётся при форматировании и определяет максимальное количество файлов и каталогов, которые могут быть в нём описаны.

Вслед за корневым каталогом на логическом диске находится область файлов и подкаталогов корневого каталога.

Область данных разбита на кластеры, причём нумерация кластеров начинается с двойки.

Файловая система FAT не может контролировать отдельно каждый сектор, поэтому она объединяет смежные сектора в кластеры ). Таким образом, уменьшается общее количество единиц хранения, за которыми должна следить файловая система. Размер кластера в является степенью двойки и определяется размером тома при форматировании диска. Кластер представляет собой минимальное пространство, которое может занимать файл.

Кластеру с номером 2 соответствуют первые секторы области данных.

Любой каталог содержит 32-байтовые элементы - дескрипторы, описывающие файлы и другие каталоги.

Элемент каталога FAT16


Содержание

Размер (байт)

Имя файла

8

Расширение

3

Байт атрибутов

1

Зарезервировано

10

Время

2

Дата

2

Номер начального кластера

2

Размер файла

4

  • Размер элемента каталога – 32 байта
Элемент каталога FAT16

«Время изменения» и «Дата изменения» кодируются по следующим формулам:

Время = (часых2048)+(минутых32)+(секунды+2);
Дата = ((год–1980)х512)+(месяц х 32)+день.

Функции FAT


  • Хранение информации о размещении файлов на диске

  • Хранение информации о свободном месте на диске

  • Хранение информации о сбойных кластерах на диске
Организация данных в FAT

  • FAT представляет собой таблицу, связывающую кластеры дискового пространства с файлами. В этой базе для каждого кластера предусматривается только один элемент.

  • Первые два элемента содержат информацию о самой системе FAT. Третий и последующие элементы ставятся в соответствие кластерам дискового пространства, начиная с первого кластера, отведенного для файлов.

  • Элементы FAT могут содержать несколько специальных значений, указывающих, что

    • кластер свободен, т.е. не использован ни одним файлом (для FAT16 это значение составляет 0000H);

    • кластер содержит один или несколько секторов с физическими дефектами и не должен использоваться (дл FAT16 это значение составляет FFF7H);

    • данный кластер - последний кластер файла (дл FAT16 это значение составляет FFF8 - FFFFH).

  • Для любого используемого файлом, но не последнего кластера элемент FAT содержит номер следующего кластера, занятого файлом, из-за этого FAT называют файловой системой со связанным списком индексов.
Логическая организация данных

На слайде представлена схема работы и организации FAT, а также фрагментация, когда части файла разбросаны по всему диску.

Размеры разделов и кластеров FAT 16 для Windows 95-2000


Размер раздела

Размер кластера

0 – 32 Мб

512 б

33 – 64 Мб

1 Кб

65 – 128 Мб

2 Кб

129 Мб – 25 6 Мб

4 Кб

25 7 Мб – 51 2 Мб

8 Кб

51 3 Мб – 102 4 Мб

16 Кб

1 Гб – 2 Гб

32 Кб

2 Гб – 4 Гб (Win’ 2000)

64 Кб

Файловая система VFAT

  • ФС Virtual FAT появилась в первой версии Win’95.

  • Во второй версии Win’95 пользователям была предложена уже FAT32.

  • Virtual FAT поддерживала длинные имена файлов (LFN), но была 16-разрядной.

  • Длинные имена (LFN) хранятся в специально отформатированных 32-байт записях, байт атрибутов у которых равен 0Fh.

  • Поддерживает разделы до 4 Гб.
Long File Names

  • FAT32 преодолела ограничение прежней системы наименования файлов "8.3". В VFAT имя файла может содержать до 255 символов. К счастью, FAT32 воспринимает файлы, которые уже существовали на диске, даже если эти файлы используются 16-битовыми приложениями, разработанными не для Windows 95. Для каждого имени файла VFAT создает псевдоним, соответствующий нотации "8.3". Например, у файла "Файл с длинным именем" будет псевдоним "файлсд~1 .doc" в FAT. Более того, VFAT тома будут совместимы с DOS и Windows 3.1. Например, дискету, которую создали в Windows 95 и на которую записали несколько файлов с длинными именами, можно прочесть в DOS или Windows 3.1.

  • Кроме того, в этой файловой системе может быть несколько расширений, разделяемых точкой. Однако тип файла определяется по последнему расширению, а остальные рассматриваются как имя файла.

  • Длинные имена (LFN) хранятся в специально отформатированных 32-байт записях, байт атрибутов у которых равен 0Fh. Для конкретного файла или подкаталога непосредственно перед его единственной записью каталога с его именем в формате 8.3 находится группа из одной или нескольких записей, представляющих длинное имя. Каждая такая запись содержит часть длинного имени файла не более 13 символов, и ОС составляет полное длинное имя из всех записей.

  • Длинные имена файлов хранятся на диске в указанном формате и размещаются в одном или нескольких 32-байт элементах каталога перед элементами каталога для коротких имен. Символы, составляющие имя файла, представлены в кодах Unicode, т. е. на каждый из них по 2 байта.
Элемент каталога V FAT

Пример длинного имени

Проблемы длинных имен


  • На первый взгляд использованный в FAT32 механизм длинных имен файлов позволяет сохранить преемственность с прикладными программами прошлого поколения и выглядит идеальным. Однако этот метод далек от совершенства:

    • Требуется больше дискового пространства

    • Бó льшая фрагментация (на уровне каталогов)
Файловая система FAT 32

  • FAT32 это развитие файловой системы FAT(VFAT, FAT16).

  • 32-разрядная адресация кластеров – максимальное число адресуемых кластеров – 4 294 377 472.

  • Поддержка больших разделов (более 4Gb), кроме этого уменьшен размер кластера на разделе.

  • Поддержка длинных имен до 255 символов, причем нет ограничений на число и размер расширения.

  • Корневой каталог, раньше имевший фиксированный размер и строго определенное место на диске, теперь можно свободно наращивать по мере необходимости подобно подкаталогу. Теперь не существует ограничений на число записей в корневом каталоге. Это особенно важно, поскольку под каждое длинное имя файла используется несколько записей каталога.
Имеет более высокую надежность: FAT32 способна перемещать корневой каталог, создает резервную копию загрузочного сектора и может работать с резервной копией FAT.

Система MS-DOS до версии 7.0 включительно и система Windows могли распознать 65 536 логических блоков на диске. Начиная с Windows 95 OSR2, появилась возможность использовать для нумерации логических элементов на диске 32-разрядные данные, а значит, число адресуемых элементов теоретически возросло до 4 294 967 295.

Зеркализация FAT


  • Исторически сложилось так, что на всех FAT-дисках существуют 2 экземпляра таблицы FAT. Ecли при чтении исходного экземпляра возникает ошибка, файловая система пытается считать его резервную копию. На дисках с 12-и 16-разрядной FAT первая таблица FAT всегда является основной, и все изменения автоматически записываются в ее копию. Создание резервной копии второй таблицы FAT называется зеркализацией (mirroring).

  • В FAT32 зеркализацию второй таблицы FAT можно отключить. Тогда операции чтения/записи ускоряются, а если первая FAT оказывается поврежденной, используется ее второй экземпляр (он становится основным). На FAT32-дисках таблица FAT может достигать огромных размеров, и отключение зеркализации способно заметно ускорить доступ к файлам.
Структура элемента каталога

  • Чтобы обеспечить возможность работы с возросшим числом кластеров, в записи каталога для каждого файла должно выделяться 4 байт для начального кластера файла.

  • 2 дополнительных байта (по сравнению с FAT16 и VFAT) выделяются среди зарезервированных 10 байт.
Сравнение FAT16 и FAT32

  • Самое принципиальное отличие заключается в том, что FAT32 намного эффективнее расходует дисковое пространство. FAT32 использует дисковые кластеры меньшего размера по сравнению с предыдущими версиями, которые ограничивались 65 535 кластерами на том (соответственно с увеличением размера диска приходилось увеличивать и размер кластеров). Следовательно, даже для дисков размером до 8 Гбайт FAT32 может использовать 4-килобайтные кластеры. В результате по сравнению с дисками FAT16 экономится в среднем 10-15% дискового пространства.

  • FAT32 также может перемещать корневой каталог и использовать резервную копию FAT вместо стандартной. Расширенная загрузочная запись FAT32 позволяет создавать копии критических структур данных; это повышает устойчивость дисков FAT32 к нарушениям структуры FAT по сравнению с предыдущими версиями. Корневой каталог в FAT32 представлен в виде обычной цепочки кластеров. Следовательно, корневой каталог может находиться в произвольном месте диска, что снимает действовавшее ранее ограничение на размер корневого каталога (512 элементов).

FAT 16

-

максимальный размер раздела – 2Гб (для Win2000 – 4Гб);

-

при размере раздела > 512 Мб неэкономно расходует место на диске (из-за большого размера кластера);

+

распознается большинством ОС, используемыми на ПК;

+

позволяет уплотнять диск программой сжатия данных Drivespace;

-

имеет корневой каталог фиксированного размера (512 записей).

FAT 32

-

работает чуть медленнее, чем FAT16;

+

разделы до 2Тб;

+

большая эффективность использования места на диске;

-

нельзя уплотнить с помощью программы сжатия данных;

-

старые версии DOS и многие другие ОС не "видят" разделы с форматом FAT 32;

+

корневой каталог является обычным расширяемым каталогом.

Для создания нового или открытия существующего файла используется функция CreateFile. При использовании функцииCreateFile необходимо указать, предполагается чтение или запись в файл или и то и другое. Также необходимо указать, необходимые действия в случае наличия файла на диске или его отсутствия (например, перезаписывать файл, если он существует и создавать новый, если – нет). Также функцияCreateFile позволяет указать возможность разделения файла с другими приложениями (одновременного чтения/записи нескольких приложений в файл). Если некоторое приложение монополизировало доступ к файлу на чтение и/или запись, то никакое другое приложение не сможет читать и/или писать в файл, пока первое его не закроет.

Операционная система присваивает каждому открытому файлу уникальный идентификатор (дескриптор, filehandle), который может быть использован при обращениях к файлу для записи, чтения и получения информации о файле. Дескриптор действителен до тех пор, пока файл не закрыт. Приложение может наследовать дескрипторы файлов от процесса, который его запустил (если дескрипторы наследуемые).

Функция CreateFileсреди прочих объектов позволяет обращаться к файлам и каналам (pipes). При обращении к каналам функцияCreateFileпозволяет создавать клиентское подключение к именованным каналам, находящимся в режиме ожидания подключения. Серверная часть канала создаётся функцией CreateNamedPipe. Одно приложение может создавать множество клиентских подключений к каналу, но к одному экземпляру канала может подключаться только одно приложение (Стоит отметить, что возможно существование нескольких экземпляров именованных каналов с одинаковыми именами).

Функция CreateFileимеет следующий прототип:

HANDLE CreateFile(

LPCTSTR lpFileName , // file name

DWORD dwDesiredAccess , // access mode

DWORD dwShareMode , // share mode

LPSECURITY_ATTRIBUTES lpSecurityAttributes , // SD

DWORD dwCreationDisposition , // how to create

DWORD dwFlagsAndAttributes , // file attributes

HANDLE hTemplateFile // handle to template file

Параметры

lpFileName

Указатель на строку, содержащую имя объекта для создания или открытия (имя файла, путь к файлу, имя канала и пр.). Если имя задается литеральной константой, то следует поместить её в макросTEXT() или поставитьLперед открывающей кавычкой. Например, TEXT("C:\\tmp.txt") или L"C:\\tmp.txt"

dwDesiredAccess

Описание желаемого режима доступа к файлу. Существует возможность передачи большого количества различных флагов. Нас интересуют флаги GENERIC_READ, GENERIC_WRITE и их объединение. При доступе к каналам следует учитывать режим создания канала сервером. Если сервер создал канал для записи, то клиент открывает его для чтения и наоборот. Если сервер создал канал для чтения и записи, то клиент может открыть его как для чтения, так и для записи.

dwShareMode

Определяет режим разделения объекта

0 – приложение открывает файл для монопольного доступа. Последующие обращения на открытие данного файла будут безуспешными, пока данных дескриптор не будет закрыт. Для разделения доступа к файлу могут использоваться следующие ключи (один или вместе):

FILE_SHARE_READ – разрешены лишь последующие операции открытия только на чтение.

FILE_SHARE_WRITE – разрешены лишь последующие операции открытия только на запись.

lpSecurityAttributes

Указатель на сруктуру SECURITY_ATTRIBUTES, которая определяет возможность наследования дескриптора дочерними процессами. Можно передаватьNULL– это значит, что дескриптор не может быть наследован (для наших приложений этого достаточно).

dwCreationDisposition

Определяет то, какие действия необходимо предпринять в случаях, если файл существует и если файл не существует. Этот параметр должен иметь одно из следующих заначений:

Значение

Пояснение

Создаёт файл. Вызов заканчивается неудачей, если файл существует.

Создаёт новый файл. Если файл существует, то его содержимое и атрибуты будут стёрты.

Открытие файла. Если файл не существует, то вызов закончится неудачей.

Открывает файл. Если файл не существует, то он будет создан.

TRUNCATE_EXISTING

Открывает файл, размер которого обнуляется. Файл должен открываться как минимум с режимом доступа GENERIC_WRITE. Если файл не существует, то вызов будет неудачен.

dwFlagsAndAttributes

Позволяет задавать файловые атрибуты (только для чтения, скрытый, системный и пр.). Также позволяет сообщать операционной системе желаемое поведение при работе с файлами. Например, запись в файл без буферизации (FILE_FLAG_NO_BUFFERING и FILE_FLAG_WRITE_THROUGH); оптимизация для неупорядоченного доступа (FILE_FLAG_RANDOM_ACCESS); открытие для асинхронного ввода/вывода (FILE_FLAG_OVERLAPPED).

В лекции рассматривается использование функций библиотеки (C++): CreateFile(), CopyFile(), MoveFile(), DeleteFile(), ReadFile(), WriteFile(), UnlockFile(), LockFile() и CloseHandle().

Материалы по данной теме имеются так же в лекции про файлы .

Содержание лекции:

Создание файлов
Для создания нового или открытия существующего файла используется функция CreateFile. При использовании функции CreateFile необходимо указать, предполагается чтение или запись в файл или и то и другое. Также необходимо указать, необходимые действия в случае наличия файла на диске или его отсутствия (например, перезаписывать файл, если он существует и создавать новый, если – нет). Также функция CreateFile позволяет указать возможность разделения файла с другими приложениями (одновременного чтения/записи нескольких приложений в файл). Если некоторое приложение монополизировало доступ к файлу на чтение и/или запись, то никакое другое приложение не сможет читать и/или писать в файл, пока первое его не закроет.


Операционная система присваивает каждому открытому файлу уникальный идентификатор (дескриптор, file handle), который может быть использован при обращениях к файлу для записи, чтения и получения информации о файле. Дескриптор действителен до тех пор, пока файл не закрыт. Приложение может наследовать дескрипторы файлов от процесса, который его запустил (если дескрипторы наследуемые).


Функция CreateFile среди прочих объектов позволяет обращаться к файлам и каналам (pipes). При обращении к каналам функция CreateFile позволяет создавать клиентское подключение к именованным каналам, находящимся в режиме ожидания подключения. Серверная часть канала создаётся функцией CreateNamedPipe. Одно приложение может создавать множество клиентских подключений к каналу, но к одному экземпляру канала может подключаться только одно приложение (Стоит отметить, что возможно существование нескольких экземпляров именованных каналов с одинаковыми именами).

Функция CreateFile имеет следующий прототип:

HANDLE CreateFile(
LPCTSTR lpFileName, // file name
DWORD dwDesiredAccess, // access mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to template file
);


Параметры:


lpFileName:
Указатель на строку, содержащую имя объекта для создания или открытия (имя файла, путь к файлу, имя канала и пр.). Если имя задается литеральной константой, то следует поместить её в макрос TEXT() или поставить L перед открывающей кавычкой. Например, TEXT("C:\\tmp.txt") или L"C:\\tmp.txt"


dwDesiredAccess:
Описание желаемого режима доступа к файлу. Существует возможность передачи большого количества различных флагов. Нас интересуют флаги GENERIC_READ, GENERIC_WRITE и их объединение. При доступе к каналам следует учитывать режим создания канала сервером. Если сервер создал канал для записи, то клиент открывает его для чтения и наоборот. Если сервер создал канал для чтения и записи, то клиент может открыть его как для чтения, так и для записи.

dwShareMode:
Определяет режим разделения объекта
0 – приложение открывает файл для монопольного доступа. Последующие обращения на открытие данного файла будут безуспешными, пока данных дескриптор не будет закрыт. Для разделения доступа к файлу могут использоваться следующие ключи (один или вместе):
FILE_SHARE_READ – разрешены лишь последующие операции открытия только на чтение.
FILE_SHARE_WRITE – разрешены лишь последующие операции открытия только на запись.
lpSecurityAttributes
Указатель на сруктуру SECURITY_ATTRIBUTES , которая определяет возможность наследования дескриптора дочерними процессами. Можно передавать NULL – это значит, что дескриптор не может быть наследован (для наших приложений этого достаточно).

dwCreationDisposition:
Определяет то, какие действия необходимо предпринять в случаях, если файл существует и если файл не существует. Этот параметр должен иметь одно из следующих заначений:
Значение Пояснение
CREATE_NEW Создаёт файл. Вызов заканчивается неудачей, если файл существует.
CREATE_ALWAYS Создаёт новый файл. Если файл существует, то его содержимое и атрибуты будут стёрты.
OPEN_EXISTING Открытие файла. Если файл не существует, то вызов закончится неудачей.
OPEN_ALWAYS Открывает файл. Если файл не существует, то он будет создан.
TRUNCATE_EXISTING Открывает файл, размер которого обнуляется. Файл должен открываться как минимум с режимом доступа GENERIC_WRITE. Если файл не существует, то вызов будет неудачен.

dwFlagsAndAttributes:
Позволяет задавать файловые атрибуты (только для чтения, скрытый, системный и пр.). Также позволяет сообщать операционной системе желаемое поведение при работе с файлами. Например, запись в файл без буферизации (FILE_FLAG_NO_BUFFERING и FILE_FLAG_WRITE_THROUGH ); оптимизация для неупорядоченного доступа (FILE_FLAG_RANDOM_ACCESS ); открытие для асинхронного ввода/вывода (FILE_FLAG_OVERLAPPED ).

В озвращаемое значение:
Если вызов успешен, возвращается дескриптор открытого файла. Если вызов неудачен, возвращается константа INVALID_HANDLE_VALUE . Код ошибки можно получить вызовом функции GetLastError. Подробную информацию об ошибке (по её коду) можно получить вызовом функции FormatMessage.

Для копирования файлов используется функция CopyFile :


BOOL CopyFile(
LPCTSTR lpExistingFileName, // имя существующего файла
LPCTSTR lpNewFileName, // имя нового файла
BOOL bFailIfExists // действие, если файл существует TRUE – ошибка
// FALSE - перезаписывать
);


Для переименования файлов и директорий используется функция MoveFile :

BOOL MoveFile(
LPCTSTR lpExistingFileName, // имя файла
LPCTSTR lpNewFileName // новое имя файла
);


В случае успеха возвращается ненулевое значение.

Для удаления существующих файлов используется функция DeleteFile :

BOOL DeleteFile(
LPCTSTR lpFileName // имя удаляемого файла
);

В случае успеха возвращается ненулевое значение.

Чтение/запись в файл:
Каждый открытый файл имеет файловый указатель (file pointer), который указывает позицию следующего файла, который будет записан/прочтен. При открытии файла его файловый указатель перемещается на начало файла. После прочтения/записи очередного файла система перемещает файловый указатель. Файловый указатель можно перемещать, используя функцию SetFilePointer .
Для чтения/записи в файл используются функции ReadFile и WriteFile, при этом необходимо, чтобы файл был открыт на чтение и на запись соответственно.

Функция ReadFile читает из файла указанное количество символов, начиная с позиции, обозначенной файловым указателем. При синхронном (в противоположность асинхронному) чтении файловый указатель сдвигается на фактически прочитанное количество байт.

BOOL ReadFile(
LPVOID lpBuffer, // буфер данных
DWORD nNumberOfBytesToRead, // количество байт для прочтения
LPDWORD lpNumberOfBytesRead, // количество фактически прочитанных байт
// асинхронном чтении
);


Параметры:


hFile:
Дескриптор читаемого файла. Должен быть открыт с доступом GENERIC_READ.


lpBuffer:
Указатель на буфер, принимающий данные из файла.


nNumberOfBytesToRead:
Задаёт количество байт, которые необходимо прочитать из файла.


lpNumberOfBytesRead:
Указатель на переменную, которая принимает количество реально прочитанных байт.


lpOverlapped:


Возвращаемое значение :
Если возвращено ненулевое значение, но прочитано 0 байт, значит файловый указатель стоял на конце файла перед операцией чтения.

Функция WriteFile записывает в файл данные, начиная с позиции, обозначенной файловым указателем. При синхронной (в противоположность асинхронному) записи файловый указатель сдвигается на фактически записанное количество байт.


BOOL WriteFile(
HANDLE hFile, // дескриптор файла
LPCVOID lpBuffer, // буфер данных
DWORD nNumberOfBytesToWrite, // количество байт для записи
LPDWORD lpNumberOfBytesWritten,// количество фактически записанных байт
LPOVERLAPPED lpOverlapped // указатель на структуру, используемую при
// асинхронном чтении
);


Параметры:


hFile:
Дескриптор файла, в который производится запись. Должен быть открыт с доступом GENERIC_WRITE.


lpBuffer:
Указатель на буфер, содержащий данные, которые необходимо записать.


nNumberOfBytesToWrite:
Задаёт количество байт, которые необходимо записать в файл.


lpNumberOfBytesWritten:
Указатель на переменную, которая принимает количество реально записанных байт.


lpOverlapped:
Укзатель на структуру OVERLAPPED. При не асинхронном доступе следует передавать NULL.

Возвращаемое значение:
Если выполнение функции произошло успешно, то возвращается ненулевое значение.


Блокировка файлов:
Т.к. система позволяет более чем одному приложению открывать файл и писать в него, приложения не должны одновременно писать в одну область файла. Эта проблема может быть решена путем временного блокирования части файла. Функция LockFile позволяет приложению получить в монопольное пользования определённое количество байт в файле. Отрезок заблокированных байт может выходить за текущий конец файла. Все попытки сторонних приложений обратиться к заблокированному участку файла потерпят неудачу.
Приложение может файл с помощью функции UnlockFile .

BOOL LockFile(
HANDLE hFile, // дескриптор файла

DWORD nNumberOfBytesToLockLow, // младшее слово длины отрезка
DWORD nNumberOfBytesToLockHigh // старшее слово длины отрезка
);


Параметры:


hFile:
Дескриптор файла, в который производится запись. Должен быть открыт с доступом GENERIC_READ или GENERIC_WRITE (или обоими).

dwFileOffsetLow:
Определяет младшее слово смещения начала блокируемого отрезка относительно начала файла.


dwFileOffsetHigh:
Определяет старшее слово смещения начала блокируемого отрезка относительно начала файла. Не равно нулю, если смещение относительно начала более чем 232 байт.

n NumberOfBytesToLockLow:
Определяет младшее слово длины блокируемого отрезка.


nNumberOfBytesToLockHigh:
Определяет старшее слово длины блокируемого отрезка. Не равно нулю, если длина отрезка более 232 байт или 4 ГБ.


Возвращаемое значение:
Если выполнение функции произошло успешно, то возвращается ненулевое значение.
Блокирование предоставляет процессу монопольный доступ к отрезку файла. Файловые блокировки не наследуются. Остальные процессы не могут ни читать, ни писать в заблокированную часть файла.

Функция UnlockFile позволяет разблокировать участок файла, ранее заблокированный функцией LockFile .

BOOL UnlockFile(
HANDLE hFile, // дескриптор файла
DWORD dwFileOffsetLow, // младшее слово смещения начала отрезка
DWORD dwFileOffsetHigh, // старшее слово смещения начала отрезка
DWORD nNumberOfBytesToUnlockLow, // младшее слово длины отрезка
DWORD nNumberOfBytesToUnlockHigh // старшее слово длины отрезка
);


Параметры данной функции аналогичны параметрам функции LockFile .

Возвращаемое значение:
Если выполнение функции произошло успешно, то возвращается ненулевое значение.
Отрезок файла, который разблокируется функцией UnlockFile должен в точности соответствовать отрезку, заблокированному функцией LockFile. Например, две соседних части файла не могут быть заблокированы по отдельности, а разблокированы как единое целое. Процесс не должен завершать выполнение, имея заблокированные части файлов. Файловый дескриптор, для которого есть заблокированные отрезки, не должен закрываться.

Для оптимального использования ресурсов операционной системы приложение должно закрывать ненужные файлы с помощью функции CloseHandle . Файлы, открытые на момент завершения приложения, закрываются автоматически.

BOOL CloseHandle(
HANDLE hObject // Дескриптор объекта (файла, канала и пр.)
);


Возвращаемое значение:
Если выполнение функции произошло успешно, то возвращается ненулевое значение.



Похожие публикации