Отличия Turbo (Borland) Pascal и Delphi

Компиляция программ на Delphi из командной строки

В Delphi основной модуль программы имеет расширение .dpr (сокращение от Delphi Project). Создадим в любом текстовом редакторе файл hw.dpr и напишем в нём следующий текст программы:

{$APPTYPE CONSOLE}

begin
  writeln('Hello, world!');
end.

{$APPTYPE CONSOLE} в заголовке файла означает что программа будет консольной (а не оконным приложением Windows).

Чтобы откомпилировать программу, в командной строке наберите: dcc32 hw.dpr, компиоятор создаст исполняемый файл hw.exe или выдаст сообщения об ошибках если они есть.

$dcc32 hw.dpr
Embarcadero Delphi for Win32 compiler version 22.0
Copyright (c) 1983,2010 Embarcadero Technologies, Inc.
hw.dpr(5)
6 lines, 0.02 seconds, 15516 bytes code, 13108 bytes data.

$hw.exe
Hello, world!

 

Типы данных в Delphi

При помощи следующей несложной программы можно изучить целочисленные и действительные типы данных в Delphi (сохранять текст надо в кодировке cp866!).

{$APPTYPE CONSOLE}

uses Math;
begin
  writeln('Тип данных   Размер в байтах   Нижняя граница..Верхняя граница');
  writeln('Целые типы:');
  writeln('  Byte: ',SizeOf(Byte),'  ',Low(Byte),'..',High(Byte));
  writeln('  ShortInt: ',SizeOf(ShortInt),'  ',Low(ShortInt),'..',High(ShortInt));
  writeln('  Word: ',SizeOf(Word),'  ',Low(Word),'..',High(Word));
  writeln('  SmallInt: ',SizeOf(SmallInt),'  ',Low(SmallInt),'..',High(SmallInt));
  writeln('  LongWord: ',SizeOf(LongWord),'  ',Low(LongWord),'..',High(LongWord));
  writeln('  Cardinal: ',SizeOf(Cardinal),'  ',Low(Cardinal),'..',High(Cardinal));
  writeln('  Integer: ',SizeOf(Integer),'  ',Low(Integer),'..',High(Integer),'=',MaxInt);
  writeln('  Longint: ',SizeOf(Longint),'  ',Low(Longint),'..',High(Longint));
  writeln('  Int64: ',SizeOf(Int64),'  ',Low(Int64),'..',High(Int64));
  writeln('Comp выводится как вещественный, хотя хранить может только целые числа');
  writeln('  Comp: ',SizeOf(Comp),'  ',MinComp,'..',MaxComp);
  writeln('Вещественные типы');
  writeln('  Single: ',SizeOf(Single),'  ',MinSingle,'..',MaxSingle);
  writeln('  Double: ',SizeOf(Double),'  ',MinDouble,'..',MaxDouble);
  writeln('  Extended: ',SizeOf(Extended),'  ',MinExtended,'..',MaxExtended);
end.

Вывод программы:

Тип данных   Размер в байтах   Нижняя граница..Верхняя граница
Целые типы:
  Byte: 1  0..255
  ShortInt: 1  -128..127
  Word: 2  0..65535
  SmallInt: 2  -32768..32767
  LongWord: 4  0..4294967295
  Cardinal: 4  0..4294967295
  Integer: 4  -2147483648..2147483647=2147483647
  Longint: 4  -2147483648..2147483647
  Int64: 8  -9223372036854775808..9223372036854775807
Comp выводится как вещественный, хотя хранить может только целые числа
  Comp: 8  -9.22337203685478E+0018.. 9.22337203685478E+0018
Вещественные типы
  Single: 4   1.50000000000000E-0045.. 3.40000000000000E+0038
  Double: 8   5.00000000000000E-0324.. 1.70000000000000E+0308
  Extended: 10   3.40000000000000E-4932.. 1.10000000000000E+4932

Форматированный вывод

С помощью функции Format (из модуля SysUtils) можно форматировать простые типы данных в строке (как функция printf в C). Первый параметр: строка Formatting определяет, как массив Data (второй параметр) будет отформатирован. 

