Это метод программирования, опирающийся на структурную организацию программы

Вид материалаДокументы

Содержание


Работа с графикой
Подобный материал:
1   2   3   4   5

5. Визуальное проектирование в среде Delphi.

Перед началом работы обычно надо настроить пути к библиотеке Delphi, а в Delphi 3.0 также установить пакеты. Это связано с тем, что Windows 95 (и, тем более, NT 4.0) имеет индивидуальные настройки для каждого пользователя. При установке Delphi все настройки выполняются автоматически, но только для Desktop того пользователя, который производил установку пакета. Помимо упомянутых настроек рекомендуется указать пути для директорий, в которых будут создаваться выходные и вспомогательные файлы.


5.8. Создание экранных форм. Инспектор объектов.

При входе в Delphi автоматически открывается проект "по умолчанию". Изначально это проект с пустой экранной формой, на которой пунктиром показана сетка, к которой идет привязка для выравнивания размеров и положений компонентов на форме. Для того, чтобы создать на форме новый компонент, надо "мышью" щелкнуть на соответствующем компоненте из палитры компонентов, а затем щелкнуть "мышью" на том месте экранной формы, где необходимо расположить компонент. Перетаскивание по форме компонента осуществляется либо "мышью" по технологии "хватай и тащи" (drag and drop), либо с помощью инспектора объектов путем задания значений свойств top и left (расстояний от верхнего и левого краев формы). Если во время проектирования вы случайно закрыли форму (нажали на "крестик" в правом верхнем углу формы), следует открыть project manager в опции view в меню Delphi. Менеджер покажет имена всех форм и модулей, имеющихся в проекте. Двойной щелчок на имени приведет к "всплыванию" наверх соответствующей формы или модуля.


5.9. Палитра компонентов.

Мы будем описывать только наиболее важные свойства и события для компонентов и перечислим только наиболее часто используемые компоненты из стандартных библиотек Delphi 3.0. Для Delphi 2.0 практически все далее сказанное также справедливо. Существенным отличием является более развитая справочная система в Delphi 3.0, позволяющая в режиме on-line получать практически всю необходимую информацию, что резко облегчает работу. Поэтому приводимые далее описания компонентов дают лишь общее знакомство с палитрой.


5.9.4. Метка (tLabel).

Этот компонент предназначен для показа в некотором месте формы статического или изменяющегося в процессе работы приложения текста. Свойство font позволяет выбирать тип, стиль, размер и цвет фонта, а height и width — задавать размеры отведенного под метку места. Свойство caption ("титр", "подпись под картинкой") позволяет вводить в инспекторе объектов (object inspector) или присваивать программно в процессе работы строку с текстом длинной до 255 символов. Свойство WordWrap дает возможность автопереноса текста на следующую строку при превышении шириной строки значения Width. Свойство AutoSize, напротив, вызывает автоматическое установление значения Width по длине строки.

Метка не может захватывать фокус, поэтому на нее невозможно установить курсор. В результате кусок текста на экране, созданный с помощью tLabel, невозможно выделить и скопировать в буфер обмена (clipboard), что бывает крайне неприятно для пользователей программ. Поэтому вместо tLabel рекомендуется использовать компонент tEdit в режиме запрета редактирования текста и с цветом, совпадающим с цветом формы.


5.9.5. Кнопка (tButton) и кнопка с картинкой (tBitButton).

Свойство caption задает надпись на кнопке, а событие onClick позволяет выполнить необходимые действия в случае нажатия кнопки. Компонент tBitButton (потомок tButton) дает возможность вывода на кнопке рядом с текстом картинки, имя картинки задается свойством Glyph ("наклейка"). Картинка должна быть в формате Bitmap. Существует возможность в одном .bmp-файле по очереди (слева направо) располагать до 6 картинок. Обычно используют 2 (для свойства enabled, установленного в true и false соответственно). При enabled=false текст на кнопке становится "пассивным" (более серым, чем обычно), и рисуется соответствующая картинка, которую также обычно изображают "более серой". Другие пары — для состояний кнопки "нажата/не нажата". Кнопку tBitButton можно использовать вместо tButton без всяких изменений, если не задавать имя картинки. Это дает дополнительные возможности: в tBitButton можно установить цвет текста на кнопке, а в tButton нельзя.


5.9.6. Однострочный пункт ввода-вывода и редактирования текста (tEdit).

Помимо своей обычной функции может служить полной заменой однострочной метке при соответствующих установках свойств (цвет фона как у компонента, на котором расположен пункт, enabled=false и т.д.). При enabled=true возможен захват фокуса пунктом. В этом случае при Readonly=true можно выделять и копировать в буфер выделенный текст, а при Readonly=false текст можно также редактировать. Свойство text позволяет возвращать или устанавливать строку текста для пункта. Свойство MaxLength позволяет устанавливать максимальную длину вводимого текста в пределах от 1 до 255 символов. Если MaxLength=0 (по умолчанию), это эквивалентно 255 (autosize). AutoSelect=true позволяет тексту в пункте автоматически становиться выделенным при входе в пункт.


5.9.7. Многострочный пункт ввода-вывода и редактирования текста (tMemo).

Аналогичен компоненту tEdit, но с произвольным количеством строк. Свойство Alignment задает режим выравнивания текста внутри компонента (по центру, левому или правому краю). Длина текста не должна превышать 64Kb. WordWarp задает режим переноса слов. ScrollBars позволяет показывать (или не показывать) внутри компонента линейки прокрутки по вертикали и горизонтали.

Вводимый/выводимый текст доступен либо как единый объект через свойство Text, либо через свойство Lines — как массив строк. Поскольку строки tMemo хранятся в объекте типа tStrings, к ним применимы все методы этого класса, например:

tMemo1.Lines.Add('эта строка добавляется в конец текста');


5.9.8. Выпадающий список (tComboBox).

Потомок tEdit. Выглядит так же, но имеет "стрелку вниз" справа от поля ввода-вывода текста. При нажатии на нее выпадает список строк, из которых можно выбрать нужную, которую обычно и показывают после этого в поле ввода-вывода. Доступ к выпадающим строкам идет через свойство Items, у которого, в свою очередь, есть свойство Strings. Доступ к видимому в "свернутом" состоянии тексту идет, как и в tEdit, через свойство Text. Добавление новой строки в список пунктов (Items) может производиться так:

ComboBox1.Items.Add('Добавленная строка');


5.9.9. Пункт независимого выбора (tCheckBox).

Это маленький квадратик с расположенной рядом меткой. У данного пункта может быть три состояния: Checked (отмечен), noChecked (не отмечен) и Grayed (затемнен, что обычно подразумевает, что он недоступен для изменения). В случае Checked в квадратике рисуется крестик или галочка, noChecked — квадратик пустой, Grayed — рисуется более темным фоном.


5.9.10. Пункты зависимого выбора — "радиокнопки" (tRadioButton).

Группа взаимоисключающих пунктов выбора (отмеченным может быть только один из группы). По виду отличаются от tCheckBox тем, что рисуются кружками. При выборе одного элемента группы в центре кружка рисуется точка, а предыдущий перестает быть отмеченным. Чтобы создать независимые группы радиокнопок, надо помещать соответствующую часть из них на другие группирующие элементы — tGroupBox или tPannel.


5.9.11. Таблица (сетка) строк (tStringGrid).

Обеспечивает вывод таблицы с текстом. Таблица состоит из ячеек (Cells). Доступ к ячейке идет через двумерное свойство Cells[i,j]. Количество столбцов в таблице задается свойством ColCount, а строк (рядов) — RowCount. Ширину и высоту ячеек в любом столбце или строке, соответственно, можно независимо задавать с помощью свойств DefaultColWidth и DefaultRowHeight.

Часть столбцов и строк в таблице могут быть "фиксированными". Число фиксированных строк задается свойством FixedRows, столбцов — свойством FixedCols. Фиксированные ячейки выполняют в таблице особую роль.
  • Во-первых, они являются заголовками столбцов и строк. Они имеют другой цвет фона (по умолчанию серый, а не белый, как обычные ячейки), не захватывают фокус (хотя такие столбец или строка как единое целое могут его захватить при заданных соответствующих опциях) и не допускают редактирования находящегося в них текста, даже если свойство doEditing для компонента установлено как true во множестве свойств Options.
  • Во-вторых, при установке в true свойства goRowsMoving и/или goColsMoving (также множество свойств Options) можно менять порядок строк и/или столбцов, "хватаясь" за них "мышью".

Приведем пример процедуры инициализации таблицы StringGrid, считая, что все необходимые атрибуты установлены на этапе визуального проектирования:

procedure SG_Init;

var i,j:Integer;

begin

StringGrid1.Cells[0,0]:='N'; {заголовок первого столбца - буква N}

for j:=1 to ColCount-1 do {заголовок других столбцов}

StringGrid1.Cells[0,j]:='Заголовок'+IntToStr(j);

for i:=1 to RowCount-1 do {первый столбец - номер строки}

StringGrid1.Cells[i,0]:=IntToStr(i);

end;

Эту процедуру можно вызвать, например, по событию onCreate для StringGrid1 (либо просто перенести приведенный текст внутрь обработчика события). В отличие от большинства других компонентов StringGrid, как правило, нуждается в программной инициализации подобного рода.


5.9.12. Диалоги (tOpenDialog,tSaveDialog è äðóãèå).

РАБОТА С ГРАФИКОЙ

Пример:

unit Unit1;

interface

uses

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

StdCtrls, ExtCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

Image1: TImage;

Edit1: TEdit;

Edit2: TEdit;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure FormCreate(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);

begin

close

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

Image1.Canvas.Pen.Color:=clGreen;

Image1.Canvas.Lineto(StrToInt(Edit1.Text),StrToInt(Edit2.Text))

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

Image1.Canvas.Pen.Color:=clRed;

Image1.Show;

end;

end.


6. Интерфейсы и множественное наследование

6.1. Общие представления об интерфейсах в Object PASCAL

Интерфейс объекта определяют методы и свойства, которые могут быть реализованы классом-наследником этого интерфейса. Они описываются аналогично абстрактным классам, так же, как абстрактные классы, но в отличие от них, не могут иметь экземпляров, не могут иметь реализации никаких своих методов (в Object PASCAL допускается реализация части методов в абстрактные классы). Реализация методов интерфейсов осуществляется в классе, поддерживающем (наследующем) данный интерфейс. Переменная типа интерфейс - это ссылка. Она дает возможность ссылаться на объект, чей класс реализует данный интерфейс. Однако с помощью такой переменной разрешается вызывать только методы, декларированные в данном интерфейсе, а не любые методы данного объекта. Интерфейсы являются альтернативой множественному наследованию, имеют практически все его достоинства и лишены его недостатков. Их использование существенно для написания ПО для распределенных систем на основе COM (the Component Object Model) и CORBA (Common Object Request Broker Architecture). Объекты, поддерживающие интерфейсы, могут взаимодействовать с COM- объектами, написанными на C++ или Java.

Интерфейсы, как и классы, могут быть описаны только в самой наружной области видимости программы или модуля, но не в процедуре или функции. Описание типа для интерфейса имеет вид:

type

interfaceName =

interface (ancestorInterface)

['{GUID}']

список полей

end;

Отличия от классов:
  • Не бывает экземпляров, реализующих тип интерфейс.
  • Список полей интерфейса может включать только свойства и методы. Поля данных использовать нельзя. Соответственно, спецификаторы read и write должны быть методами.
  • Все поля интерфейса всегда имеют тип видимости public (без явного указания); не разрешено использовать спецификаторов видимости (но у массивов может быть использован спецификатор default).
  • Не бывает конструкторов и деструкторов.
  • Методы не могут быть специфицированы как виртуальные, динамические, перекрытые, абстрактные. Поскольку они не имеют реализации в экземплярах типа, эти различия не имеют значения.
  • Наследование через интерфейсы множественное.
  • Реализация интерфейса может быть только в классе, при этом он должен реализовать все методы интерфейса (а значит, и все свойства)

Интерфейс, как и класс, наследует все методы прародителя, однако только на уровне абстракций, без реализации методов. Однако интерфейс наследует право реализации этих методов в классе, поддерживающем этот интерфейс. В декларации интерфейса можно указать, что интерфейс наследуется от прародительского интерфейса. Если такого указания нет, то интерфейс является непосредственным потомком IUnknown, который определен в модуле System и является прародителем для всех интерфейсов. В IUnknown продекларировано 3 метода — QueryInterface, _AddRef, Release. QueryInterface предназначен для поддержки интерфейсов объектов. _AddRef, _Release обеспечивают управление ссылками на интерфейсы. Простейший путь реализовать эти методы — создать класс-наследник от tInterfacedObject , описанного в модуле System.

Пример описания интерфейса:

type

IMalloc =

interface(IUnknown)

['{00000002-0000-0000-C000-000000000046}']

function Alloc(Size: Integer): Pointer; stdcall;

functionRealloc(P:Pointer;Size:Integer):Pointer;stdcall;

procedure Free(P: Pointer); stdcall;

function GetSize(P: Pointer): Integer; stdcall;

function DidAlloc(P: Pointer): Integer; stdcall;

procedure HeapMinimize; stdcall;

end;

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

type

className =

class (ancestorClass,interface1,...,interfaceN)

memberList

end;

Например,

type

TMemoryManager =

class(TInterfacedObject, IMalloc, IErrorInfo)

...

end;

Когда класс реализует интерфейс, он должен реализовать (или наследовать реализацию) каждого метода, декларированного в интерфейсе. Ниже приведено описание tInterfacedObject из модуля System:

type

TInterfacedObject =

class(TObject, IUnknown)

protected

FRefCount: Integer;

function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall;

function _AddRef: Integer; stdcall;

function _Release: Integer; stdcall;

public

property RefCount: Integer read FRefCount;

end;

tInterfacedObject реализует интерфейс. Поэтому в нем описаны и реализованы все три метода IUnknown. Классы, реализующие интерфейсы, могут быть использованы как прародители. Класс tInterfacedObject реализует три метода интерфейса IUnknown и поэтому удобен как прародитель для всех классов, реализующих интерфейсы. Когда интерфейс реализован в классе, каждый из его методов реализован соответствующим методом (по умолчанию с тем же именем) и с такой же сигнатурой.

В классе-наследнике можно перекрыть методы реализуемого интерфейса. При этом соответствующий метод должен быть виртуальным или динамическим). Также возможно заново унаследовать интерфейс:

type

IWindow =

interface

['{00000115-0000-0000-C000-000000000146}']

procedure Draw;

...

end;

TWindow =

class(TInterfacedObject, IWindow) //TWindow реализует IWindow

procedure Draw;

...

end;

TFrameWindow =

class(TWindow, IWindow) //TFrameWindow переопределяет реализацию Iwindow

procedure Draw;

...

end;

При этом в классе все методы от прародительской реализации этого интерфейса запираются (не наследуются), в том числе выражения различения методов.


6.2. Реализация интерфейсов свойством

Директива implements в описании свойства класса позволяет делегировать реализацию интерфейса свойству в реализуемом классе. Например, свойство

property MyInterface: IMyInterface

read FMyInterface

implements IMyInterface;

описывает свойство, реализующее интерфейс ImyInterface. Директива implements должна быть последней в описании свойства и может иметь перечисление после нее имен нескольких интерфейсов, разделенных запятыми. Делегируемое свойство:
  • может иметь тип класса или интерфейса.
  • не может быть массивом или иметь спецификатор индекса.
  • должно иметь спецификатор read; если имеется метод “read”, он не может быть динамическим (хотя может быть виртуальным) или описывать директиву message.
  • Если свойство типа интерфейс, соответствующий интерфейс или его интерфейс-прародитель должен присутствовать в списке интерфейсов класса, где продекларировано свойство.

Делегируемое свойство должно возвращать объект, чей класс полностью реализует интерфейс, без использования выражений различия методов (“мэппинга”), см. далее. Например:

type

IMyInterface =

interface

procedure P1;

procedure P2;

end;

TMyClass =

class(TObject, IMyInterface)

FMyInterface: IMyInterface;

property MyInterface: IMyInterface

read FMyInterface

implements IMyInterface;

end;

tatherClass=

class(tany,ImyInterface)



end;

var

aMyClass: TMyClass;

aOtherClass:tOtherClass;

aMyInterface: IMyInterface;

begin

aMyClass := TMyClass.Create;

aMyClass.FMyInterface := ... // объект, чей класс реализует ImyInterface

aMyInterfase:=aMyClass.myInterfase// ссылка на тот же объект

aMyInterface := MyClass;// допустимо, так как ImyInteface – интерфейс-прародитель для tMyClass; ссылка на объект,агрегирующий

aMyInterface.MyInterface:=totherClass.create;//это второй объект

aMyInterface.P1;

aMyInterfase.MyInterfase.P1;//это вызов методаP1 для второго объекта

end;


6.3. Различение имен при реализации нескольких интерфейсов

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

procedure interface.interfaceMethod = implementingMethod;

или

function interface.interfaceMethod = implementingMethod;

где implementingMethod — это метод, определенный в классе, или одном из его прародителей. Он может быть методом, описанном далее в описании класса, но не может быть “private” методом класса или одного из его прародителей, описанном в другом модуле. Пример:

type

TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo)

function IMalloc.Alloc = Allocate;

procedure IMalloc.Free = Deallocate;

...

end;

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

В спецификации интерфейса может быть описан уникальный глобальный идентификатор — globally unique identifier (GUID), представленный в виде строки литералов, заключенной в скобки:

['{xxxxxxxx–xxxx–xxxx–xxxx–xxxxxxxxxxxx}']

где каждый X это 16-ричная цифра ( от 0 до F). GUID — это 16-байтовое двоичное число, уникально идентифицирующее интерфейс.

Если у интерфейса есть GUID, можно его использовать для запросов к переменной интерфейса, получить ссылки на его реализации. Типы tGUID и pGUID, определенные в модуле System, используются для работы с переменными типа GUID и указателями на такие переменные. Их описание следующее:

type

pGUID = tGUID;

tGUID =

record

D1: Longword;

D2: Word;

D3: Word;

D4: array[0..7] of Byte;

end;

Можно описать типизированную константу типа TGUID, например, имеющую имя IID -IMalloc:

const IID_IMalloc: TGUID = '{00000002-0000-0000-C000-000000000046}';

Вызов в процедуре переменной типа GUID аналогичен соответствующему типу interfase. Может быть использовано как значение, так и постоянный параметр типа TGUID. Например:

function Supports(Unknown: IUnknown; const IID: TGUID): Boolean;

Таким образом, вызов Supports может быть сделан одним из двух путей:

if Supports(Allocator, IMalloc) then ...

или

if Supports(Allocator, IID_IMalloc) then ...


5.11. Спецификаторы вызовов процедур и функций

При задании процедуры или функции можно использовать спецификаторы вызова register, pascal, cdecl, stdcall, safecall. Например:

function MyFunction(X, Y: Real): Real; cdecl;

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


Директива

Передача параметров

Очищает стек

Передает параметры

Вызовы

Register

слева направо

программа

через регистры

по умолчанию, наиболее эфф.

Pascal

слева направо

программа

через стек

обратно совместимо с Turbo PASCAL

cdecl

справа налево

вызывающий

через стек

DLL,написанные на C или C++

stdcall

справа налево

программа

через стек

Windows API

safecall

справа налево

программа

через стек

Объекты COM; методы парных интерфейсов


5.12. Динамические массивы

Было:

var

A: array[1..100] of string;

B: array[1..10] of array [1..20] of integer;

Стало можно:

var

A: array of string;

B: array of array of integer;

Декларация динамического массива (переменных A и B) не выделяет под них памяти. Память выделяется процедурой SetLength:

SetLength(A,100);

SetLength(B,10,20);

Индексация динамических массивов всегда идет от нуля.

Для освобождения памяти из-под динамического массива:

а) либо присвоить nil переменной:

A:=nil;

B:=nil;

б) либо — вызвать процедуру Finalize:

Finalize(A);

Finalize(B);

Пример:

unit Unit1;

interface

uses

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

StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

Label1: TLabel;

Label2: TLabel;

Button3: TButton;

Label3: TLabel;

Label4: TLabel;

Label5: TLabel;

Button4: TButton;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure Button3Click(Sender: TObject);

procedure Button4Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

A: array of string;

B: array of array of integer;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);

begin

close

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

SetLength(A,100);

SetLength(B,10,20);

Label1.caption:='Память выделена';

end;

procedure TForm1.Button3Click(Sender: TObject);

var s:string;

begin

A[99]:='Ok';

