Шаг 307.
Создание внутрипроцессных серверов автоматизации. Создание и использование DLL. Вызов в DLL функций приложения

    На этом шаге мы рассмотрим особенности вызова функций приложения из DLL.

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

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

var
  NSum:Integer = 0;

function CalculateSum(ReturnCallback:pointer):integer; stdcall;
  external 'FirstLib.dll' name 'Sum';

function GetNextValue:integer; stdcall;
begin
  if NSum<200 then begin
    Inc(NSum);
    Result := NSum;
  end else Result := -1;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Caption := IntToStr(CalculateSum(@GetNextValue));
end;

    В приложении создается функция, не являющаяся методом класса, адрес которой передается в DLL. При реализации таких функций - это так называемые функции обратного вызова (callback functions) - желательно использовать соглашение о вызовах stdcall, чтобы их можно было бы вызывать из DLL, созданных на других языках программирования. Вызов функции в DLL можно проиллюстрировать на примере:

type
  TReturnNextMethod=function:integer; stdcall;

function CalculateSum(ReturnCallback:pointer):integer; stdcall;
  export;
var
  N:integer;
  ReturnNext:TReturnNextMethod;
begin
  Result := 0;
  if ReturnCallback=nil then Exit;
  N := 0;
  ReturnNext:=TReturnNextMethod(ReturnCallback);
  while N>=0 do begin
    N := ReturnNext;
    if N>=0 then Result:=Result+N;
  end;
end;
Текст этого приложения вместе с DLL можно взять здесь (414,3 Кб).

    При вызове метода объекта следует учитывать тот факт, что метод объекта характеризуется двумя адресами - адресом метода и адресом данных. Соответственно, необходимо передавать два указателя. Вместо передачи двух указателей можно воспользоваться структурой TMethod, определенной в модуле SysUtils.pas:

type 
  TMethod = record
    Code, Data: Pointer; 
  end;

    Код в приложении для приведенного выше примера выглядит следующим образом:

type
  TGetNextValueObject=function:integer of object; stdcall;

function CalculateSumObject(Method:TMethod):integer;
  stdcall;  external 'FirstLib.dll' name 'SumObject';

function TForm1.GetNextValueObject:integer; stdcall;
begin
  if NSum<10 then begin
    Inc(NSum);
    Result := NSum;
  end else Result := -1;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  FGetNext:TGetNextValueObject;
begin
  NSum := 0;
  FGetNext := GetNextValueObject;
  Caption := IntToStr(CalculateSumObject(TMethod(FGetNext)));
end;

    Код в DLL, осуществляющий вызов метода объекта, выглядит следующим образом:

type
  TReturnNextMethodObject=function:integer of object;
   stdcall;

function CalculateSumObject(Method:TMethod):integer;
   stdcall; export;
var
  N:integer;
  ReturnNext:TReturnNextMethodObject;
begin
  Result := 0;
  N := 0;
  ReturnNext := TReturnNextMethodObject(Method);
  while N>=0 do begin
    N := ReturnNext;
    if N>=0 then Result := Result+N;
  end;
end;

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

    На следующем шаге мы закончим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг