Delphi 3. Библиотека программиста

       

Переменная DLLProc


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

Кроме того, Windows сообщает DLL о факте присоединения или отсоеди нения процесса или программного потока (thread), но чтобы извлечь из этого пользу, придется немного потрудиться. Для этого следует подготовить специальную процедуру и присвоить ссылку на нее переменной DLLProc (определенной в модуле System). Процедура определяется так:

procedure DLLHandler (Reason: Integer);

Параметр Reason может быть равен одной из четырех констант: DLL_PROCESS_ ATTACH, DLL_PROCESS_DETACH, DLL_THREAD_ATTACH или DLL_THREAD_DETACH.

Вам придется организовать обработку сообщений DLL_PROCESS_ATTACH и вызвать CreateFileMapping, чтобы создать общий блок памяти (или получить указатель на уже имеющийся блок). Ваша DLL должна также обрабатывать сообщения DLL_PROCESS_DETACH и освобождать блок памяти, чтобы Windows могла удалить его, когда блок не будет использоваться ни одним процессом.

В проекте SHAREME.DPR (листинг 2.11) реализован общий блок памяти. В данном примере общая память представляет собой целое число, которое увеличивается с присоединением и уменьшается с отсоединением очередного процесса.

Листинг 2.11. Реализация общей памяти в 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. Если логический номер блока будет потерян, освободить память уже не удастся.



Содержание раздела