12.3 Using a library in a pascal program

In order to use a function that resides in a library, it is sufficient to declare the function as it exists in the library as an external function, with correct arguments and return type. The calling convention used by the function should be declared correctly as well. The compiler will then link the library as specified in the external statement to your program1.

For example, to use the library as defined above from a pascal program, you can use the following pascal program (except on Macos):

Listing: progex/psubs.pp


program testsubs;

uses strings;

function SubStr(const CString: PChar; FromPos, ToPos: longint): PChar;
  cdecl; external 'subs';

var
  s: PChar;
  FromPos, ToPos: Integer;
begin
  s := strnew('TestMe');
  FromPos := 2;
  ToPos := 3;
  WriteLn(SubStr(s, FromPos, ToPos));
end.

As is shown in the example, you must declare the function as external, followed by the library name. On mac, you must also add the {$linklib } directive, as in the following example:

Listing: progex/psubsm.pp


program testsubs;

uses strings;

function SubStr(const CString: PChar; FromPos, ToPos: longint): PChar;
  cdecl; external 'subs';

var
  s: PChar;
  FromPos, ToPos: Integer;
begin
  s := strnew('TestMe');
  FromPos := 2;
  ToPos := 3;
  WriteLn(SubStr(s, FromPos, ToPos));
end.

Here also, it is necessary to specify the correct calling convention (it should always match the convention as used by the function in the library), and to use the correct casing for your declaration. Also notice, that the library importing did not specify the filename extension, nor was the lib prefix added.

This program can be compiled without any additional command-switches, and should run just like that, provided the library is placed where the system can find it. For example, on linux, this is /usr/lib or any directory listed in the /etc/ld.so.conf file. On Windows, this can be the program directory, the Windows system directory, or any directory mentioned in the PATH.

Using the library in this way links the library to your program at compile time. This means that

  1. The library must be present on the system where the program is compiled.

  2. The library must be present on the system where the program is executed.

  3. Both libraries must be exactly the same.

Or it may simply be that you don’t know the name of the function to be called, you just know the arguments it expects.

It is therefore also possible to load the library at run-time, store the function address in a procedural variable, and use this procedural variable to access the function in the library. The dynlibs provides a cross-platform API to load a libary and get the entry points of the functions in the library.

The following example demonstrates this technique:

Listing: progex/plsubs.pp


program testsubs;

uses dynlibs;

Type
  TSubStrFunc =
    function(const CString:PChar;FromPos,ToPos: longint):PChar;cdecl;

const
  baselibname = 'libsubs.';

{$ifdef windows}
  libext = 'dll';
{$else}
{$ifdef macos}
  libext = 'dylib';
{$else}
  libext = 'so';
{$endif}
{$endif}

var
  s: PChar;
  FromPos, ToPos: Integer;
  lib : TLibHandle;
  SubStr : TSubStrFunc;

begin
  s := 'Test';
  FromPos := 2;
  ToPos := 3;
  lib:=LoadLibrary('libsubs.so');
  Pointer(Substr):=GetProcAddress(lib,'SubStr');
  WriteLn(SubStr(s, FromPos, ToPos));
  unloadLibrary(lib);
end.

As in the case of compile-time linking, the crucial thing in this listing is the declaration of the TSubStrFunc type. It should match the declaration of the function you’re trying to use.

Failure to specify a correct definition will result in a faulty stack or, worse still, may cause your program to crash with an access violation.

1If you omit the library name in the external modifier, then you can still tell the compiler to link to that library using the {$Linklib} directive.