Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
delphi.pdf
Скачиваний:
197
Добавлен:
24.02.2016
Размер:
6.84 Mб
Скачать

@BubleSort := GetProcAddress(LibHandle, 'BubleSortIntegers');

@QuickSort := GetProcAddress(LibHandle, 'QuickSortIntegers');

Обратите внимание на использование символа @ перед именем каждой переменной. Он говорит о том, что выполняется не вызов подпрограммы, а работа с ее адресом.

Если этот адрес отличен от значения nil, значит подпрограмма с указанным именем была найдена в библиотеке и ее можно вызвать путем обращения к процедурной переменной:

if (@BubleSort <> nil) and (@QuickSort <> nil) then

begin

...

BubleSort(Arr);

...

QuickSort(Arr);

...

end

По окончании сортировки программа выгружает библиотеку вызовом функции FreeLibrary.

Как вы убедились, динамический импорт в сравнении со статическим требует значительно больше усилий на программирование, но он имеет ряд преимуществ:

Более эффективное использование ресурсов оперативной памяти по той причине, что библиотеку можно загружать и выгружать по мере надобности;

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

Динамический импорт отлично подходит для работы с библиотеками драйверов устройств. Он, например, используется самой средой Delphi для работы с драйверами баз данных.

5.4. Использование библиотеки из программы на языке C++

234

Созданные в среде Delphi библиотеки можно использовать в других языках программирования, например в языке C++. Язык C++ получил широкое распространение как язык системного программирования, и в ряде случаев программистам приходится прибегать к нему.

Ниже показано, как выполнить импорт подпрограмм BubleSort и QuickSort в языке C++.

extern "C" __declspec(dllimport)

void __stdcall BubleSort(int* Array, int HighIndex);

extern "C" __declspec(dllimport)

void __stdcall QuickSort(int* Array, int HighIndex);

Не углубляясь в детали синтаксиса, заметим, что в языке C++ отсутствуют открытые массивы в параметрах подпрограмм. Тем не менее, программист может вызывать такие подпрограммы, основываясь на том, что открытый массив неявно состоит из двух параметров: указателя на начало массива и номера последнего элемента.

5.5. Глобальные переменные и константы

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

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

5.6. Инициализация и завершение работы библиотеки

Инициализация библиотеки происходит при ее подключении к программе и состоит в выполнении секций initialization во всех составляющих библиотеку модулях, а также в ее главном программном блоке. Завершение работы библиотеки происходит при отключении библиотеки от программы; в этот момент в каждом модуле выполняется секция finalization.

235

Используйте эту возможность тогда, когда библиотека запрашивает и освобождает какие-то системные ресурсы, например файлы или соединения с базой данных. Запрос ресурса выполняется в секции initialization, а его освобождение — в секции finalization.

Существует еще один способ инициализации и завершения библиотеки, основанный на использовании предопределенной переменной DllProc. Переменная DllProc хранит адрес процедуры, которая автоматически вызывается при отключении библиотеки от программы, а также при создании и уничтожении параллельных потоков в программах, использующих DLL-библиотеку (потоки обсуждаются в главе 14). Ниже приведен пример использования переменной DllProc:

library MyLib;

var

SaveDllProc: TDLLProc;

procedure LibExit(Reason: Integer); begin

if Reason = DLL_PROCESS_DETACH then begin

...

// завершение библиотеки

end;

// вызов предыдущей процедуры

SaveDllProc(Reason);

end;

 

begin

// инициализация библиотеки

...

SaveDllProc := DllProc;

// сохранение предыдущей процедуры

DllProc := @LibExit;

// установка процедуры LibExit

end.

 

Процедура LibExit получает один целочисленный аргумент, который уточняет причину вызова. Возможные значения аргумента:

DLL_PROCESS_DETACH — отключение программы;

DLL_PROCESS_ATTACH — подключение программы;

DLL_THREAD_ATTACH — создание параллельного потока;

DLL_THREAD_DETACH — завершение параллельного потока.

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

236

Мы рекомендуем вам прибегать к переменной DllProc лишь в том случае, если библиотека должна реагировать на создание и уничтожение параллельных потоков. Во всех остальных случаях лучше выполнять инициализацию и завершение с помощью секций initialization и finalization.

5.7. Исключительные ситуации и ошибки выполнения подпрограмм

Для поддержки исключительных ситуаций среда Delphi использует средства операционной системы Window. Поэтому, если в библиотеке возникает исключительная ситуация, которая никак не обрабатывается, то она передается вызывающей программе. Программа может обработать эту исключительную ситуацию самым обычным способом — с помощью операторов try … except ... end. Такие правила действуют для программ и DLL-библиотек, созданных в среде Delphi. Если же программа написана на другом языке программирования, то она должна обрабатывать исключение в библиотеке, написанной на языке Delphi как исключение операционной системы с кодом $0EEDFACE. Адрес инструкции, вызвавшей исключение, содержится в первом элементе, а объект, описывающий исключение, — во втором элементе массива ExceptionInformation, который является частью системной записи об исключительной ситуации.

Если библиотека не подключает модуль SysUtils, то обработка исключительных ситуаций недоступна. В этом случае при возникновении в библиотеке любой ошибки происходит завершение вызывающей программы, причем программа просто удаляется из памяти и код ее завершения не выполняется. Это может стать причиной побочных ошибок, поэтому если вы решите не подключать к библиотеке модуль SysUtils, позаботьтесь о том, чтобы исключения "не выскальзывали" из подпрограмм библиотеки.

5.8. Общий менеджер памяти

Если выделение и освобождение динамической памяти явно или неявно поделены между библиотекой и программой, то и в библиотеке, и в программе следует обязательно подключить модуль ShareMem. Его нужно указать в секции uses первым, причем как в библиотеке, так и в использующей ее программе.

Модуль ShareMem является модулем импорта динамически загружаемой библиотеки Borlndmm.dll, которая должна распространяться вместе с вашей

237

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]