Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Программирование для Windows NT

© Александр Фролов, Григорий Фролов
Том 26, часть 1, М.: Диалог-МИФИ, 1996, 272 стр.

[Назад] [Содеожание] [Дальше]

Функции для работы с файлами

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

Универсальная функция CreateFile

Можно было бы подумать, что функция CreateFile предназначена только для создания файлов, аналогично функции _lcreat XE "_lcreat" из программного интерфейса Microsoft Windows версии 3.1, однако это не так. Во-первых, с помощью функции CreateFile можно выполнять не только создание нового файла, но и открывание существующего файла или каталога, а также изменение длины существующего файла. Во-вторых, эта функция может выполнять операции не только над файлами, но и над каналами передачи данных, трубами XE "трубы" (pipe XE "pipe" ), дисковыми устройствами и консолями. В этом томе мы, однако, ограничимся лишь файлами.

Прототип функции CreateFile мы привели ниже:


HANDLE CreateFile(
  LPCTSTR lpFileName,     // адрес строки имени файла 
  DWORD  dwDesiredAccess, // режим доступа 
  DWORD  dwShareMode, // режим совместного использования файла 
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // дескриптор 
                                              // защиты 
  DWORD  dwCreationDistribution, // параметры создания 
  DWORD  dwFlagsAndAttributes,   // атрибуты файла 
  HANDLE hTemplateFile);  // идентификатор файла с атрибутами

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

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

 Константа

 Описание

 0

 Доступ запрещен, однако приложение может определять атрибуты файла или устройства, открываемого при помощи функции CreateFile XE "CreateFile"

 GENERIC_READ

 Разрешен доступ на чтение

 GENERIC_WRITE

 Разрешен доступ на запись

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

 Константа

 Описание

 0

 Совместное использование файла запрещено

 FILE_SHARE_READ

 Другие приложения могут открывать файл с помощью функции CreateFile XE "CreateFile" для чтения

 FILE_SHARE_WRITE

 Аналогично предыдущему, но на запись

Через параметр lpSecurityAttributes необходимо передать указатель на дескриптор защиты или значение NULL, если этот дескриптор не используется. В наших приложениях мы не работаем с дескриптором защиты.

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

 Константа

 Описание

 CREATE_NEW

 Если создаваемый файл уже существует, функция CreateFile возвращает код ошибки

 CREATE_ALWAYS

 Существующий файл перезаписывается, при этом содержимое старого файла теряется

 OPEN_EXISTING

 Открывается существующий файл. Если файл с указанным именем не существует, функция CreateFile XE "CreateFile" возвращает код ошибки

 OPEN_ALWAYS

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

 TRUNCATE_EXISTING

 Если файл существует, он открывается, после чего длина файла устанавливается равной нулю. Содержимое старого файла теряется. Если же файл не существует, функция CreateFile XE "CreateFile" возвращает код ошибки

Параметр dwFlagsAndAttributes задает атрибуты и флаги для файла.

При этом можно использовать любые логические комбинации следующих атрибутов (кроме атрибута FILE_ATTRIBUTE_NORMAL, который можно использовать только отдельно):

 Атрибут

 Описание

 FILE_ATTRIBUTE_ARCHIVE

 Файл был архивирован (выгружен)

 FILE_ATTRIBUTE_COMPRESSED

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

 FILE_ATTRIBUTE_NORMAL

 Остальные перечисленные в этом списка атрибуты не установлены

 FILE_ATTRIBUTE_HIDDEN

 Скрытый файл

 FILE_ATTRIBUTE_READONLY

 Файл можно только читать

 FILE_ATTRIBUTE_SYSTEM

 Файл является частью операционной системы

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

 Флаг

 Описание

 FILE_FLAG_WRITE_THROUGH

 Отмена промежуточного кэширования данных для уменьшения вероятности потери данных при аварии

 FILE_FLAG_NO_BUFFERING

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

 FILE_FLAG_OVERLAPPED

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

 FILE_FLAG_RANDOM_ACCESS

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

 FILE_FLAG_SEQUENTIAL_SCAN

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

 FILE_FLAG_DELETE_ON_CLOSE

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

 FILE_FLAG_BACKUP_SEMANTICS

 Файл будет использован для выполнения операции выгрузки или восстановления. При этом выполняется проверка прав доступа

 FILE_FLAG_POSIX_SEMANTICS

 Доступ к файлу будет выполняться в соответствии со спецификацией POSIX

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

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

