2.4.1. Вызов внешних dll из паскаль скрипта

2.4.1.1. Общая информация

Возможность вызова методов из подгружаемых dll появилась с версии клиента 1.4.25.94.

Если библиотека еще не была загружена в приложение, то загрзуку библиотеки может выполнить явно и не явно.

2.4.1.2. Неявная загрузка Dll

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

Сигнатура метода:
  function MethodName(Arguments): ResultType; [CallingConvention;] external 'LibraryName' [MethodNameInExternalLibrary];
Где:
  • MethodName — имя метода;
  • Arguments — аргументы;
  • ResultType — возвращаемый тип;
  • CallingConvention — соглашение вызова;
  • LibraryName — имя библиотеки;
  • MethodNameInExternalLibrary — имя метода во внешней библиотеке.
Пример описания метода c именем MyFunction из библиотеки MyCustomLib.dll:
  function MyFunction(arg: integer): integer; external 'MyCustomLib.dll'

В качестве соглашений вызова могут использоваться stdcall, register, pascal, cdecl, safecall. Более подробно: соглашение о вызове.

В описании функции поддерживаются следующие типы:
  • Integer
  • Boolean
  • Char
  • Extended
  • String
  • Pointer
  • PChar
  • Object
  • Class
  • WideChar
  • PWideChar
  • AnsiString
  • Currency
  • Variant
  • Interface
  • WideString
  • Int64
  • Longint
  • Cardinal
  • Longword
  • Single
  • Byte
  • Shortint
  • Word
  • Smallint
  • Double
  • Real
  • DateTime
  • Comp
  • TObject  - наследники (класс должен быть зарегистрирован в скриптере)
Пример неявной загрузки библиотеки, вызова методов dll из паскаль скрипта:
  <pascal>
  { declaration of external functions }

  function MessageBox(hwnd: pointer; text, caption: AnsiString; msgtype: integer): integer;
    stdcall; external 'User32.dll' name 'MessageBoxA';

  function GetDiskFreeSpace(root: AnsiString; var secPerCluster, bytesPerCluster, freeClusters,
    totalClusters: integer): boolean; stdcall; external 'Kernel32.dll' name 'GetDiskFreeSpaceA';

  function FindWindow(className, windowName: AnsiString): integer;
    stdcall; external 'User32.dll' name 'FindWindowA';

  function GetKeyState(virtKey: integer): short; stdcall; external 'User32.dll';

  function FreeSpaceInfo(drive: String): string;
  var a, b, c, d: word;
  begin
    if GetDiskFreeSpace(drive, a, b, c, d) then
      result := Format('%d sections per cluster; %d bytes per cluster; %d free clusters; %d total clusters', [a, b, c, d])
    else
      result := '(could not get drive information)';
  end;

  const { windows consts }
    MB_ICONQUESTION = $20;
    MB_YESNO = $4;
    IDYES = 6;
    VK_SHIFT = $10;

  begin
    OutTxt := '';
    if MessageBox(nil, 'Are you sure?', 'Confirmation', MB_ICONQUESTION + MB_YESNO) = IDYES then
      OutTxt := OutTxt + 'MessageBox: user answered YES' + #13#10
    else
       OutTxt := OutTxt + 'MessageBox: user answered NO' + #13#10 ;

    OutTxt := OutTxt + 'Drive C: free space: ' + FreeSpaceInfo('C:') + #13#10 ;

    if FindWindow('TAppBuilder', '') > 0 then
       OutTxt := OutTxt + 'Delphi is open'  + #13#10
    else
       OutTxt := OutTxt + 'Delphi is not open'  + #13#10 ;

    if GetKeyState(VK_SHIFT) < 0 then
       OutTxt := OutTxt + 'Shift is pressed'  + #13#10
    else
       OutTxt := OutTxt + 'Shift is not pressed'  + #13#10 ;

    ShowMessage(OutTxt);
    end;
  </pascal>

2.4.1.3. Явная загрузка Dll

Явно загрузка выполняется с использованием метода LoadLibrary. Использование явной загрузки позволяет вручную управлять загрузкой и выгрузкой dll, тем самым писать более оптимизированный код. Использовать метод LoadLibrary следует в паре с методом FreeLibrary(dll) и конструкцией try finally.

Пример:
  <pascal>

  { declaration of external functions }
  function CreateCustomForm(caption: AnsiString; color: integer): TObject; stdcall; external 'CustomLib.dll';
  procedure ShowForm(form: TObject); external 'CustomLib.dll';
  procedure GetBounds(form: TObject; var l, t, w, h: integer); cdecl; external 'CustomLib.dll' name 'FormGetBounds';
  function DecodeToday(var year, month, day: integer): boolean; stdcall; external 'CustomLib.dll';
  function tsr(x, y: double): double; external 'CustomLib.dll' name 'TimesSquareRoot';

  const
    clRed = $0000FF;

  begin
    { explicit dynamic load of the library }
    dll := LoadLibrary('CustomLib');
    try
      ShowMessage(Format('CustomLib.dll loaded: handle = %d', [dll]));
      frm := CreateCustomForm('My form', clRed);
      try
        ShowForm(frm);
        l := 0; t := 0;
        w := 0; h := 0;
        GetBounds(frm, l, t, w, h);
        ShowMessage(Format('Form bounds: left=%d, top=%d, width=%d, height=%d', [l, t, w, h]));
      finally
        frm.Free;
      end;
    finally
      FreeLibrary(dll); { unload }
      ShowMessage(Format('DLL %d unloaded', [dll]));
    end;

    { automatic load of the library on demand }
    y := 0; m := 0; d := 0;
    leap := DecodeToday(y, m, d);
    ShowMessage(Format('Today: day=%d, month=%d, year=%d', [d, m, y]));
    if leap then
      ShowMessage(Format('%d is leap', [y]))
    else
      ShowMessage(Format('%d is not leap', [y]));
    ShowMessage('Square root of ' + IntToStr(m * d) + ' is ' + FloatToStr(tsr(m, d)));


  end;
  </pascal>