Форматируемая cтрока может включать обычные символы (которые передаются неизменными в строку результата), и символы форматирования данных, которые начинаются с % и заканчиваются индикатором типа данных: 

Общий формат форматирования каждой подстроки следующий: 

%[Index:][-][Width][.Precision]Type 

где квадратные скобки относятся к дополнительным параметрам, и :. - символы - литералы, первые 2 из которых используются, чтобы идентифицировать два из дополнительных параметров. 

{$APPTYPE CONSOLE}
uses SysUtils;

var text : string;
begin
  Writeln(Format('%s', ['Hello'])); { Вывод: Hello }

  { Соединение символьного текста и элемента данных }
  Writeln(Format('String = "%s"', ['Hello'])); { String = "Hello" }

  { Примеры каждого из типов данных }
  Writeln(Format('Decimal = %d', [-123]));  { Decimal = -123 }
  Writeln(Format('Exponent = %e', [12345.678])); { Exponent = 1,23456780000000E+004 }
  Writeln(Format('Fixed = %f', [12345.678])); { Fixed = 12345,68 }
  Writeln(Format('General = %g', [12345.678])); { General = 12345,678 }
  Writeln(Format('Number = %n', [12345.678])); { Number = 12 345,68 }
  Writeln(Format('Money = %m', [12345.678])); { Money = 12 345,68р. }
  Writeln(Format('Pointer = %p', [addr(text)])); { Pointer = 00417E78 }
  Writeln(Format('String = %s', ['Hello'])); { String = Hello }
  Writeln(Format('Unsigned decimal = %u', [123])); { Unsigned decimal = 123 }
  Writeln(Format('Hexadecimal = %x', [140])); { Hexadecimal = 8C }  
  DecimalSeparator := '|'; { Показ с новым символом десятичной точки }  
  ThousandSeparator := '_'; { Показ с новым разделителем тысяч }
  Writeln(Format('Number = %n', [12345.678])); { Number = 12_345|68 }

  { Используя выравнивание в колонке (ширина колонки) и значения точности }
  { Символы '<>' используются, чтобы выделить форматирование }
  Writeln(Format('Padded decimal = <%7d>', [1234])); { Padded decimal = <   1234> }
  { С отрицательной шириной выравнивание влево }
  Writeln(Format('Justified decimal = <%-7d>', [1234])); { Justified decimal = <1234   > }

  { Если указать '.' то число будет дополнено '0' до заданной длины }
  Writeln(Format('0 padded decimal  = <%.6d>', [1234])); { 0 padded decimal  = <001234> }

  { Комбинация ширины и дополнения. Сначала ширина, потом дополнение }
  Writeln(Format('Width + precision = <%8.6d>', [1234])); { Width + precision = <  001234> }

  { Индекс позволяет менять порядок вывода (например, несколько раз выводить одну строку) }
  Writeln(Format('Reposition after 3 strings = %s %s %s %1:s %s',['Zero', 'One', 'Two', 'Three']));
  { Reposition after 3 strings = Zero One Two One Two }

  { Обратите внимание, испытание показало, что * для параметра ширины может привести к ECONVERTERROR. }
  Writeln(Format('In line = <%10.4d>', [1234]));  { In line = <      1234> }
  Writeln(Format('Part data driven  = <%*.4d>', [10, 1234])); { Part data driven = <      1234> }
  Writeln(Format('Data driven = <%*.*d>', [10, 4, 1234]));  { Data driven = <      1234> }
end.

Отличия Object и Class

type
  TClass = class
    Proc1 : Integer; { Объявление поля }
  end;

var VClass : TClass;
begin
  VClass := TClass.Create({параметры (может не быть)});
  VClass.Proc1 := 1; { обращение к полю без «^» }
  VClass.Free; { Очищаем память после всех обращений к полям }
end.
  { Пример демонстрирует отличия Object и Class }
  {$APPTYPE CONSOLE}  { Консольное приложение Delphi } 

  uses SysUtils;

  type
    Number = integer; { Задаём тип для всех чисел в программе }
    { Один раз определив тип Number будем дальше его использовать во всей программе }
    { Объявление класса }
    MyClass = class { Неявно унаследован от (TObject) }
      x,y : Number;
      constructor Create( ix : Number ); { Конструктор Create }
      destructor Free; { Деструктор Free (в Pascal он назывался Destroy) }
      function MyMethod : Number;
    end;
    { Объявление типа данных object }
    MyObject = object { Расширенный record }
      x,y : Number;
      function MyMethod : Number;
    end;

  constructor MyClass.Create; { Конструктор }
  begin
    x := ix;
    y := 0;
  end;

  destructor MyClass.Free; { Деструктор }
  begin
    writeln('Free');
  end;

  { Методы объявляются одинаково! }
  function MyClass.MyMethod : Number;
  begin
    Result := x*y;
  end;

  function MyObject.MyMethod : Number;
  begin
    Result := x*y;
  end;

  { Эмуляция присваивания классов на объектах }
  procedure SimulateClassesByPointers;
  var Ap,Bp : ^MyObject; { 2 указателя на объект }
  begin
    new(Ap); new(Bp); { Завели в динамической памяти }
    Bp := Ap; { Теперь Bp указывает на то же, на что и Ap }
    Ap.x := 20; { Изменили поле в Ap, изменилось поле и в Bp }
    writeln('A = ',Ap.x,' B = ',Bp.x); { A = 20 B = 20 }
  end;

  var
    Ac,Bc : MyClass; { 2 переменные - экземпляры класса }
    Ao,Bo : MyObject; { 2 объекта }
  begin
    Ac := MyClass.Create(1);
    Bc := Ac;
    Ac.x := 3;
    writeln('A = ',Ac.x,' B = ',Bc.x); { A = 3 B = 3 }
    Bc.x := 9;
    writeln('A = ',Ac.x,' B = ',Bc.x); { A = 9 B = 9 }
    Bo := Ao;
    Ao.x := 3;
    writeln('A = ',Ao.x,' B = ',Bo.x); { A = 3 B = 0 }
    Bo.x := 9;
    writeln('A = ',Ao.x,' B = ',Bo.x); { A = 3 B = 9 }
    SimulateClassesByPointers;
    Ac.Free;
    readln; // Ожидаем нажатия Enter - пауза в конце программы
  end.

Использование свойств (property), Public, Private

Поля property – свойства
Описание:
Property идентификатор : тип данных read имя метода или поля write имя метода или поля
Если читаем – read, если пишем write

{$APPTYPE CONSOLE}

uses SysUtils;

type { Класс "Квадрат" }
  Square = class
  private { Доступны только внутри класса Square }
    function GetArea : real;
    procedure SetArea( V : real );
  public { Доступны извне класса }
    { Сторона квадрата }
    Side : Real;
    { Площадь квадрата }
    property Area : real Read GetArea Write SetArea;
    { property ИМЯ_СВОЙСТА : ТИП }
    {   Read ФУНКЦИЯ_ИЛИ_ПЕРЕМЕННАЯ_ДЛЯ_ЧТЕНИЯ }
    {   Write ПРОЦЕДУРА_ИЛИ_ПЕРЕМЕННАЯ_ДЛЯ_ИЗМЕНЕНИЯ }
    property Side2 : real Read Side Write Side;
  end;

procedure Square.SetArea( V : real );
begin
  Side := sqrt(V);
end;

function Square.GetArea : real;
begin
  Result := Side * Side;
end;

var A : Square; { A - экземпляр класса "Квадрат" }
begin
  A := Square.Create;
  A.Side := 2;
  writeln(A.Side:0:1,' ',A.Area:0:1);
  A.Area := 9;
  writeln(A.Side:0:1,' ',A.Area:0:1);
  readln;
end.

Пример наследования (правила наследования)

Правила наследования
Public, Private, Protected
По-умолчанию Public – открыт доступ для всех.
Private – видит только class и потомки, другие классы, функции (вся остальная программа) не увидят

  {$APPTYPE CONSOLE}

  uses SysUtils;

  type
    { Parent - Надкласс (SuperClass) по отношению к Sun }
    Parent = class
      X : integer;
      { Виртуальный деструктор }
      destructor Free; virtual;
    end;
    { Sun - Подкласс (SubClass) по отношению к Parent }
    Sun = class(Parent)
      Y : integer;
      { overload - перезагрузка метода }
      { Описывает новый метод (с новыми параметрами) => используем overload }
      constructor Create; overload;
      constructor Create( X,Y : integer ); overload;
      { Перекрытие деструктора }
      destructor Free; override;
    end;
    { У Sun будут свойства и X и Y }

  constructor Sun.Create;
  begin
    writeln('constructor #1');
  end;

  constructor Sun.Create( X,Y : integer );
  begin
    writeln('constructor #2');
    { Для различения переменных и полей используется Self }
    Self.X := X;
    Self.Y := Y;
  end;

  destructor Parent.Free;
  begin
    writeln('Parent.Free');
  end;

  destructor Sun.Free;
  begin
    writeln('Sun.Free');
  end;

  var
    S : Sun;
    A : array [1..3] of Parent;
  begin
    S := Sun.Create; { Создаём экземпляр класса Sun }
    S.X := 1;  S.Y := 2; { У него есть оба поля, и X и Y }
    { В одном массиве могут быть экземпляры разных классов но с ОДНИМ общим предком }
    A[1] := Parent.Create;  A[2] := Parent.Create;  A[3] := Sun.Create;
  end.

Виртуальные методы, перекрытия. Override, Overload

Virtual, Dynamic - означают что методы могут перекрываться. Dynamiс – таблица хранится для класса, а в объекте только ссылка на класс, экономит память. Virtual - экономит время.

  {$APPTYPE CONSOLE}

  uses SysUtils;

  type
    Shape = class
      procedure Draw; virtual; // Только виртуальный метод может быть перекрыт (Override)
    end;
    Square = class(Shape)
      procedure Draw; override; // Перекрытие
    end;
    Circle = class(Shape)
      procedure Draw; override; // Перекрытие
    end;
    Text3D = class(Shape)
      procedure Draw; override; // Перекрытие
    end;

  procedure Shape.Draw;
  begin
    writeln('Shape.Draw');
  end;

  procedure Square.Draw;
  begin
    writeln('Square.Draw');
  end;

  procedure Circle.Draw;
  begin
    writeln('Circle.Draw');
  end;

  procedure Text3D.Draw;
  begin
    writeln('Text3D.Draw');
  end;

  var
    A : array [1..3] of Shape; // Массив фигур
    i : integer; // Переменная цикла
  begin
    // Создаём 3 объекта разных типов, но помещаем их в один массив
    A[1] := Square.Create;   A[2] := Circle.Create;   A[3] := Text3D.Create;
    // Пробегаем в цикле все элементы массива и вызываем для каждого метод Draw
    for i := low(A) to high(A) do
      A[i].Draw;
      { Будет выведено: Square.Draw  Circle.Draw  Text3D.Draw, т.е.
        для каждого элемента массива был вызыван свой Draw в соответствии с его типом }
    Readln; { Пауза в конце программы, ожидание нажания Enter }
  end.
  type
    A = class
      procedure Method; virtual; abstract; { Абстрактный метод - при вызове - ошибка! }
    end;
    B = class(A) { B - наследник A }
      procedure Method; override; { Перекрываем абстрактный A.Method }
    end;
    C = class(B) { C - наследник В }
      procedure Method; reintroduce; { Перевведение (противоположно override) }
        { Из-за reintroduce C.Method не связан с Method в предках! }
    end;

  procedure B.Method;
  begin
    Writeln('B.Method');
  end;

  procedure C.Method;
  begin
    Writeln('C.Method');
  end;

  var V : A;
  begin
    V := B.Create; V.Method; { Нормально выводится: B.Method }
    V := C.Create; V.Method; { Выводится: B.Method, хотя в C тоже есть Method }
    V := A.Create; { В Delphi экземпляр класса с абстрактным методом создать можно }
    { Попытаемся вызвать абстрактный метод! }
    V.Method; { ОШИБКА: Exception class EAbstractError with message 'Abstract Error' }
  end.

Визуальные компоненты и свойства

  procedure TForm1.FormCreate(Sender: TObject);
  begin
    { Исключение, у Font свойство Name это не имя компонента, а название шрифта! }
    Font.Name := 'Times New Roman'; { Вся форма шрифтом Times New Roman }
    { Свойство Style: Разрешить жирный шрифт Bold }
    Font.Style := [fsBold]; { пишется в [] - т.к. множество (set) }
    Font.Style := [fsBold,fsItalic]; { Жирный наклонный шрифт }
    Font.Style := [fsUnderline]; { Подчёркнутый шрифт }
    Font.Style := []; { Обычный шрифт }
  end;

Пример обработки изображений

Связь между размерами компонентов и размерами формы

Свойство Align – прижимает объект к краям внутри родительского компонента (формы или панели), представим себе, что у нас есть надпись (TLabel) Label1

  procedure TForm1.FormCreate(Sender: TObject);
  begin
    Label1.Align := alTop; { Метка заполняет верхнюю часть (прижата к верху формы) }
    Label1.Align := alBottom; { нижнюю часть }
    Label1.Align := alLeft; { левую часть }
    Label1.Align := alRight; { правую часть }
    Label1.Align := alClient; { всё свободное пространство }
    Label1.Align := alNone; { не прижимать, объект размещается в координатах как вы его и поставили в редакторе }
  end;

Свойство Anchor (якорь) – привязка по расстоянию до краёв

  procedure TForm7.FormCreate(Sender: TObject);
  begin
    Label1.Anchors := [akLeft,akTop]; { Сохранять расстояние до края сверху и слева }
    Label1.Anchors := [akRight,akBottom]; { Сохранять расстояние до правого края и низа }
    Label1.Anchors := [akLeft,akTop,akRight]; { Растягиваться вместе с формой по ширине }
    Label1.Anchors := [akLeft,akTop,akBottom]; { Растягиваться вместе с формой по высоте }
    { Растягиваться вместе с формой по обоим направлениям (всё в True) }
    { Обычно используется только для одного центрального компонента формы! }
    Label1.Anchors := [akLeft,akTop,akRight,akBottom];
  end;

Изменения и дополнения в стандартных типах данных

Массивы: SetLength, Finalize

Динамические массивы в Delphi можно описывать без размера: Имя_переменной : array of тип_данных;

Может быть динамический массив из динамических массивов: Имя_переменной : array of array of тип_данных;

Имя – это указатель.

После объявления массива в var инициализируем (задавая длину):

SetLength(Имя_переменной, размер);

var ArByte : array of byte; { ArByte – это указатель }
begin
  SetLength(ArByte, 5); { Для данного массива индексы от 0 до 4 }
  ArByte[0] := 23;
  { Почему: SetLenght вызывать два раза нельзя, т.е. менять размер массива в процессе работы нельзя?? }
  SetLength(ArByte, 3); { Изменяем размер массива! }
  Writeln(ArByte[0]); { Печатает 23 }
  ArByte := nil; { Избавляемся от массива }
  Finalize(ArByte); { Или так }
end.
{ Двумерные динамические массивы }
var Arr2byte: array of array of byte;
begin
  SetLength(Arr2byte,2); { Задаём количество строк!!! }
  SetLength(Arr2byte[0], 5); { 1–ая строка содержит 5 эл. }
  SetLength(Arr2byte[1], 4); { 2–ая строка содержит 4 эл. }
  { Освобождаем в обратном порядке: сначала строки, а потом весь массив }
  Finalize(Arr2byte[0]);
  Finalize(Arr2byte[1]);
  Finalize(Arr2byte);
end.

Строки в Delphi

String => ShortString
String[ N ] => String [ N ]

В Delphi String – это array of char, т.е. нет ограничений на её длину как в Паскале (в Pascal максимальная длина строки - 255 символов).

«0» - ая ячейка ничего не хранит
WideString  - это array of widechar
  Занимает 2 байта (char – 1 байт)
PСhar – при обращении к f- м ОС (API)
Должен быть длиннее Pchar,
В нашем «0» - конец строки
Также и  в string – последний символ «0»

Преобразование

{ В Delphi - PChar = ^array of char; }
{ Char - Unicode с 2009-ой версии Delphi, до этого ANSI }

procedure CopyPChar( var dest : array of char; source : PChar; Count : Integer );
var i : integer;
begin
  for i := 0 to Count-1 do begin
    dest[i] := source[i];
    if source[i] = #0 then exit;
  end;
end;

var
  ArrChar : array [0..255] of char;
  VPChar : PChar;
  Str : string = 'Hello!';
begin
  VPChar := ArrChar; { Теперь VPChar ссылается на ArrChar }
  ArrChar[0] := 'T';
  ArrChar[1] := 'E';
  ArrChar[2] := 'S';
  ArrChar[3] := 'T';
  ArrChar[4] := #0;
  Writeln(VPChar); { Печатает 'TEST' }
  Writeln(ArrChar); { Печатает 'TEST' }

  VPChar := PChar(Str); { Получаем из string PChar }
  Writeln('VPChar = ',VPChar); { Печатает Hello! }
  Str := VPChar; { Обратный перевод из PChar в string - автоматически! }
  Writeln('Str = ',Str); { Печатает Hello! }
  { Arrchar := VPChar; }{ Нельзя присвоить! Несовместимые типы Array of Char и PChar }
  CopyPChar(ArrChar, VPChar, 255); { Можно копировать, 255 – копировать не более 255 символов }
  Writeln(ArrChar); { Печатает Hello! }
end.

Работа с файлами в Delphi

Запись в текстовый файл.

var
  T : TextFile; { Вместо Text => TextFile }
begin
  AssignFile(T,'MyFile.txt'); { Вместо Assign => AssignFile }
  Rewrite(T); { так же как в Pascal }
  Writeln(T,'Hello world!'); { так же }
  CloseFile(T); { Вместо Close => CloseFile }
end.  

Показать все файлы находящиеся в заданном каталоге.

procedure ShowDir; { Показать все файлы или каталоги }
var SR: TSearchRec; { Запись для хранения временных данных при поиске файлов }
begin
  { FindFirst задаёт параметры поиска и ищет первую подходящую запись в каталоге }
  { Параметры FindFirst: }
  {   1. Строка - Шаблон поиска }
  {   2. Аттрибуты для поиска: }
  {     faAnyFile - Файлы и каталоги, faReadOnly - Файлы только для чтения }
  {     faHidden - Скрытые файлы, faSysFile - Системные файлы }
  {     faVolumeID - Файлы идентификаторы размера }
  {     faDirectory - Каталоги, faArchive - Архивные файлы }
  {   3. Запись для поиска }
  if FindFirst('C:\Program Files\*.*', faAnyFile, SR) = 0 then
    repeat
      if SR.Attr and faDirectory <> 0 then { Если это каталог }
        Write('Directory: ')
      else { Иначе это файл }
        Write('File: ');
      Writeln(SR.Name);
    until FindNext(SR) <> 0;
  FindClose(SR); { После FindFirst, FindNext  вызвать FindClose } 
end;

Процедуры и функции в Delphi

External - после заголовка Procedure пишем external (директива) – которая обозначает, что процедура не описывается в программе, а берется в другой DLL. ' Например: procedure MyProcedure; external 'MyDLL.DLL';.

Forward - при объявлении процедуры, которая будет описана ниже.

Передача параметров в процедуру (cоглашения по вызовам):

Директива

Порядок передачи параметров

Где очищается стек

Передача параметров через регистры процессора

Register

Слева-направо

В подпрограмме

Да

Pascal

Слева-направо

В подпрограмме

Нет

Cdecl

Справа-налево

В вызывающей программе

Нет

StdCall (стандартный вызов)

Справа-налево

В подпрограмме

Нет

Safecall

Справа-налево

В подпрограмме

Нет

Использование соглашения по вызовам register наиболее эффективно, так как обычно позволяет избежать создания стекового фрейма при вызове подпрограммы.

Библиотеки в Delphi

Стандартные Unit

Имя Unit ?НЕ? может не совпадать с именем файла, где он хранится ??? .

{ Обычно пишем }
unit Unit7;

interface { Обьявление экспортируемых функций }

  { Внешние функции }
  function Sqr( I:Double ):Double;

implementation

  function Sqr( I:Double ):Double;
  begin
    Sqr := I*I;
  end;

initialization { не обязателен }
  Writeln('Тут пишутся те операторы которые выполняются при');
  Writeln('запуске программы (когда подключается модуль)');

finalization
{  может отсутствовать }
  Writeln('Операторы, которые выполняются всегда при закрытии программы');
  Writeln('Тут можно написать много строк');

end.

Динамические подключаемые библиотеки (DLL)

  { Заголовок библиотеки }
  library MyLib; { имя библиотеки }
   { имя должно совпадать с именем файла где хранится - MyLib.dpr }

  { DLL - не входит в проект, это законченная программа }
  { Программа и DLL могут быть написаны на разных языках }

  uses
    SysUtils,
    Classes;

  { Локальные типы данных и функции не видны! }
  type
    TMyRec = record
      F1 : byte;
      F2 : real;
      F3 : PChar;
    end;

  Procedure LibProc1( A:byte ) Export;
  begin
    Writeln('Тело 1 процедуры');
  end;

  Procedure LibProc2( A:TMyRec ) Export; //лучший вариант записи
  begin
    Writeln('Тело 2 процедуры');
  end;

  Procedure LibProc3( A:TMyRec) Export;
  begin
    Writeln('Тело 3 процедуры');
  end;

  { Все процедуры разом из DLL нельзя экспортировать в программу }
  { Для указания какие процедуры экспортировать служит директива Exports }
  Exports
    LibProc1, { Если параметры не указаны, индекс будет 0, внешнее имя LibProc1
     { внешнее имя чувствительно к регистру }
    LibProc2 index 2, { Т.е. у LibProc1 будет индекс 0, а у LibProc2 индекс 2.
      Правило: если вы импортируете по индексу, их надо указывать явно,
      не надеясь на индексы по умолчанию. }
    LibProc3 index 3 name 'ExternalName1' {внешнее имя};
    {  (Имя (внутр имя) в dll) index ЧИСЛО Name ВНЕШНЕЕ_ИМЯ;
       Вызывать можно через index или через внешнее имя лучше через index }

  {$R *.res}

  begin
    Writeln('Загрузка библиотеки');
  end.

Статический вызов DLL

1) I Внешнее имя в библиотекеI , external Iимя библиотеки I 2) I Новое имя I , external Iимя библиотеки I index l индекс l 3) I Новое имя I , external Iимя библиотеки I , name lвнешнее имяl

Динамический вызов DLL

Function LoadLibrary ( LibName : Pchar) : word ; Если возвр. «0» - то не нашел библиотеку , если нашел, то возвр. Число с которой ассоциируется библиотека Var Handle : word Handle := LoadLibrary ( ‘ MyLib ‘ ) ; Когда библиотека нужна, её нужно закрыть : Function FreeLibrary ( Handle : word ) ; Связывание Function GetProcAdress ( Handle : Word : Name : Pchar ) : Pointer Можно вместо Name указать индекс «1» Хорошим тоном считается Unit MyLibUnit Type TmyRec = recjrd F1 : byte F2 : real F3 : Pchar End; Interface Procedure UlibProc1 ( A : byte ) external ‘ MyLib ’ index 0 Procedure UlibProc2 ( A : TMyRec ) external ‘ MyLib ’ index 2 Procedure UlibProc3 ( A : pchar ) external ‘ MyLib ’ index 3 Emplementation End.

Подключается на этапе выполнения программы

Unit, DLL (Library) Вызов процедуры из DLL

-----------------------------------

Многозадачность (потоки и методы их синхронизации в Delphi)

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

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

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

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

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

Объявление класса потока

Для реализации потока используется абстрактный класс TThread, который надо унаследовать в своём классе и переопределить метод Execute.

type 
  TMyThread = class(TThread)
  protected
    procedure Execute; override; { Перегруженный метод предка (класса TThread) }
  end;

procedure TMyThread.Execute;
begin
  { Здесь можно писать код, который будет выполняться в отдельном потоке, }
  { выполнение которого подвешивало бы программу }
  { Примечание: метод execute предка вызывать не надо! }
end;

Создание и запуск потока

