Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Razrabotka_DLL

.pdf
Скачиваний:
3
Добавлен:
09.03.2016
Размер:
500.46 Кб
Скачать

11

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

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

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

МНОГОМОДУЛЬНЫЕ DLL-БИБЛИОТЕКИ

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

Правила, которых полезно придерживаться при разработке любых – и простых, и самых сложных динамических библиотек на языке Object Pascal:

1.Вся динамическая библиотека должна быть представлена как совокупность программных модулей (в частном случае, один-единственный модуль) языка Object Pascal. Процедуры и функции, которые могут быть экспортируемыми, должны быть объявлены в интерфейсной части с ключевым словом stdcall.

2.Файл проекта DLL-библиотеки (с расширением DPR) должен содержать раздел uses, подключающий все необходимые модули, раздел exports, перечисляющий экспортируемые функции, а также операторный блок begin ... end, инициализирующий динамическую библиотеку. Не забывайте, что каждый из модулей библиотеки может иметь секцию initialization, вызываемую перед инициализационным блоком DPR-файла, и секцию finalization, вызываемую перед выгрузкой DLL.

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

Разработаем динамическую библиотеку, которая продемонстрирует практическое использование описанных выше правил.

Выберите в меню команду Файл – Новый – ... и начните новую DLL. Затем выберите в меню команду Файл – Новый – Модуль ... и начните новый модуль. Сохраните заготовку модуля под именем Main.pas, а заготовку проекта – под именем StrLib.dpr. Сделайте текст модуля следующим:

unit Main;

interface

function Min(x,y:integer):Integer; stdcall; function Max(x,y:integer):Integer; stdcall;

implementation

function Min(x,y:integer):Integer; begin

if x<y then Min:=x else Min:=y; end;

function Max(x,y:integer):Integer; begin

if x>y then Max:=x else Max:=y; end;

end.

Переключитесь на файл проекта и вставьте в его исходный текст оператор exports с именами функций Min и Max. Результирующий текст файла проекта должен быть следующим:

library StrLib;

{ Important note about DLL memory management: . . . }

uses

SysUtils,

Classes,

12

Main in 'Main.pas';

{$R *.res} exports

Min,

Max;

begin end.

Сохраните модуль и проект, выбрав в меню команду Файл – Сохранить все. Когда вы скомпилируете проект с помощью команды меню Проект – Компилировать StrLib, в рабочем каталоге появится двоичный файл StrLib.dll.

Подключим библиотеку DLL к программе. Создадим новый проект и модуль. Поместите в модуль заголовки функций из StrLib.dll.

unit My_Unit; interface uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

function Min(x,y:Integer):Integer; stdcall; function Max(x,y:Integer):Integer; stdcall;

type

TMy_Form = class(TForm) Button1: TButton; GroupBox1: TGroupBox; Edit1: TEdit;

Edit2: TEdit; GroupBox2: TGroupBox; Label1: TLabel; Label2: TLabel; Edit3: TEdit;

Edit4: TEdit;

procedure Button1Click(Sender: TObject);

private { Private declarations } public { Public declarations } end;

var

My_Form: TMy_Form;

implementation

function Min(x,y:Integer):Integer; stdcall; external 'StrLib'; function Max(x,y:Integer):Integer; stdcall; external 'StrLib';

{$R *.dfm}

13

procedure TMy_Form.Button1Click(Sender: TObject); var a,b:integer;

begin a:=StrToInt(Edit1.Text); b:=StrToInt(Edit2.Text);

edit3.Text:=IntToStr(Min(a,b));

edit4.Text:=IntToStr(Max(a,b));

end;

end.

Переключитесь на исходный текст файла проекта, подключите в нем модуль ShareMem (он должен быть

первым!).

program My_Project;

uses ShareMem,

Forms,

My_Unit in 'My_Unit.pas' {My_Form};

{$R *.res}

begin

Application.Initialize; Application.CreateForm(TMy_Form, My_Form); Application.Run;

end.

Тестовый проект готов.

ЗАМЕЧАНИЯ ПО РАЗРАБОТКЕ DLL-БИБЛИОТЕК ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ И КОНСТАНТЫ

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

Несмотря на то что DLL может использоваться одновременно несколькими приложениями, для нее кажется, что она работает лишь с одним приложением. Иначе говоря, при каждом подключении DLL к какому-нибудь приложению Windows создает для DLL новое множество глобальных переменных и констант.

КОД ИНИЦИАЛИЗАЦИИ И ЗАВЕРШЕНИЯ

Каждая динамическая библиотека может иметь программный код инициализации и завершения. Код инициализации вызывается при подключении DLL к приложению или при вызове приложением функции LoadLibrary. Код завершения вызывается при завершении приложения, использующего DLL, или при вызове приложением функции FreeLibrary. Если вашей DLL необходима инициализация, то выполняйте ее в секции initialization главного модуля библиотеки. Если вашей DLL необходима деинициализация, то выполняйте ее в секции finalization главного модуля библиотеки. Если при инициализации DLL обнаруживается ошибка, препятствующая нормальной работе, установите переменную ExitCode модуля System в ненулевое значение. Это обеспечит автоматическую выгрузку DLL с уведомлением вызывающего приложения о неудачной загрузке.

ИСКЛЮЧИТЕЛЬНЫЕ СИТУАЦИИ И ОШИБКИ ВРЕМЕНИ ВЫПОЛНЕНИЯ

Если в DLL возникает исключительная ситуация и она никак в ней не обрабатывается, го исключительная ситуация распространяется на приложение, подключившее DLL. Ваше приложение, написанное на Delphi, может отло-

14

вить эту исключительную ситуацию самым обычным способом – с помощью операторов try ... except. Приложение, написанное на другом языке программирования, должно обрабатывать исключительную ситуацию библиотеки, написанной на Delphi, как исключительную ситуацию oпeрационной системы с кодом $0EEDFACE. Адрес возникновения исключительной ситуации содержится в первом элементе, а объект исключительной ситуации Delphi

— во втором элементе массива Exceptionlnformation, который является частью системой записи об исключительной ситуации.

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

ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ

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

Модуль ShareMem является интерфейсом к библиотеке Delphimm.dll, которая должна поставляться с приложениями и библиотеками, использующими модуль ShareMem. При использовании ShareMem стандартный менеджер памяти заменяется на менеджер памяти из библиотеки Delphimm.dll, делающий возможным выделение и освобождение памяти библиотекой и приложением совместно.

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