Libraries and Packages (Object Pascal)
Go Up to Libraries and Packages Index
A dynamically loadable library is a dynamic-link library (DLL) on Windows, or a DYLIB on Mac. 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.
Object Pascal programs can call DLLs and assemblies written in other languages, and applications written in other languages can call DLLs or assemblies written in Object Pascal.
Calling Dynamically Loadable Libraries
You can call operating system routines directly, but they are not linked to your application until run time. This means that the library need not be present when you compile your program. Also, there is no compile-time validation of attempts to import a routine.
Before you can call routines defined in DLL or assembly, 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.
Object Pascal does not support importing variables from DLLs or assemblies.
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.
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 Object Pascal 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 (Object Pascal).
Dynamic Loading (Windows-only)
You can access routines in a library through direct calls to Windows APIs, including LoadLibrary, FreeLibrary, and GetProcAddress. These functions are declared in Windows.pas. In this case, use procedural-type variables to reference the imported routines.
uses Windows, ...; type TTimeRec = record Second: Integer; Minute: Integer; Hour: Integer; end; TGetTime = procedure(var Time: TTimeRec); THandle = Integer; var Time: TTimeRec; Handle: THandle; 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.