Главная » Delphi » Экспорт объектов из библиотек DLL

0

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

Ниже приведен список условий и ограничений, накладываемых на экспорт объек

та из DLL:

•    Вызывающее приложение может использовать лишь те методы объекта, кото

рые были объявлены как виртуальные.

•   Экземпляры объектов должны  создаваться только  внутри библиотеки DLL.

•  Экспортируемый объект должен  быть определен как в библиотеке DLL, так и в вызывающем приложении, причем объявление методов должно  выполняться в одном и том же порядке.

•    Нельзя создать объект класса, производного от содержащегося внутри библио

теки DLL.

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

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

Листинг 6.15. Объект, предназначенный для экспорта из DLL

type

TConvertType = (ctUpper, ctLower); TStringConvert = class(TObject)

{$IFDEF STRINGCONVERTLIB}

privateFPrepend: String; FAppend : String;

{$ENDIF}

public

function ConvertString(AConvertType: TConvertType;

AString: String): String;

virtual; stdcall; {$IFNDEF STRINGCONVERTLIB} abstract; {$ENDIF}

{$IFDEF STRINGCONVERTLIB}

constructor Create(APrepend, AAppend: String);

destructor Destroy; override;

{$ENDIF}

end;

{ У любого приложения, использующего этот класс, идентификатор STRINGCONVERTLIB не определен (поскольку у них есть свой идентификатор), а значит, для них определение этого класса будет выглядеть следующим образом:

TStringConvert = class(TObject)

public

function ConvertString(AConvertType: TConvertType;

AString: String): String; virtual; stdcall; abstract;

end;

}В  листинге 6.15  фактически  содержится  исходный  код  подключаемого  файла StrConvert.inc. Размещение определения класса  этого  объекта в подключаемом файле  вызвано необходимостью соблюдения третьего  пункта  приведенного  выше списка  требований, согласно которому класс объекта должен  быть одинаково опреде лен как в библиотеке DLL, так и в вызывающем приложении. Когда вызывающее при ложение и библиотека DLL получают  определение класса  из  одного  и того  же  под ключаемого файла, совпадение определения гарантировано. Если в определение объ екта необходимо внести изменения, то достаточно, отредактировав один  подключае мый файл, повторно скомпилировать оба проекта, а не вводить эти  изменения дваж ды — сначала  в вызывающее приложение, а затем  в библиотеку DLL, что  может  при вести к возникновению ошибок.

Рассмотрим следующее определение метода ConvertSring():

function ConvertString(AConvertType: TConvertType;

AString: String): String; virtual; stdcall;

Причина объявления этого  метода  виртуальным (с помощью ключевого слова virtual) заключается не в необходимости создавать производные классы,  в которых можно было бы впоследствии переопределять метод ConvertString(). Он объявлен виртуальным, чтобы  создать  точку входа в таблицу виртуальных методов (VMT — Virtual Method Table). Не вдаваясь  пока  в подробности, скажем,  что  таблица VMT представ ляет собой  участок памяти, содержащий указатели  на виртуальные методы  объекта. С помощью таблицы VMT вызывающее приложение может  получить указатель  на опре деленный метод конкретного объекта. Для методов, которые не объявлены виртуаль ными, таблица VMT записей не содержит, а вызывающее приложение не сможет по лучить на них указатель.  Как видите, все это делается для получения вызывающим приложением указателя  на функцию. Но поскольку  этот  указатель  зависит от типа ме тода,  определяемого в объекте, Delphi автоматически, неявным образом, обработает любые ссылки на адреса, передавая методу дополнительный параметр self.

Обратите внимание на идентификатор условного определения STRINGCONVERTLIB.

При  экспорте объекта переопределение в вызывающем приложении необходимо лишь

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

Листинг 6.16 демонстрирует реализацию объекта TStringConvert.

Листинг 6.16. Реализация объекта TStringConvert

unit StringConvertImp;

{$DEFINE STRINGCONVERTLIB}

interface

uses SysUtils;

{$I StrConvert.inc}

function InitStrConvert(APrepend,

AAppend: String): TStringConvert; stdcall;

implementation

constructor TStringConvert.Create(APrepend, AAppend: String);

begin

inherited Create;

FPrepend := APrepend;

FAppend       := AAppend;

end;

destructor TStringConvert.Destroy;

begin

inherited Destroy;

end;

function TStringConvert.ConvertString(AConvertType: TConvertType; AString: String): String;

begin

case AConvertType of

ctUpper: Result := Format(‘%s%s%s’,

[FPrepend, UpperCase(AString), FAppend]);

ctLower: Result := Format(‘%s%s%s’,

[FPrepend, LowerCase(AString), FAppend]);

end;

end;

function InitStrConvert(APrepend,

begin

AAppend: String): TStringConvert;

Result := TStringConvert.Create(APrepend, AAppend);

end;

end.

Согласно предъявляемым к экспортируемым объектам требованиям, такой  объект должен  создаваться в библиотеке DLL. Это  условие  реализуется в стандартной  экс портируемой функции InitStrConvert(), которой передаются два параметра, предназначенных для конструктора. Мы добавили это  для иллюстрации способа пе редачи информации конструктору объекта через функцию интерфейса.

Обратите внимание: в этом  модуле объявляется условная  директива STRINGCON- VERTLIB. Остальная часть  модуля  не  нуждается   в  дополнительных  разъяснениях. Файл проекта библиотеки DLL представлен в листинге 6.17.

Листинг 6.17. Файл проекта библиотеки StringConvertLib.dll

library StringConvertLib;

uses

ShareMem, SysUtils, Classes,

StringConvertImp in ‘StringConvertImp.pas';

exports

InitStrConvert;

end.

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

В листинге 6.18 показан пример использования экспортируемого объекта для пре образования произвольной строки в строку из прописных или строчных букв. Проект этого  примера называется StrConvertTest.dpr. Его можно  найти на прилагаемом компакт диске (см. также www.williamspublishing.com).

Листинг 6.18. Проект, использующий объект преобразования строк

unit MainFrm;

interface uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

{$I strconvert.inc}

type

TMainForm = class(TForm)

btnUpper: TButton;edtConvertStr: TEdit;

btnLower: TButton;

procedure btnUpperClick(Sender: TObject);

procedure btnLowerClick(Sender: TObject);

private

public

end;

var

MainForm: TMainForm;

function InitStrConvert(APrepend,

AAppend: String): TStringConvert; stdcall;

external ‘STRINGCONVERTLIB.DLL';

implementation

{$R *.DFM}

procedure TMainForm.btnUpperClick(Sender: TObject);

var

ConvStr: String;

FStrConvert: TStringConvert;

begin

FStrConvert := InitStrConvert(‘Upper ‘, ‘ end’);

try

ConvStr := edtConvertStr.Text;

if ConvStr <> EmptyStr then

edtConvertStr.Text := FStrConvert.ConvertString(ctUpper,

ConvStr);

finally

FStrConvert.Free;

end;

end;

procedure TMainForm.btnLowerClick(Sender: TObject);

var

ConvStr: String;

FStrConvert: TStringConvert;

begin

FStrConvert := InitStrConvert(‘Lower ‘, ‘ end’);

try

ConvStr := edtConvertStr.Text;

if ConvStr <> EmptyStr then

edtConvertStr.Text := FStrConvert.ConvertString(ctLower,

ConvStr);

finally

FStrConvert.Free;

end;

end;

end.

Резюме

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

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

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии