Libraries and Packages (Delphi)

From RAD Studio
Jump to: navigation, search

Go Up to Libraries and Packages Index

A dynamically loadable library is a dynamic-link library (DLL) on Windows, a DYLIB on Mac, or a shared object (SO) on Linux. It is a collection of routines that can be called by applications and by other DLLs or shared objects. Like units, dynamically loadable libraries contain sharable code or resources. But this type of library is a separately compiled executable that is linked, at run time, to the programs that use it.

Delphi programs can call DLLs and shared objects written in other languages, and applications written in other languages can call DLLs or shared objects written in Delphi.

Calling Dynamically Loadable Libraries

You can call operating system routines that are not linked to your application. These routines are usually in a DLL or in a shared object. On Windows and macOS, there is no compile-time validation of attempts to import a routine. It means that the library does not need to be present when you compile your program.

On other platforms, such as Linux, to resolve an external reference, you have to link to a shared object. If you want to avoid validation, use LoadLibrary and GetProcAddress as described in the Dynamic Loading section.

Before you can call routines defined in DLL or shared object, you must import them. This can be done in two ways: by declaring an external procedure or function, or by direct calls to the operating system. Whichever method you use, the routines are not linked to your application until run time.

Delphi does not support importing variables from DLLs or shared objects.

Static Loading

The simplest way to import a procedure or function is to declare it using the external directive. For example:

 procedure DoSomething; external 'MYLIB.DLL';

If you include this declaration in a program, MYLIB.DLL is loaded once, when the program starts. Throughout the execution of the program, the identifier DoSomething always refers to the same entry point in the same shared library.

Declarations of imported routines can be placed directly in the program or unit where they are called. To simplify maintenance, however, you can collect external declarations into a separate "import unit" that also contains any constants and types required for interfacing with the library. Other modules that use the import unit can call any routines declared in it.

Delayed Loading (Windows-only)

The delayed directive can be used to decorate an external routine to delay the loading of the library containing the routine. The actual loading happens when the routine is called for the first time. The following example demonstrates the use of the delayed directive:

 function GetSomething: Integer; external 'somelibrary.dll' delayed;

In the example above, the GetSomething routine is imported from the somelibrary.dll library. The delayed directive ensures that somelibrary.dll is not statically linked to the application, but rather dynamically.

The delayed directive is useful in the case where the imported routines do not exist on the target operating system on which the application is run. Statically imported routines require that the operating system find and load the library when the application is started. If the routine is not found in the loaded library, or the library does not exist, the Operating System halts the execution of the application. Using the delayed directive enables you to check, at run time, whether the Operating System supports the required APIs; only then you can call the imported routines.

Another potential use for the delayed directive is related to the memory footprint of the application: decorating the less probably to be used routines, as delayed may decrease the memory footprint of the application, because the libraries are loaded only when required. The abusive use of delayed can damage the speed performance of the program (as perceived by the end user).

Note: Trying to call a delayed routine that cannot be resolved results in a run-time error (or an exception, if the SysUtils unit is loaded).

In order to fine-tune the delay-loading process used by the Delphi Run-time Library, you can register hook procedures to oversee and change its behavior. To accomplish this, use SetDliNotifyHook2 and SetDliFailureHook2, declared in the SysInit unit. Also see the code example at DelayedLoading (Delphi).

Dynamic Loading

You can access routines in a library through direct calls to Windows APIs, including LoadLibrary, FreeLibrary, and GetProcAddress. They are also available on macOS, Linux, and Android. These functions are declared in Winapi.Windows.pas unit for Windows and in System.SysUtils.pas for other platforms. In this case, use procedural-type variables to reference the imported routines.


For example:

uses System.SysUtils {$IFDEF MSWINDOWS},Winapi.Windows{$ENDIF};
 
type
   TTimeRec = record
     Second: Integer;
     Minute: Integer;
     Hour: Integer;
   end;
 
   TGetTime = procedure(var Time: TTimeRec);
 
var
  Time: TTimeRec;
  Handle: HMODULE;
  GetTime: TGetTime;
begin
  Handle := LoadLibrary('libraryname');
  if Handle <> 0 then
  begin
    @GetTime := GetProcAddress(Handle, 'GetTime');
    if @GetTime <> nil then
    begin
      GetTime(Time);
      with Time do
        Writeln('The time is ', Hour, ':', Minute, ':', Second);
    end;
    FreeLibrary(Handle);
  end;
end.

When you import routines this way, the library is not loaded until the code containing the call to LoadLibrary executes. The library is later unloaded by the call to FreeLibrary. This allows you to conserve memory and to run your program even when some of the libraries it uses are not present.

See Also