Реализация общей памяти в DLL
{ SHAREME.DPR — Пример использования общей памяти для организации межпроцессного взаимодействия Автор: Джим Мишель Дата последней редакции: 12/05/97 } library shareme; uses Windows, SysUtils, Classes; const pCounter: ^Longint = nil; function GetProcessCount : Longint; stdcall; export; begin Result := pCounter^; end; procedure MyDLLHandler (Reason: Integer);
const hMapObject : THandle = 0; var fInit : Boolean; begin case Reason of DLL_PROCESS_ATTACH : begin { Создаем именованный объект для совместного доступа } hMapObject := CreateFileMapping ( $FFFFFFFF,{ использовать страничный файл } nil,{ без атрибутов безопасности } PAGE_READWRITE,{ доступ по чтению/ записи } 0,{ старшие 32 бита размера } sizeof (longint),{ младшие 32 бита размера } "SharedMem"{ имя объекта } );
{ Память инициализируется первым присоединенным процессом } fInit := (GetLastError <>
ERROR_ALREADY_EXISTS);
{ Получаем указатель на общую область памяти, отображаемую на файл } pCounter := MapViewOfFile ( hMapObject, { отображаемый объект } FILE_MAP_WRITE, { доступ по чтению/записи } 0, { старшие 32 бита смещения } 0, { младшие 32 бита смещения } 0 { по умолчанию: отображение на весь файл } );
{ Инициализируем или увеличиваем счетчик } if (fInit) then pCounter^ := 1 else pCounter^ := pCounter^ + 1; end; DLL_PROCESS_DETACH : begin { Уменьшаем счетчик } pCounter^ := pCounter^ - 1; { Разрываем связь между общей памятью и адресным пространством процесса } UnmapViewOfFile (pCounter);
{ Закрываем логический номер объекта } CloseHandle (hMapObject);
end; (* Присоединение и отсоединение потоков не обрабатывается DLL_THREAD_ATTACH : DLL_THREAD_DETACH : *) end; end; Exports GetProcessCount index 1 name "GetProcessCount"; begin DLLProc := @MyDLLHandler; MyDLLHandler (DLL_PROCESS_ATTACH);
end.
Особое внимание следует обратить на две строки из секции инициализации DLL. Первая строка инициализирует переменную DLLProc из модуля System и заносит в нее ссылку на управляющую процедуру DLL (MyDLLHandler). Я думал, что ничего больше не потребуется, но оказалось, что при загрузке DLL почему-то не производится вызов этой процедуры с параметром DLL_PROCESS_ATTACH, поэтому такой вызов приходится организовывать в секции инициализации DLL. Видимо, в библиотеках Delphi допущена какая-то ошибка при генерации кода инициализации DLL.
Чтобы проверить, как работает общая память, создайте форму, при инициализации которой вызывается функция DLL GetProcessCount, и выведите значение переменной-счетчика с помощью компонента TLabel. Если запустить несколько экземпляров приложения, счетчик будет увеличиваться с присоединением каждой новой копии. Если закрыть один или несколько экземпляров приложения, а потом снова открыть их, соответственно изменится и значение счетчика (то есть если запустить три экземпляра, закрыть один, а потом запустить еще один, то итоговое значение счетчика процессов будет равно 3).
Глобальные области памяти (вроде той, что используется в SHAREME) поглощают драгоценные ресурсы Windows, так что старайтесь разумно подходить к их выделению. Если вы работаете со множеством различных полей из одной DLL, сгруппируйте их в общем блоке памяти (то есть в записи) и выделите один общий блок для всей информационной структуры. При этом объем ресурсов Windows, используемых программой, сводится к минимуму. Также проследите за тем, чтобы DLL правильно освобождали свою память. Если DLL аварийно завершится или по другой причине закончит работу, не освободив свои блоки памяти, то распределенная память и ресурсы Windows будут числиться занятыми до перезагрузки Windows. Если логический номер блока будет потерян, освободить память уже не удастся.