B[9,19]:=7;

Label3.caption:=A[99];

str(B[9,19],s);

Label5.caption:=s;

end;

procedure TForm1.Button4Click(Sender: TObject);

begin

A:=nil;

Finalize(B);

Label1.caption:='Память освобождена';

end;

end.


5.13. Перезагрузка (overloading) методов, процедур и функций

Разрешено использование одинаковых имен, но с разными сигнатурами аргументов (числом или типом аргументов):

Constructor create(aOwner:tComponent);overload;override;

Constructor create(aOwner:tComponent;Text:String);overload;

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

Constructor create(aOwner:tComponent;Name:String);overload;

Пример для функций:

function Divide(x,y:real):Real;overload;

begin

Result:=x/y;

end;

function Divide(x,y:integer):Integer;overload;

begin

Result:=x div y;

end;


5.14. Параметры по умолчанию

Описываются аналогично константам в предыдущих версиях:

Name: Type=value;

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

Procedure FillArray(A:array of integer;V:integer=1);

Тогда вызов этой процедуры можно производить так:

FillArray(myArray,1);

Либо, что то же,

FillArray(myArray);

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


Технология Drag and Drop

Особенности разработки DLL

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

DLL не в состоянии поставлять в программу переменные, константы и типы. В результате DLL не могут экспортировать в программу объекты – для этого в Object PASCAL используются пакеты. DLL подключаются к программе в момент ее исполнения, т.е. динамически. Значительным преимуществом DLL является то, что если несколько программ используют одну и ту же DLL , в памяти будет лишь один экземпляр разделяемого программами кода. При необходимости DLL могут динамически загружаться и выгружаться из памяти программным путем.

Для создания DLL в Object Pascal введено зарезервированное слово Library, которым должен начинаться текст библиотеки. За словом Library следует ее идентификатор, но в отличие от модуля работа с DLL определяется именем DLL-файла, а не идентификатором, следующим за Library.

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

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

В разделе описаний DLL могут объявляться типы (в том числе и классы), константы и переменные, но они остаются скрытыми от вызывающей программы и могут использоваться только внутри DLL. В разделе описаний помимо стандартных для обычной программы объявлений используется специальный раздел объявления экспортируемых подпрограмм. Этот раздел начинается зарезервированным словом Exports, за которым через запятую перечисляются имена и индексы экспортируемых подпрограмм, описанных до того и помеченных директивой Export:

Library Lib;

Function Funct1 (x : real) : Real; Export;

begin



end;

Procedure Proc1 ; Export;

begin



end;

Exports

Funct1 index 1, Proc1 index2;

begin

end.

Индекс присваивается процедуре автоматически по порядку ее появления в списках Exports: первая получает индекс 0, следующая 1 и т.д. Можно явно указать индекс добавив за ее именем в списке Exports слово index и целое число без знака в диапазоне от 1 до 32767. Можно определить внешнее имя экспортируемой процедуры отличное от ее идентификатора. Для этого в списке Exports добавляется слово name и внешнее имя в кавычках:

Exports

Funct1 index 1 name ‘myFunct’;

Внешние имена критичны к регистру букв.

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

Exports

Funct1 index 1 name ‘MyFunct’ resident;

Т.к. в этом случае информация о процедуре сохраняется в памяти во все время использования библиотеки.

Для использования процедур из DLL необходимо описать их как внешние, добавив за словом External имя библиотеки и если надо индекс или имя :

Procedure Proc1 ; External ‘MyDLL1’;

Function Func1 (x: real) : real; External ‘MyDLL1’ index1;

Function MyFunct (x:real): real ; External ‘MyDLL1’ name ‘Funct1’;

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

Программа может загружать и высвобождать DLL (динамически) с помощью функций: LoadLibrary, GetProcAddress и FreeLibrary.

begin //Процедура с динамической загрузкой DLL

var : DLLHandler:LongInt;

Handler := LoadLibrary(‘MyDLL1.dll’);

if DLLHandler = 0 then

begin

ShowMessage (‘DLL не найдена’);

Halt(1);

end;

{ определяем адреса процедур функций от первой до третьей по индексу,четвертой– по имени. При вызове по индексу младшее слово PChar содержит индекс, делаем приведение типов: }

@ Funct1 := GetProcAddress (Handler, Pchar (LongInt(1))) ; //Настройка дескриптора DLL

@ SubC := GetProcAddress (Handler, Pchar (LongInt(2))) ;

@ MulC := GetProcAddress (Handler, Pchar (LongInt(3))) ;

@ Proc1 := GetProcAddress (Handler,’Proc1’) ;

FreeLibrary (DLLHandler) // Освобождаем библиотеку

end;


5.17. Технологии DDE и OLE

5.18. Технология ActiveX

Подпроцессы (Threads)

Для создания дополнительного подпроцесса в программах Delphy предназначен специальный модуль (в репозитории он представлен пиктограммой Thread Object). При выборе этого модуля Delphi запрашивает имя класса, который будет дочерним для основополагающего класса TThread. Класс TThread содержит абстрактный метод Execute, который должен исполняться в подпроцессе и обязан перекрываться в потомке.

Методы и свойства объектов из библиотеки визуальных компонентов могут использоваться только в рамках вызова метода Synchronize, например:

Procedure MyThread.Execute;

begin



Synchronize (UpdateCaption );

end;

где метод UpdateCaption может быть, например, таким:

Procedure MyThread.UpdateCaption;

begin

Form1.Caption := ‘Новый текст метки’;

end;

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

Пример перекрытия абстрактного метода Execute в потомке tMyThread:

Type tMyThread=

class(tThread)

private

myField1:…;

myField2:…;

protected

procedure Execute;override;



procedure MyVisualMethod;virtual;

public

constructor create(параметры); (если нужны)

end;

var ThreadsRunning:integer;

procedure tMyThread.execute;

begin



Synchronized MyVisualMethod;



end;

procedure tMyThread,create(параметр);

begin

inberited create(false);

FreeOnTerminate:=true;

end;

procedure tMyThread.myVisualMethod;

begin

Form1.label2.caption:=…;

If Terminated then Exit;

end;

procedure Form1.StartButtonClick(Sender:tObject);

begin

aThread1:=tMyThread.create(…);

Inc(ThreadsRunning);

aThread1.onTerminate:=ThreadDone;

StartButton.enabled:=false;

aTread2:=tMyThread.reate(…);

Inc(ThreadsRunning);

aThread2.onTerminate:=ThreadDone;

….

end;

procedure Form1.ThreadDone(Sender:tObject);

begin

Dec(ThreadsRunning);

Label1.caption:=IntToStr(ThreadsRunning);

if ThreadsRunning=0 then

StartButton.enabled:=true

end;

procedure Form1.Stop1ButtonClick(Sender:tObject);

begin

aThread1.Terminate

end;




5.20. Использование библиотек OpenGL и GLU

5.21. Создание программистом собственного компонента

Литература


  1. В.В.Фаронов . Delphi 6. Учебный курс.- М.:"Нолидж", 2001.- 608 с.
  2. П.Г.Дарахвелидзе, Е.П.Марков.Delphi 4. Среда визуального программирования.-СПб.:БХВ – Санкт-Петербург, 1999. – 816 с.
  3. А.М.Епанешников, В.А.Епанешников. Программирование в среде Delphi: Учебное пособие в 4-х частях. М.: ДИАЛОГ-МИФИ, 1997-1998.
  4. М.В.Краснов. OpenGL.Графика в проектах Delphi. -СПб.:БНВ-Санкт-Петербург, 2000.-352 с.
  5. Тэйлор Д. и др. Переход на Kylix для Delphi-программистов./Пер.с англ. СПб: "Питер" – 2002.- 304 с.
  6. Калверт Ч. и др. Borland Kylix. Руководство разработчика./Пер.с англ.: "Вильямс" - 2002, 880 с.
  7. Г.Буч. Объектно-ориентированный анализ и проектирование с примерами приложений на C++, 2-е изд./Пер.с англ.-М., Бином,СПб, 2000.-560 с.
  8. Д.Чеппел. Технологии ActiveX и OLE./Пер.с англ.-М.:"Русская Редакция”,1997.-320 с.
  9. А.Л.Фридман. Основы объектно-ориентированного программирования на языке Си++.-М.:Горячая линия-Телеком, Радио и связь, 1999.-208 с.