В том случае, если файл уже существует и были указаны константы CREATE_ALWAYS или OPEN_ALWAYS, функция CreateFile XE "CreateFile" не возвращает код ошибки. В то же время в этой ситуации функция GetLastError XE "GetLastError" возвращает значение ERROR_ALREADY_EXISTS XE "ERROR_ALREADY_EXISTS" .

Функция CloseHandle

Функция CloseHandle позволяет закрыть файл. Она имеет единственный параметр - идентификатор закрываемого файла. Заметим, что если мы указали функции CreateFile XE "CreateFile" флаг FILE_FLAG_DELETE_ON_CLOSE, сразу после закрывания файл будет удален. Как мы уже говорили, такая методика очень удобна при работе со временными файлами.

Функции ReadFile и WriteFile

С помощью функций ReadFile и WriteFile приложение может выполнять, соответственно, чтение из файла и запись в файл. По своему назначению эти функции аналогичны функциям _lread XE "_lread" , _lwrite XE "_lwrite" , _hread XE "_hread" и _hwrite XE "_hwrite" из программного интерфейса Microsoft Windows версии 3.1.

Приведем прототипы функций ReadFile и WriteFile:


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

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

Через параметр hFile этим функциям необходимо передать идентификатор файла, полученный от функции CreateFile XE "CreateFile" .

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

Параметр nNumberOfBytesToRead используется для функции ReadFile и задает количество байт данных, которые должны быть прочитаны в буфер lpBuffer. Аналогично, параметр nNumberOfBytesToWrite задает функции WriteFile XE "WriteFile" размер блока данных, имеющего адрес lpBuffer, который должен быть записан в файл.

Так как в процессе чтения возможно возникновение ошибки или достижение конца файла, количество прочитанных или записанный байт может отличаться от значений, заданных, соответственно, параметрами nNumberOfBytesToRead и nNumberOfBytesToWrite. Функции ReadFile XE "ReadFile" и WriteFile XE "WriteFile" записывают количество действительно прочитанных или записанных байт в двойное слово с адресом, соответственно, lpNumberOfBytesRead и lpNumberOfBytesWrite.

Параметр lpOverlapped используется в функциях ReadFile и WriteFile для организации аснхронного режима чтения и записи. Если запись выполняется синхронно, в качестве этого параметра следует указать значение NULL. Способы выполнения асинхронного чтения и записи мы рассмотрим позже. Заметим только, что для использования асинхронного режима файл должен быть открыт функцией CreateFile XE "CreateFile" с использованием флага FILE_FLAG_OVERLAPPED. Если указан этот флаг, параметр lpOverlapped не может иметь значение NULL. Он обязательно должен содержать адрес подготовленной структуры типа OVERLAPPED.

Если функции ReadFile и WriteFile были выполнены успешно, они возвращают значение TRUE. При возникновении ошибки возвращается значение FALSE. В последнем случае вы можете получить код ошибки, вызвав функцию GetLastError XE "GetLastError" .

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

Функция FlushFileBuffers

Так как ввод и вывод данных на диск в операционной системе Microsoft Windows NT буферизуется, запись данных на диск может быть отложена до тех пор, пока система не освободится от выполнения текущей работы. С помощью функции FlushFileBuffers XE "FlushFileBuffers" вы можете принудительно заставить операционную систему записать на диск все изменения для файла, идентификатор которого передается этой функции через единственный параметр:


BOOL FlushFileBuffers(HANDLE hFile);

В случае успешного завершения функция возвращает значение TRUE, при ошибке - FALSE. Код ошибки вы можете получить при помощи функции GetLastError XE "GetLastError" .

Заметим, что при закрывании файла функцией CloseHandle содержимое всех буферов, связанных с этим файлом, записывается на диск автоматически. Поэтому вы должны использовать функцию FlushFileBuffers XE "FlushFileBuffers" только в том случае, если запись содержимого буферов нужно выполнить до закрывания файла.

Функция SetFilePointer

С помощью функции SetFilePointer приложение может выполнять прямой доступ к файлу, перемещая указатель текущей позиции, связанный с файлом. Сразу после открывания файла этот указатель устанавливается в начало файла. Затем он передвигается функциями ReadFile XE "ReadFile" и WriteFile XE "WriteFile" на количество прочитанных или записанных байт, соответственно.

Функция SetFilePointer позволяет выполнить установку текущей позиции:


