|
.: Учебник для продвинутых по Delphi 7 :. ГЛАВА 9
Большинство приложений
создаются для того, чтобы обрабатывать данные — это прописная истина. С развитием
программных технологий мы имеем возможность получать и посылать все более крупные
и сложные массивы данных; однако до сих пор 90% из них хранятся в файлах.
Для использования файлов
в приложении разработчику приходится решать множество задач. Главные из них
— поиск необходимого файла и выполнение с ним операций ввода/вывода. Основные принципы и структура
файловой системы мало изменились со времен MS-DOS. Файловые системы (FAT32,
NTFS), появившаяся в Windows 2000 служба
Active Directory не изменяют главного — понятия файла и способов обращения
к нему. Среда Delphi дает вам возможность
выбрать один из четырех вариантов работы: В этой главе мы изучим
все основные способы работы с файлами в приложениях Delphi на конкретных примерах
создания программного кода. Использование
файловых переменных. Типы файлов Зачастую современный программный
код Delphi для чтения данных из файла удивительно похож на аналогичный, написанный,
к примеру, в Turbo Pascal 4.0. Это возможно потому, что программисты Borland
сохранили неизменным "старый добрый" набор файловых функций, работающих
через файловые переменные. При организации операций
файлового ввода/вывода в приложении большое значение имеет, какого рода информация
содержится в файле. Чаше всего это строки, но встречаются двоичные данные или
структурированная информация, например массивы или записи. Естественно, что сведения
о типе хранящихся в файле данных важно изначально задать. Для этого используются
специальные файловые переменные, определяющие тип файла. Они делятся на нетипизированные
и типизированные. Перед началом работы с
любым файлом необходимо описать файловую переменную, соответствующую типу данных
этого файла. В дальнейшем эта переменная используется при обращении к файлу.
В Delphi имеется возможность
создавать нетипизированные файлы. Для их обозначения используется ключевое слово
file:
var UntypedFile: file; Такие файловые переменные
используются для организации быстрого и эффективного ввода/вывода безотносительно
к типу данных. При этом подразумевается, что данные читаются или записываются
в виде двоичного массива. Для этого применяются специальные процедуры блочного
чтения и записи (см. ниже). Типизированные файлы обеспечивают
ввод/вывод с учетом конкретного типа данных. Для их объявления используется
ключевое слово file of, к которому
добавляется конкретный тип данных. Например, для работы с файлом, содержащим
набор байтов, файловая переменная объявляется так:
var ByteFile: file of byte; При этом можно использовать
любые типы фиксированного размера, за исключением указателей. Разрешается применять
структурные типы, если их составные части удовлетворяют названному выше ограничению.
Например, можно создать файловую переменную для записи:
type Country = record
Name: String; Capital:
String; Population:
Longlnt; Square:
Longlnt; end;
var CountryFile: file of Country; Для работы с текстовыми
файлами используется специальная файловая переменная
TextFile или Text:
var F: TextFile; Теперь рассмотрим две самые
распространенные операции, выполняемые при работе с файлами. Это чтение и запись.
Для их осуществления применяются специальные функции файлового ввода/вывода.
Итак, для выполнения операции
чтения или записи необходимо произвести следующие действия: 1. Объявить файловую переменную
необходимого типа. 2. При помощи функции AssignFile
связать эту переменную с требуемым файлом. 3. Открыть файл
при помощи функций Append, Reset, Rewrite.
4. Выполнить операции чтения
или записи. При этом, в зависимости от сложности задачи и структуры данных,
может использоваться целый ряд вспомогательных функций. 5. Закрыть файл при помощи
функции CloseFile. Внимание По сравнению с Turbo Pascal изменились названия только двух функций: Assign стала AssignFile, a Close превратилась в CloseFile. В качестве примера рассмотрим небольшой фрагмент исходного кода.
Если в диалоге открытия файла OpenDlg был выбран файл, то его имя связывается с файловой переменной F при помощи процедуры AssignFiie. В качестве имени файла рекомендуется всегда передавать полное имя файла (включая его маршрут). Как раз в таком виде возвращают результат выбора файла диалоги работы с файлами TOpenDialog, TOpenPictureDiaiog. Затем при помощи процедуры Reset этот файл открывается для чтения и записи. В цикле выполняется чтение из файла текстовых строк и запись их в компонент TMemo. Процедура Readin осуществляет чтение текущей строки файла и переходит на следующую строку. Цикл выполняется, пока функция EOF не сообщит о достижении конца файла. После завершения чтения файл закрывается. Такой же исходный код можно использовать и для записи данных в файл. Необходимо только заменить процедуру чтения на процедуру записи. Теперь остановимся подробнее на назначении используемых для файлового ввода/вывода функций. Открытие файла может осуществляться тремя процедурами — в зависимости от типа его дальнейшего использования. Процедура
открывает существующий файл для чтения и записи, текущая позиция устанавливается на первой строке файла. Процедура
открывает файл для записи информации после его последней строки, текущая позиция устанавливается на конец файла. Процедура
создает новый файл и открывает его, текущая позиция устанавливается в начало файла. Если файл с таким именем уже существует, то он перезаписывается. Переменная RecSize используется только при работе с нетипизированными файлами и определяет размер одной записи для операции передачи данных. Если этот параметр опущен, то по умолчанию RecSize равно 128 байт. Чтение данных из типизированных и текстовых файлов выполняют процедуры Read И Readin. Процедура Read имеет различное объявление для текстовых и других типизированных файлов:
При одном вызове процедуры можно читать данные в произвольное число переменных. Естественно, что тип переменных должен совпадать с типом файла. При чтении в очередную переменную читается ровно столько байтов из файла, сколько занимает тип данных. В следующую переменную читается столько же байтов, расположенных следом. После выполнения процедуры текущая позиция устанавливается на первом непрочитанном байте. Аналогично работают несколько процедур Read для одной переменной, выполненных подряд. Процедура
считывает одну строку текстового файла и устанавливает текущую позицию на следующей строке. Если использовать процедуру без переменных vi. .vn, то она просто передвигает текущую позицию на очередную строку файла. Процедуры для записи в файл write и writein описаны аналогично:
Параметры P1, P2, ..., Pn могут быть одним из целых или вещественных типов, одним из строковых типов или логическим типом. Но у них есть возможность дополнительного форматирования при выводе. Каждый параметр записи может иметь форму:
Рn — выводимая переменная или выражение; MinWidth — минимальная ширина поля в символах, которая должна быть больше 0; DecPlaces — содержит количество десятичных символов после запятой при отображении вещественных чисел с фиксированной точкой. Обратите внимание, что для текстовых файлов в функциях Read и write файловая переменная F может быть опущена. В этом случае чтение и запись осуществляются в стандартные файлы ввода/вывода. Когда программа компилируется как консольное приложение (флаг {$APPTYPE CONSOLE}), Delphi автоматически связывает входной и выходной файлы с окном консоли. Для контроля за текущей позицией в файле применяются две основные функции. Функция EOF(F) возвращает значение True, если достигнут конец файла. Функция EOLN(F) аналогично сигнализирует о достижении конца строки. Естественно, в качестве параметра в функции необходимо передавать файловую переменную. Процедура
обеспечивает смещение текущей позиции на N элементов. Размер одного элемента в байтах зависит от типа данных файла (от типизированной переменной). Рассмотрим теперь режим блочного ввода/вывода данных между файлом и областью адресного пространства (буфером). Этот режим отличается значительной скоростью передачи данных, причем скорость пропорциональна размеру одного передаваемого блока — чем больше блок, тем больше скорость. Для реализации этого режима необходимо использовать только нетипизированные файловые переменные. Размер блока определяется в процедуре открытия файла (Reset, Rewrite). Непосредственно для выполнения операций используются процедуры BlockRead и BlockWrite. Процедура
выполняет запись блока из файла в буфер. Параметр F ссылается на нетипизированную файловую переменную, связанную с нужным файлом. Параметр Buf определяет любую переменную (число, строку, массив, структуру), в которую читаются байты из файла. Параметр Count содержит число считываемых блоков. Наконец, необязательный параметр AmtTransferred возвращает число реально считанных блоков. При использовании блочного чтения или записи размер блока необходимо выбирать таким образом, чтобы он был кратен размеру одного значения того типа, который хранится в файле. Например, если в файле хранятся значения типа Double (8 байт), то размер блока может быть равен 8, 16, 24, 32 и т. д. Фрагмент исходного кода блочного чтения при этом выглядит следующим образом:
Как видно из примера, размер блока установлен в процедуре Reset и кратен размеру элемента массива DoubleArray, в который считываются данные. В переменной Transfered возвращается число считанных блоков. Если размер файла меньше заданного в процедуре BlockRead числа блоков, ошибка не возникает, а в переменной Transfered передается число реально считанных блоков. Процедура
используется аналогично. Оставшиеся функции ввода/вывода, работающие с файловыми переменными, подробно описаны в табл. 9.1. Таблица 9.1. Процедуры и функции для работы с файлом
Ввод/вывод с использованием функций Windows API Для тех, кто переходит на Delphi не с прежних версий Turbo Pascal, а с С, других языков или начинает освоение с "нуля", более привычными будут стандартные функции работы с файлами Windows. Тем более, что возможности ввода/вывода в них расширены. Каждый файл в этом наборе функций описывается не переменной, а дескриптором (Handle) — 32-разрядной величиной, которая идентифицирует файл в операционной системе. В Win32 файл открывается при помощи функции, имеющей обманчивое название:
Хоть ее название и начинается с create, но она позволяет не только создавать, но и открывать уже существующие файлы. Такое огромное количество параметров оправдано, т. к. createFile используется для открытия файлов на диске, устройств, каналов, портов и вообще любых источников ввода/вывода. Назначение параметров описано в табл. 9.2. Таблица 9.2. Параметры функции CreateFile
Функция createFile возвращает дескриптор открытого объекта ввода/вывода. Если открытие невозможно из-за ошибок, возвращается код INVALID_HANDLE_VALUE, а расширенный код ошибки можно узнать, вызвав функцию GetLastError. Закрывается файл в Win32 функцией closeHandie (не closeFile, a closeHandle! Правда, "легко" запомнить? Что поделать, так их назвали разработчики Win32). Приведем из большого разнообразия несколько приемов использования функции CreateFile. Часто программисты хотят иметь возможность организовать посекторный доступ к физическим устройствам хранения — например к дискете. Сделать это не так уж сложно, но при этом методы для Windows 98 и Windows 2000 различаются. В Windows 2000 придется открывать устройство ('\\.\A:'), а в Windows 98 — специальный драйвер доступа (обозначается '\\.\vwin32'). И то и другое делается функцией createFile. Листинг 9.1 Чтение сектора с дискеты при помощи функции CreateFile
Для чтения и записи данных в Win32 используются функции:
Здесь все сходно с BlockRead и Blockwrite: hFile — это дескриптор файла, Buffer — адрес, по которому будут читаться (писаться) данные; третий параметр означает требуемое число читаемых (записываемых) байтов, а четвертый — фактически прочитанное (записанное). Последний параметр — IpOverlapped — обсудим чуть позже. Функция createFile используется и для доступа к портам ввода/вывода. Часто программисты сталкиваются с задачей: как организовать обмен данными с различными нестандартными устройствами, подключенными к параллельному или последовательному порту? В Turbo Pascal для DOS был очень хороший псевдомассив Ports: пишешь Port[x] := у; и не знаешь проблем. В Win32 прямой доступ к портам запрещен и приходится открывать их как файлы:
Самое большое отличие от предыдущего примера — в скромном флаге FILE_FLAG_OVERLAPPED. О роли этих изменений- в следующем разделе
Отложенный (асинхронный) ввод/вывод Эта принципиально новая возможность введена впервые в Win32 с появлением реальной многозадачности. Вызывая функции чтения и записи данных, вы на самом деле передаете исходные данные одному из потоков (threads) операционной системы, который и осуществляет фактические обязанности по работе с устройством. Время доступа всех периферийных устройств гораздо больше доступа к ОЗУ, и ваша программа, вызвавшая Read или write, будет дожидаться окончания операции ввода/вывода. Замедление работы программы налицо. Выход был найден в использовании отложенного (overlapped) ввода/вывода. До начала отложенного ввода/вывода инициализируется дескриптор объекта типа события (функция createEvent) и структура типа TOveriapped. Вы вызываете функцию ReadFile или writeFile, в которой последним параметром указываете на TOveriapped. Эта структура содержит дескриптор события Windows (event). ОС начинает операцию (ее выполняет отдельный программный поток, скрытый от программиста) и немедленно возвращает управление; вы можете не тратить время на ожидание. Признак того, что операция началась и продолжается — получение кода возврата ERROR_IO_PENDING. Пусть вас не пугает слово "error" в названии — это совершенно нормально. Если операция продолжается долго (а чтение и запись файлов на дискете, да и на диске, именованных каналов можно отнести к "длинным" операциям), то программа может спокойно выполнять последующие операторы. Событие будет "взведено" ОС тогда, когда ввод/вывод закончится. Когда, по мнению программиста, ввод/вывод должен быть завершен, можно проверить это, использовав функцию WaitForSingleObject.
Объект ожидания (параметр hHandle) в этом случае — тот самый, который создан нами, указан в структуре TOveriapped и передан в качестве параметра в функцию ReadFile или WriteFile. Можно указать любое время ожидания, в том числе бесконечное (параметр Timeout при этом равен константе INFINITE). Признаком нормального завершения служит получение кода возврата WAIT_OBJECT_0. Листинг 9.2. Пример отложенной операции чтения
Если вы задали конечный интервал в миллисекундах, а операция еще не закончена, waitForSingieObject вернет код завершения WAIT_TIMEOUT. Функция GetOverlappedResult возвращает в параметре nb число байтов, действительно прочитанных или записанных во время отложенной операции.
При работе с файлами разработчик обязательно должен предусмотреть обработку возможных ошибок. Практика показывает, что именно операции ввода/вывода вызывают большую часть ошибок, возникающих в приложении из-за воздействия окружающей программной среды. Контроль за ошибками ввода/вывода зависит от применяемых функций. При использовании доступа через Win32 API все функции возвращают код ошибки Windows, который и нужно проанализировать. При возникновении ошибок ввода/вывода в функциях, использующих файловые переменные, генерируется исключительная ситуация класса EinOutError. Но так происходит только в том случае, если включен контроль ошибок ввода/вывода. Для этого используются соответствующие директивы компилятора:
Класс EinOutError отличается тем, что у него есть поле ErrorCode. При возникновении этой исключительной ситуации вы можете получить его значение и принять решение. Основные коды имеют такие значения:
При отключенном контроле в случае возникновения ошибки выполнение программы продолжается без остановки. Однако в этом случае устранение возможных последствий ошибки возлагается на разработчика. Для этого применяется функция
которая возвращает значение 0 при отсутствии ошибок.
Еще одна часто выполняемая с файлом операция — поиск файлов в заданном каталоге. Для организации поиска и отбора файлов используются специальные процедуры, а также структура, в которой сохраняются результаты поиска. Запись
обеспечивает хранение характеристик файла после удачного поиска. Дата и время создания файла хранятся в формате MS-DOS, поэтому для получения этих параметров в принятом в Delphi формате TDateTime необходимо использовать следующую функцию:
Обратное преобразование выполняет функция
Свойство Attr может содержать комбинацию следующих флагов-значений:
Для определения параметров файла используется оператор AND:
Непосредственно для поиска файлов используются функции FindFirst и FindNext. Функция
находит первый файл, заданный полным маршрутом Path и параметрами Attr (см. выше). Если заданный файл найден, функция возвращает 0, иначе — код ошибки Windows. Параметры найденного файла возвращаются в записи F типа TSearchRec. Функция
применяется для повторного поиска следующего файла, удовлетворяющего критерию поиска. При этом используются те параметры поиска, которые заданы последним вызовом функции FindFirst. В случае удачного поиска возвращается 0. Для освобождения ресурсов, выделенных для выполнения поиска, применяется функция:
В качестве примера организации поиска файлов рассмотрим фрагмент исходного кода, в котором маршрут поиска файлов задается в однострочном текстовом редакторе DirEdit, а список найденных файлов передается в компонент TListBox.
Потоки — очень удачное средство для унификации ввода/вывода для различных носителей. Потоки представляют собой специальные объекты-наследники абстрактного класса Tstream. Сам Tstream "умеет" открываться, читать, писать, изменять текущее положение и закрываться. Поскольку для разных носителей эти вещи происходят по-разному, конкретные аспекты реализованы в его потомках. Наиболее часто используются потоки для работы с файлами на диске и памятью. Многие классы VCL имеют унифицированные методы LoadFromstream и saveTostream, которые обеспечивают обмен данными с потоками. От того, с каким физическим носителем работает поток, зависит место хранения данных.
Базовые классы TStream и THandleStream В основе иерархии классов потоков лежит класс Tstream. Он обеспечивает выполнение основных операций потока безотносительно к реальному носителю информации. Основными из них являются чтение и запись данных. Класс Tstream порожден непосредственно от класса TObject. Потоки также играют важную роль в чтении/записи компонентов из файлов ресурсов (DFM). Большая группа методов обеспечивает взаимодействие компонента и потока, чтение свойств компонента из ресурса и запись значений свойств в ресурс. Таблица 9.3. Свойства и методы класса Tstream
Итак, в основе операций считывания и записи данных в потоке лежа! методы Read и Write. Именно они вызываются для реального выполнения операции внутри методов ReadBuffer И WriteBuffer, ReadComponent И WriteComponent. Так как класс TStream является абстрактным, то методы Read и write также являются абстрактными. В классах-наследниках они перекрываются, обеспечивая работу с конкретным физическим носителем данных. Метод Seek используется для изменения текущей позиции в потоке. "Точка отсчета" позиции зависит от значения параметра Origin:
Группа методов обеспечивает чтение и запись из потока ресурса компонента. Они используются при создании компонента на основе данных о нем, сохраненных в формате файлов ресурсов. Для чтения ресурса используется метод ReadComponentRes, в котором последовательно вызываются:
Класс THandleStream инкапсулирует поток, связанный с физическим носителем данных через дескриптор. Для создания потока используется конструктор
в параметре которого передается дескриптор. Впоследствии доступ к дескриптору осуществляется через свойство:
Класс TFileStream позволяет создать поток для работы с файлами. При этом поток работает с файлом без учета типа хранящихся в нем данных (см. выше). Полное имя файла задается в параметре FileName при создании потока:
Параметр Mode определяет режим работы с файлом. Он составляется из флагов режима открытия:
И флагов режима совместного использования:
Для чтения и записи из потока используются методы Read и write, унаследованные от класса THandleStream:
Обратите внимание, что в данном фрагменте кода функция seek используется для записи данных в конец файлового потока. При необходимости копирования одного файла в другой целиком используется метод CopyFrom, унаследованный от класса Tstream:
Обратите внимание, что в данном случае идя определения размера передаваемого потока необходимо использовать свойство stream, size, которое дает реальный объем данных, содержащихся в потоке. Функция sizeof (stream) в этом случае даст размер объекта потока, и не более того.
Класс TMemoryStream обеспечивает сохранение данных в адресном пространстве. При этом методы доступа к этим данным остаются теми же, что и при работе с файловыми потоками. Это позволяет использовать адресное пространство для хранения промежуточных результатов работы приложения, а также при помощи стандартных методов осуществлять обмен данными между памятью и другими физическими носителями. Свойство
определяет область памяти, отведенную для хранения данных потока. Изменение размера отведенной памяти осуществляется методом
Для очистки памяти потока используется метод
Чтение/запись данных в память выполняется привычными методами Read и Write. Также запись данных в память может осуществляться методами:
Дополнительно можно использовать методы записи данных в файл или поток:
Так как строковые константы и переменные широко применяются при разработке приложений, то для удобства работы с ними создан специальный класс TStringStream. Он обеспечивает хранение строки и доступ к ней во время выполнения приложения. Он обладает стандартным для потоков набором свойств и методов, добавляя к ним еще несколько, упрощающих использование строк. Свойство только для чтения
обеспечивает доступ к хранимой строке. Методы
И
реализуют обычный для потоков способ чтения и записи строки для произвольной переменной Buffer. Метод
обеспечивает чтение count байтов строки потока, начиная с текущей позиции. Метод
дописывает к строке строку AString, начиная с текущей позиции. При работе с файлами и потоками используются дополнительные классы исключительных ситуаций. Класс EFCreateError возникает при ошибке создания файла, a EFOpenError — при открытии файла. При чтении/записи данных в поток могут возникнуть исключительные ситуации EReadError И EWriteError.
Оповещение об изменениях в файловой системе Многие программисты задавались вопросом: как получить сигнал от операционной системы о том, что в файловой системе произошли какие-то изменения? Такой вид оповещения позаимствован из ОС UNIX и теперь доступен программистам, работающим с Win32. Для организации мониторинга файловой системы нужно использовать три функции — FindFirstChangeNotification, FindNextChangeNotification И FinddoseChangeNotification. Первая из них возвращает дескриптор объекта файлового оповещения, который можно передать в функцию ожидания. Объект активизируется тогда, когда в заданной папке произошли те или иные изменения (создание или уничтожение файла или папки, изменение прав доступа и т. д.). Вторая функция — готовит объект к реакции на следующее изменение. Наконец, с помощью третьей функции следует закрыть, ставший ненужным, объект. Так может выглядеть код метода Execute потока, созданного для мониторинга:
На главной форме должны находиться компоненты, нужные для выбора обследуемой папки, а также компонент TListBox, в который будут записываться имена файлов:
Приложение готово. Чтобы оно стало полнофункциональным, предусмотрите в нем механизм перезапуска потока при изменении обследуемой папки.
Использование отображаемых файлов Последний — самый нетрадиционный вид работы с файлами — это так называемые отображаемые файлы. Вообще говоря, в 32-разрядной Windows под "памятью" подразумевается не только оперативная память (ОЗУ), но также и память, резервируемая операционной системой на жестком диске. Этот вид памяти называется виртуальной памятью. Код и данные отображаются на жесткий диск посредством страничной системы (paging system) подкачки. Страничная система использует для отображения страничный файл (win386.swp в Windows 95/98 и pagefile.sys в Windows NT). Необходимый фрагмент виртуальной памяти переносится из страничного файла в ОЗУ и, таким образом, становится доступным. А что, если так же поступить и с любым другим файлом и сделать его частью адресного пространства? В Win32 это возможно. Для выделения фрагмента памяти должен быть создан специальный системный объект Win32, называемый отображаемым файлом. Этот объект "знает", как соотнести файл, находящийся на жестком диске, с памятью, адресуемой процессами. Одно или более приложений могут открыть отображаемый файл и получить тем самым доступ к данным этого объекта. Таким образом, данные, помещенные в страничный файл приложением, использующим отображаемый файл, могут быть доступны другим приложениям, если они открыли и используют тот же самый отображаемый файл. Создание и использование объектов файлового отображения осуществляется посредством функций Windows API. Этих функций три:
Отображаемый файл создается операционной системой при вызове функции CreateFileMapping. Этот объект поддерживает соответствие между содержимым файла и адресным пространством процесса, использующего этот файл. Функция CreateFiieMapping имеет шесть параметров:
Первый параметр имеет тип THandle. Он должен соответствовать дескриптору уже открытого при помощи функции createFile файла. Если значение параметра hFile равно SFFFFFFFF, то это приводит к связыванию объекта файлового отображения со страничным файлом операционной системы. Второй параметр — указатель на запись типа TSecurityAttributes. При отсутствии требований к защите данных в Windows NT значение этого параметра всегда равно nil. Третий параметр имеет тип DWORD. Он определяет атрибут защиты. Если при помощи отображаемого файла вы планируете совместное использование данных, третьему параметру следует присвоить значение PAGE_READWRITE. Четвертый и пятый параметры также имеют тип DWORD. Когда выполняется функция CreateFiieMapping, значение типа DWORD четвертого параметра сдвигается влево на четыре байта и затем объединяется со значением пятого параметра посредством операции and. Проще говоря, значения объединяются в одно 64-разрядное число, равное объему памяти, выделяемой объекту файлового отображения из страничного файла операционной системы. Поскольку вы вряд ли попытаетесь осуществить выделение более чем 4 Гбайт данных, то значение четвертого параметра всегда должно быть равно нулю. Используемый затем пятый параметр должен показывать, сколько памяти в байтах необходимо зарезервировать в качестве совместной. Если вы хотите отобразить весь файл, четвертый и пятый параметры должны быть равны нулю. Шестой параметр имеет тип PChar и представляет собой имя объекта файлового отображения. Функция CreateFileMapping возвращает значение типа THandle. В случае успешного завершения возвращаемое функцией значение представляет собой дескриптор созданного объекта файлового отображения. В случае возникновения какой-либо ошибки возвращаемое значение будет равно 0. Следующая задача — спроецировать данные файла в адресное пространство нашего процесса. Этой цели служит функция MapviewOfFile. Функция MapViewOfFile имеет пять параметров:
Первый параметр имеет тип THandle. Его значением должен быть дескриптор созданного объекта файлового отображения — тот, который возвращает функция createFileMapping. Второй параметр определяет режим доступа к файлу: FILE_MAP_WRITE, FILE_MAP_READ или FILE_MAP_ALL_ACCESS. Третий и четвертый параметры также имеют тип DWORD. Это — смещение отображаемого участка относительно начала файла в байтах. В нашем случае эти параметры должны быть установлены в нуль, поскольку значение, которое мы даем пятому (последнему) параметру функции MapViewOfFile, также равно нулю. Пятый (и последний) параметр функции MapViewOfFile, как и предыдущие параметры, имеет тип DWORD. Он используется для определения (в байтах) количества данных объекта файлового отображения, которые надо отобразить в процесс (сделать доступными для вас). Для достижения наших целей это значение должно быть установлено в нуль, что означает автоматическое отображение в процессе всех данных, выделенных перед этим функцией CreateFileMapping. Значение, возвращаемое функцией MapViewOfFile, имеет тип "указатель". Если функция отработала успешно, то она вернет начальный адрес данных объекта файлового отображения. Следующий фрагмент кода демонстрирует вызов функции MapViewOfFile:
После того как получен указатель pSharedBuf, вы можете работать со своим файлом как с обычной областью памяти, не заботясь о вводе, выводе, позиционировании и т. п. Все эти проблемы берет на себя файловая система. Последние две функции, имеющие отношение к объекту файлового отображения, называются UnMapViewOfFile И CloseHandle. Функция UnMapViewOfFile делает то, что подразумевает ее название. Она прекращает отображение в адресное пространство процесса того файла, который перед этим был отображен При помощи функции MapViewOfFile. Функция CloseHandle закрывает дескриптор объекта файлового отображения, возвращаемый функцией CreateFileMapping. Функция UnMapViewOfFile должна вызываться перед функцией CloseHandle. Функция UnMapViewOfFile передает единственный параметр типа указатель:
Отображаемые файлы уже будут использоваться и в других главах этой книги. Не стоит удивляться, ведь это очень мощный инструмент: помимо возможности совместного доступа он позволяет заметно ускорить доступ к файлам, особенно большого размера.
При разработке приложений очень часто приходится решать задачи обмена данными между приложениями или приложением и устройством ввода/вывода. При этом большая часть созданного кода обеспечивает работу приложения с файлами. В этой главе рассмотрены методы, обеспечивающие взаимодействие программы с файловой системой и примеры их использования. Для организации обмена данными в приложениях используются специальные объекты — потоки, которые не только хранят информацию во время выполнения приложения, но и предоставляют разработчику набор стандартных свойств и методов для управления данными. Затруднительно сказать, при изучении каких глав будет полезен материал этой главы. Скорее всего, он понадобится везде.
|
®Сайт разработал: Nek по вопросам пишите сюда NekSuper@yandex.ru |