Delphi: обмен данными между приложениями

КОМПЬЮТЕРНЫЕ КУРСЫ "ПОИСК"

[Главная страница] [Delphi] [Контакты]

Проецируемые в память файлы


Не менее мощным и гибким методом организации обмена данными между приложениями является метод, который базируется на проецируемых в память файлах (Files Mapping). Главная идея этого механизма основывается на использовании динамической разделяемой памяти системы для хранения в ней данных. Как известно, каждый процесс имеет свой участок памяти, называемый виртуальным адресным пространством. При использовании механизма проецируемых в память файлов данные становятся доступны из любого процесса, который использует этот файл. В этом случае говорят, что файл отображается в виртуальное адресное пространство процесса, поэтому данные, хранимые в файле, доступны процессу, который этот файл открыл. Механизм проецирования файлов в память используется, например, для исполняемых файлов приложений, а также для DLL.

Для работы с проецируемыми в память файлами существует целый ряд API-функций. Но прежде чем их рассматривать, разберемся в процессе организации обмена данными через проецируемые файлы. На первом этапе необходимо создать объект (файл, отображаемый в память), затем "отобразить" созданный объект в адресное пространство процесса приложения, получая возможность записи и чтения данных их этого файла. При отображении файла на определенный участок памяти (адресного пространства процесса) манипуляции с данными этого участка памяти отражаются на содержимом файла. После произведенных над объектом манипуляций необходимо закрыть доступ к данным файла (удалить проекцию и закрыть файл).

Рассмотрим некоторые функции для работы с проецируемым в память файлом. Для того чтобы создать объект файла, проецируемого в память, можно использовать функцию CreateFileMapping. Ее синтаксис выглядит следующим образом:

function CreateFileMapping(
  hFile: THandle;		// дескриптор файла
  lpFileMappingAttributes: PSecurityAttributes; // атрибуты защиты
  flProtect,			// флаги доступа к файлу
  dwMaximumSizeHigh,		// старшее двойное слово размера объекта
  dwMaximumSizeLow: DWORD;	// младшее двойное слово размера объекта
  lpName: PWideChar		// имя объекта отображения
): THandle; stdcall;

Параметры:

  • hFile - должен содержать дескриптор открытого файла, для которого будет создаваться объект, отображающий этот файл в память процесса.
  • lpFileMappingAttributes - указатель на структуру TSecurityAttributes. Если указателю присвоить nil, то атрибуты защиты устанавливаются по умолчанию.
  • flProtect - содержит флаги, которые задают режимы доступа к виду файла в памяти процесса. Этот параметр может принимать одно из следующих значений:
    • PAGE_READONLY — из вида файла можно только читать данные;
    • PAGE_READWRITE — разрешает чтение и запись данных в вид файла;
    • PAGE_WRITECOPY — разрешает чтение и запись данных в вид файла, но при записи создается новая копия вида файла.
  • Кроме того, в параметре flProtect может быть установлена любая комбинация флагов, которые определяют атрибуты секций исполняемых файлов, заданных в переносимом формате (portable executable files). Эти флаги рассматриваться не будут.
  • dwMaximumSizeHigh, dwMaximumSizeLow - определяют соответственно значение старшей и младшей частей, которые в совокупности задают размер объекта, отображающего файл в память. Если эти значения установлены в 0, то объект, отображающий файл в память, имеет размер, равный размеру файла. Отметим, что если размер этого объекта будет меньше размера файла, то система не сможет отобразить весь файл в память. Если же размер объекта, отображающего файл, больше чем размер файла, то размер файла увеличивается до размера объекта.
  • lpName - используется для задания имени объекта, отображающего файл в память. Как обычно, это имя используется для доступа к одному и тому же объекту в разных процессах. Если процесс пытается получить доступ к уже созданному объекту, отображающему файл, то флаги доступа, установленные в параметре flProtect, должны соответствовать флагам доступа, уже установленным в существующем объекте, отображающем файл.

В случае успешного завершения эта функция возвращает дескриптор объекта (THandle), отображающего файл в память, а в случае неудачи — 0.

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