Теперь необходимо запустить поток. Как всякий класс tnew необходимо создать:
var MT : TMyThread;
begin
  MT := TMyThread.Create(true); { Значение true значит, что после создания класса поток автоматически запущен не будет }
  MT.FreeOnTerminate := true; { Освобождать при завершении }
  { Устанавливаем приоритет потока в одно из возможных значений: }
  { tpidle Работает, когда система простаивает 
tplowest Нижайший 
tplower Низкий 
tpnormal Нормальный 
tphigher Высокий 
tphighest Высочайший 
tptimecritical Критический
new.priority := tplowest; }
{  MT. }
  MT.Resume; { Запускаем поток }
end;

Чтобы поток отдал процессорное время вставляйте функцию sleep(n), где n - количество миллисекунд, на которое поток приостановит свое выполнение, встретив это функцию.

Обмен данными между потоками и основной программой

Execute, Suspendet, Synhronize

Tthread Методы класса TThread :

1) Procedure Execute ; override Появится implementation , где Begin // TODO -вставить что нужно end Что запустить / не запустить поток : Property Suspended. Boolean ; при False – поток ON 2) Procedure Synchronize ( VizProc : procedure ) - обращаем при выводе на экран Constructor Thread. Create ( Susp : Boolean ) Для закрытия потока : Procedure Terminate После вызова лучше дождаться что бы Property Terminated.Boolean

Работа с принтером

uses SysUtils, Dialogs, Classes, Graphics,
  Printers; { Подключаем модуль с принтером, там объявлен класс TPrinter и Printer : TPrinter }
  { Объект класса TPrinter создается автоматически если в Uses: Uses Printer }

var  printDialog : TPrintDialog; { Диалог для выбора принтера }
begin
  printDialog := TPrintDialog.Create(nil);
  printDialog.Options := [poPrintToFile];
  if printDialog.Execute then begin
    { 1 пункт = 1/72 дюйма }
    Writeln('Ширина листа в пунктах: ',Printer.PageWidth);
    Writeln('Высота листа в пунктах: ',Printer.PageHeight);
    Printer.Orientation := poPortrait; { Ориентация листа: Книжная }
    Printer.Orientation := poLandscape; { Альбомная }
    Printer.BeginDoc; { Начать рисование документа }
    { Далее рисуем то что хотим печатать на Canvas:TCanvas }
    { MoveTo(x,y:integer) – перемещение в точку x,y }
    Printer.Canvas.MoveTo(10,20);
    { LineTo – рисование линии из текущей точки заданную в соответствии с Pen }
    Printer.Canvas.LineTo(100,200); { Рисовать в точку X=100;Y=200) }
    { Настройка шрифта для вывода текста }
    with Printer.Canvas.Font do begin {шрифт}
      Name := 'Times New Roman'; { Имя шрифта (выбор шрифта из стандартных) }
      Style := [fsBold,fsItalic]; { Наклонный (fsItelic) жирный (fsBold) шрифт }
        { Style	fsBold–жирный, fsItalic – курсив,
          fsUnderLine - подчёркнутый, fsSrikeOut – зачеркнутый }
      Size := 15; { Размер шрифта: Size – размер задается в пунктах }
      Color := clRed; { Выбираем цвет }
    end;
    { Настройка инструмента "Перо" - рисование линий }
    with Printer.Canvas.Pen do begin
      Color := clBlue; { Цвет линий для рисования }
      Mode := pmXOR; { Режим рисования (прозрачность) }
      Style := psDash; { Тип линии:
       * psSolid — сплошная линия;
       * psDash — линия в виде пунктира (состоит из коротких линий);
       * psDot — линия из точек;
       и так далее }
      Width := 2; { Ширина (толщина) линии }
    end;
    { Настройка инструмента "Кисть" - закраска }
    { Задаёт параметры закраски рисуемых фигур }
    with Printer.Canvas.Brush do begin
      Color := clGreen; { Зелёная заливка }
      Style := bsSolid; { Сплошная }
    end;
    Printer.NewPage; { создать еще лист и перейти на него }
    { ..тут можно что-то нарисовать на 2-ой странице.. }
    Printer.EndDoc; { Отдать документ на печать (пробуем вывести на принтер) }
  end;
end.