Программирование для 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 определяет тип доступа, который должен быть предоставлен к открываемому файлу. Здесь вы можете использовать логическую комбинацию следующих констант:
С помощью параметра dwShareMode задаются режимы совместного использования открываемого или создаваемого файла. Для этого параметра вы можете указать комбинацию следующих констант:
Через параметр lpSecurityAttributes необходимо передать указатель на дескриптор защиты или значение NULL, если этот дескриптор не используется. В наших приложениях мы не работаем с дескриптором защиты. Параметр dwCreationDistribution определяет действия, выполняемые функцией CreateFile, если приложение пытается создать файл, который уже существует. Для этого параметра вы можете указать одну из следующих констант:
Параметр dwFlagsAndAttributes задает атрибуты и флаги для файла. При этом можно использовать любые логические комбинации следующих атрибутов (кроме атрибута FILE_ATTRIBUTE_NORMAL, который можно использовать только отдельно):
В дополнение к перечисленным выше атрибутам, через параметр dwFlagsAndAttributes вы можете передать любую логическую комбинацию флагов, перечисленных ниже:
И, наконец, последний параметр 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 определяет способ изменения текущей позиции и может принимать одно из перечисленных ниже значений:
В случае успешного завершения функция 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. |