function MapViewOfFile(
  hFileMappingObject: THandle;  // дескриптор объекта, отображающего файл
  dwDesiredAccess: DWORD;   	// режим доступа
  dwFileOffsetHigh,             // старшее двойное слово смещения
  dwFileOffsetLow,              // младшее двойное слово смещения
  dwNumberOfBytesToMap: DWORD   // количество отображаемых байт
): Pointer; stdcall;

Параметры:

  • hFileMappingObject - должен содержать дескриптор объекта, отображающего файл в память, который был предварительно создан функцией CreateFileMapping.
  • dwDesiredAccess - задает режим доступа к виду файла и может принимать одно из следующих значений:
    • FILE_MAP_WRITE - чтение и запись в вид файла;
    • FILE_MAP_READ — только чтение из вида файла;
    • FILE_MAP_ALL_ACCESS — чтение и запись в вид файла;
    • FILE_MAP_COPY — при записи в вид файла создается его копия, а исходный файл не изменяется.
  • Следует отметить, что установленное в этом параметре значение должно соответствовать режиму доступа, который установлен для объекта, отображающего файл в память.
  • dwFileOffsetHigh и dwFileOffsetLow - задают смещение от начала файла или, другими словами, первый байт файла, начиная с которого файл отображается в память. Это смещение задается в байтах и должно быть кратно гранулярности распределения виртуальной памяти в системе (allocation granularity), которая может быть определена при помощи вызова функции GetSystemInfo. Единственным параметром этой функции является указатель на структуру SYSTEM_INFO, в поле dwAllocationGranularity которой функция GetSystemInfo помещает гранулярность (в байтах). Это значение зависит от архитектуры компьютера и в большинстве случаев равно 64 Кбайт.
  • dwNumberofBytesToMap - задает количество байт, которые будут отображаться в память из файла. Если значение этого параметра равно нулю, то в память будет отображен весь файл.

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

Следующей функцией, противоположной по производимым действиям функции MapViewOfFile, является UnMapViewOfFile. Она отключает проецируемый файл от текущего процесса:

function UnmapViewOfFile(lpBaseAddress: Pointer): BOOL; stdcall;

Функция принимает указатель, возвращаемый MapViewOfFile, и использует его для отмены проекции файла на адресное пространство процесса. В случае успешной выгрузки функция возвращает True, в противном случае — False.

И последняя функция, которую необходимо рассмотреть, — это CloseHandle. Она используется для закрытия дескриптора (многих системных объектов, а не только проекции файла).

function CloseHandle(hObject: THandle): BOOL; stdcall;

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

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

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

В секцию описания переменных программы помещаем следующие объявления:

var
  Form1: TForm1;
  //Глобальные переменные
  //Описатель объекта проецируемого файла
  hFileMapMapObj: THandle;
  //Указатель на начальный адрес данных
  lpBaseAddress: PChar;

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  //Создаем проецируемый файл с именем FileMemory
  //и передаем полученный в результате описатель
  //в глобальную пеменную hFileMapObj
  hFileMapMapObj := CreateFileMapping(MAXDWORD, nil, PAGE_READWRITE,
                              0, 4, 'FileMemory');
  if hFileMapMapObj = 0 then
    ShowMessage('Не могу создать проецируемый файл')
  else
    //Подключаем файл к адресному пространству
    //и получаем начальный адрес данных
    lpBaseAddress := MapViewOfFile(hFileMapMapObj, FILE_MAP_WRITE, 0, 0, 0);
  if lpBaseAddress = nil then
    ShowMessage('Не могу подключить проецируемый файл!')
  else
  begin
    //Считываем данные в проецируемый файл
    StrPCopy(lpBaseAddress, Edit1.Text);
    //Через 2 секунды сработает таймер
    Timer1.Enabled := True;
  end;
end;

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

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Label2.Caption := PChar(lpBaseAddress);
  Label2.Color := clRed;
  Timer1.Enabled := False;
end;

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

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  //Отключим файл от адресного пространства
  UnmapViewOfFile(lpBaseAddress);
  //Освобождаем объект файла
  CloseHandle(hFileMapMapObj);
  //Закрываем форму
  Action := caFree;
end;

Исходный код проекта (Delphi XE)

Используемая литература: Программирование в Delphi. Трюки и эффекты. Александр Чиртик