DWORD SetFilePointer(
  HANDLE hFile,                // идентификатор файла 
  LONG   lDistanceToMove, // количество байт, на которое будет 
                          // передвинута текущая позиция 
  PLONG  lpDistanceToMoveHigh, // адрес старшего слова, 
            // содержащего расстояние для перемещения позиции 
  DWORD  dwMoveMethod);        // способ перемещения позиции 

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

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

Если вы работаете с файлами, размер которых не превышает 232 - 2 байта, для параметра lpDistanceToMoveHigh можно указать значение NULL. В том случае, когда ваш файл очень большой, для указания смещения может потребоваться 64-разрядное значение. Для того чтобы указать очень большое смещение, вы должны записать старшее 32-разрядное слово этого 64-разрядного значения в переменную, и передать функции SetFilePointer XE "SetFilePointer" адрес этой переменной через параметр lpDistanceToMoveHigh. Младшее слово смещения следует передавать как и раньше, через параметр lDistanceToMove.

Параметр dwMoveMethod определяет способ изменения текущей позиции и может принимать одно из перечисленных ниже значений:

 Значение

 Описание

 FILE_BEGIN

 Смещение отсчитывается от начала файла, при этом значение смещения трактуется как беззнаковая величина

 FILE_CURRENT

 Смещение отсчитывается от текущей позиции в файле и может принимать как положительные, так и отрицательные значения

 FILE_END

 Смещение отсчитывается от конца файла и трактуется как отрицательная величина

В случае успешного завершения функция SetFilePointer возвращает младшее слово новой 64-разрядной позиции в файле. Старшее слово при этом записывается по адресу, заданному параметром lpDistanceToMoveHigh.

При ошибке функция возвращает значение 0xFFFFFFFF. При этом в слово по адресу lpDistanceToMoveHigh записывается значение NULL. Код ошибки вы можете получить при помощи функции GetLastError XE "GetLastError" .

Функция SetEndOfFile

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


BOOL SetEndOfFile(HANDLE hFile);

Для изменения длины файла вам достаточно установить текущую позицию в нужное место с помощью функции SetFilePointer XE "SetFilePointer" , а затем вызвать функцию SetEndOfFile XE "SetEndOfFile" .

Функции LockFile и UnlockFile

Так как операционная система Microsoft Windows NT является мультизадачной и допускает одновременную работу многих процессов, возможно возникновение ситуаций, в которых несколько задач попытаются выполнять запись или чтение для одних и тех же файлов. Например, два процесса могут попытаться изменить одни и те же записи файла базы данных, при этом третий процесс будет в то же самое время выполнять выборку этой записи.

Напомним, что если функции CreateFile указать режимы совместного использования файла FILE_SHARE_READ XE "FILE_SHARE_READ" или FILE_SHARE_WRITE XE "FILE_SHARE_WRITE" , несколько процессов смогут одновременно открыть файлы и выполнять операции чтения и записи, соответственно. Если же эти режимы не указаны, совместное использование файлов будет невозможно. Первый же процесс, открывший файл, заблокирует возможность работы с этим файлом для других процессов.

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

Блокировка участка файла выполняется функцией LockFile, прототип которой представлен ниже:


BOOL LockFile(
  HANDLE hFile, // идентификатор файла 
  DWORD  dwFileOffsetLow,  // младшее слово смещения области 
  DWORD  dwFileOffsetHigh, // старшее слово смещения области
  DWORD  nNumberOfBytesToLockLow,   // младшее слово длины 
                                    // области 
  DWORD  nNumberOfBytesToLockHigh); // старшее слово длины 
                                    // области

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

Смещение блокируемой области (64-разрядное) задается при помощи параметров dwFileOffsetLow (младшее слово) и dwFileOffsetHigh (старшее слово). Размер области в байтах задается параметрами nNumberOfBytesToLockLow (младшее слово) и nNumberOfBytesToLockHigh (старшее слово). Заметим, что если в файле блокируется несколько областей, они не должны перекрывать друг друга.

В случае успешного завершения функция LockFile возвращает значение TRUE, при ошибке - FALSE. Код ошибки вы можете получить при помощи функции GetLastError XE "GetLastError" .

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


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

Заметим, что в программном интерфейсе операционной системы Microsoft Windows NT есть еще две функции, предназначенные для блокирования и разблокирования областей файлов. Эти функции имеют имена, соответственно, LockFile XE "LockFile" Ex и UnlockFile XE "UnlockFile" Ex.

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

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

[Назад] [Содеожание] [Дальше]