Difference between revisions of "Dynamically loading headers"

From Free Pascal wiki
(Solution of Delphi 2010)
(code highlighting and category)
Line 19: Line 19:
  
 
dynlibs.pas
 
dynlibs.pas
 
+
<delphi>type
   type
+
   PLibHandler = ^TLibHandler;
    PLibHandler = ^TLibHandler;
+
 
 +
  TLibEventLoading = function(User: Pointer; Handler: PLibHandler): Boolean;
 +
  TLibEventUnloading = procedure(Handler: PLibHandler);
 
      
 
      
     TLibEventLoading = function(User: Pointer; Handler: PLibHandler): Boolean;
+
  PPLibSymbol = ^PLibSymbol;
     TLibEventUnloading = procedure(Handler: PLibHandler);
+
  PLibSymbol = ^TLibSymbol;
 +
  TLibSymbol = record
 +
     pvar: PPointer;  { pointer to Symbol variable }
 +
    name: String;    { name of the Symbol }
 +
    weak: Boolean;  { no error if this symbol don't exist }
 +
  end;
 +
 
 +
  TLibHandler = record
 +
    InterfaceName: String;               { abstract name of the library }
 +
    Defaults    : array of String;      { list of default library filenames }
 +
    Filename    : String;                { handle of the current loaded library }
 +
    Handle      : TLibHandle;            { filename of the current loaded library }
 +
    Loading      : TLibEventLoading;     { loading event, called after the unit is loaded }
 +
     Unloading    : TLibEventUnloading;    { unloading event, called before the unit is unloaded }
 +
    SymCount    : Integer;              { number of symbols }
 +
    Symbols      : PLibSymbol;            { symbol address- and namelist }
 +
    ErrorMsg    : String;                { last error message }
 +
    RefCount    : Integer;              { reference counter }
 +
  end;
 
      
 
      
    PPLibSymbol = ^PLibSymbol;
 
    PLibSymbol = ^TLibSymbol;
 
    TLibSymbol = record
 
      pvar: PPointer;  { pointer to Symbol variable }
 
      name: String;    { name of the Symbol }
 
      weak: Boolean;  { no error if this symbol don't exist }
 
    end;
 
 
      
 
      
    TLibHandler = record
+
{ handler definition }
      InterfaceName: String;                { abstract name of the library }
+
function LibraryHandler(const InterfaceName: String; const DefaultLibraries: array of String;
      Defaults    : array of String;      { list of default library filenames }
+
  const Symbols: PLibSymbol; const SymCount: Integer; const AfterLoading: TLibEventLoading = nil;
      Filename    : String;                { handle of the current loaded library }
+
  const BeforeUnloading: TLibEventUnloading = nil): TLibHandler;
      Handle      : TLibHandle;            { filename of the current loaded library }
+
 
      Loading      : TLibEventLoading;      { loading event, called after the unit is loaded }
+
{ initialization/finalization }
      Unloading    : TLibEventUnloading;    { unloading event, called before the unit is unloaded }
+
function TryInitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
      SymCount    : Integer;              { number of symbols }
+
  const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
      Symbols      : PLibSymbol;            { symbol address- and namelist }
+
function TryInitializeLibrary(var Handler: TLibHandler; const LibraryName: String = '';
      ErrorMsg    : String;                { last error message }
+
  const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
      RefCount    : Integer;              { reference counter }
+
function InitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
    end;
+
  const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
   
+
function InitializeLibrary(var Handler: TLibHandler; const LibraryName: String = '';
   
+
  const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
  { handler definition }
+
function ReleaseLibrary(var Handler: TLibHandler): Integer;
  function LibraryHandler(const InterfaceName: String; const DefaultLibraries: array of String;
+
 
    const Symbols: PLibSymbol; const SymCount: Integer; const AfterLoading: TLibEventLoading = nil;
+
{ errors }
    const BeforeUnloading: TLibEventUnloading = nil): TLibHandler;
+
procedure AppendLibraryError(var Handler: TLibHandler; const Msg: String);
 
+
function GetLastLibraryError(var Handler: TLibHandler): String;
  { initialization/finalization }
+
procedure RaiseLibraryException(var Handler: TLibHandler);
  function TryInitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
+
 
    const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
+
{ symbol load/clear }
  function TryInitializeLibrary(var Handler: TLibHandler; const LibraryName: String = '';
+
function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer;
    const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
+
  const ErrorSym: PPLibSymbol = nil): Boolean;
  function InitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
+
procedure ClearLibrarySymbols(const Symbols: PLibSymbol; const Count: Integer);</delphi>
    const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
 
  function InitializeLibrary(var Handler: TLibHandler; const LibraryName: String = '';
 
    const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
 
  function ReleaseLibrary(var Handler: TLibHandler): Integer;
 
 
 
  { errors }
 
  procedure AppendLibraryError(var Handler: TLibHandler; const Msg: String);
 
  function GetLastLibraryError(var Handler: TLibHandler): String;
 
  procedure RaiseLibraryException(var Handler: TLibHandler);
 
 
 
  { symbol load/clear }
 
  function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer;
 
    const ErrorSym: PPLibSymbol = nil): Boolean;
 
  procedure ClearLibrarySymbols(const Symbols: PLibSymbol; const Count: Integer);
 
  
 
=== Naming conventions ===
 
=== Naming conventions ===
Line 84: Line 83:
  
 
==== foobar.pas ====
 
==== foobar.pas ====
  unit foobar;
+
<delphi>unit foobar;
 
    
 
    
  {$i foobar.inc}
+
{$i foobar.inc}
 
    
 
    
  end.
+
end.</delphi>
  
 
==== foobardyn.pas ====
 
==== foobardyn.pas ====
  unit foobardyn;
+
<delphi>unit foobardyn;
 
    
 
    
  {$DEFINE LOAD_DYNAMICALLY}
+
{$DEFINE LOAD_DYNAMICALLY}
  {$i foobar.inc}
+
{$i foobar.inc}
 
    
 
    
  end.
+
end.</delphi>
  
 
==== foobar.inc ====
 
==== foobar.inc ====
  {$mode objfpc}{$H+}
+
<delphi>{$mode objfpc}{$H+}
  {$MACRO ON}
+
{$MACRO ON}
  // other possible compiler flags
+
// other possible compiler flags
 
    
 
    
  interface
+
interface
 
    
 
    
  uses
+
uses
    ctypes, dynlibs;
+
  ctypes, dynlibs;
 +
 +
{$IFDEF UNIX}
 +
  {$DEFINE extdecl:=cdecl}
 +
  const
 +
    foobarlib = 'libfoobar.'+sharedsuffix;
 +
    foobarvlib = foobarlib+'.0.9.9';
 +
{$ENDIF}
 +
{$IFDEF WINDOWS}
 +
  {$DEFINE extdecl:=stdcall}
 +
  const
 +
    foobarlib = 'libfoobar.dll';
 +
    foobarvlib = foobarlib;
 +
{$ENDIF}
 
    
 
    
  {$IFDEF UNIX}
+
{$IFDEF LOAD_DYNAMICALLY}
    {$DEFINE extdecl:=cdecl}
+
  {$DEFINE D}
    const
+
{$ELSE}
      foobarlib = 'libfoobar.'+sharedsuffix;
+
   {$DEFINE S}
      foobarvlib = foobarlib+'.0.9.9';
+
{$ENDIF}
  {$ENDIF}
 
   {$IFDEF WINDOWS}
 
    {$DEFINE extdecl:=stdcall}
 
    const
 
      foobarlib = 'libfoobar.dll';
 
      foobarvlib = foobarlib;
 
  {$ENDIF}
 
 
    
 
    
  {$IFDEF LOAD_DYNAMICALLY}
+
// header stuff
    {$DEFINE D}
 
  {$ELSE}
 
    {$DEFINE S}
 
  {$ENDIF}
 
 
    
 
    
  // header stuff
+
{$IFDEF S}function{$ELSE}var{$ENDIF}foobar_dosomething{$IFDEF D}: function{$ENDIF}(a:cint; b:cdouble): cint; extdecl;{$IFDEF S}external foobarlib;{$ENDIF}
 +
// more external functions
 
    
 
    
  {$IFDEF S}function{$ELSE}var{$ENDIF}foobar_dosomething{$IFDEF D}: function{$ENDIF}(a:cint; b:cdouble): cint; extdecl;{$IFDEF S}external foobarlib;{$ENDIF}
 
  // more external functions
 
 
    
 
    
 +
{$IFDEF LOAD_DYNAMICALLY}
 +
function InitializeFoobar(const LibraryName: String = ''): Integer;
 +
function TryInitializeFoobar(const LibraryName: string = ''): Integer;
 +
function ReleaseFoobar: Integer;
 
    
 
    
   {$IFDEF LOAD_DYNAMICALLY}
+
var
  function InitializeFoobar(const LibraryName: String = ''): Integer;
+
   FoobarLibrary: TLibHandler;
  function TryInitializeFoobar(const LibraryName: string = ''): Integer;
+
{$ENDIF LOAD_DYNAMICALLY}
  function ReleaseFoobar: Integer;
 
 
    
 
    
  var
+
implementation
    FoobarLibrary: TLibHandler;
 
  {$ENDIF LOAD_DYNAMICALLY}
 
 
    
 
    
   implementation
+
{$IFDEF LOAD_DYNAMICALLY}
 +
const
 +
   foobar_symbols: array[0..X] of TLibSymbol = (
 +
    (pvar:@foobar_dosomething; name:'foobar_dosomething'; weak:false), 
 +
    // more external functions
 +
  );
 
    
 
    
  {$IFDEF LOAD_DYNAMICALLY}
+
function foobar_init(User: Pointer; Handler: PLibHandler): Boolean;
  const
+
var
    foobar_symbols: array[0..X] of TLibSymbol = (
+
  args: PMysqlArgs absolute User;
      (pvar:@foobar_dosomething; name:'foobar_dosomething'; weak:false), 
+
begin
      // more external functions
+
  // do some initialization calls
    );
+
end;
 
    
 
    
  function foobar_init(User: Pointer; Handler: PLibHandler): Boolean;
+
procedure foobar_end(Handler: PLibHandler);
  var
+
begin
    args: PMysqlArgs absolute User;
+
   // do some calls before library is unloaded
   begin
+
end;
    // do some initialization calls
 
  end;
 
 
    
 
    
  procedure foobar_end(Handler: PLibHandler);
+
function TryInitializeFoobar(const LibraryName: string): Integer;
  begin
+
begin
    // do some calls before library is unloaded
+
  Result := TryInitializeLibrary(FoobarLibrary, LibraryName);
  end;
+
end;
 
    
 
    
  function TryInitializeFoobar(const LibraryName: string): Integer;
+
function InitializeFoobar(const LibraryName: String): Integer;
  begin
+
begin
    Result := TryInitializeLibrary(FoobarLibrary, LibraryName);
+
  Result := InitializeLibrary(FoobarLibrary, LibraryName);
  end;
+
end;
 
    
 
    
  function InitializeFoobar(const LibraryName: String): Integer;
+
function ReleaseFoobar: Integer;
  begin
+
begin
    Result := InitializeLibrary(FoobarLibrary, LibraryName);
+
  Result := ReleaseLibrary(FoobarLibrary);
  end;
+
end;
 
    
 
    
  function ReleaseFoobar: Integer;
+
initialization
  begin
+
   FoobarLibrary := LibraryHandler('foobar', [foobarlib,foobarvlib, {moreversions} ], @foobar_symbols,
    Result := ReleaseLibrary(FoobarLibrary);
+
    Length(foobar_symbols), @foobar_init, @foobar_end);
  end;
+
{$ENDIF}</delphi>
 
 
   initialization
 
    FoobarLibrary := LibraryHandler('foobar', [foobarlib,foobarvlib, {moreversions} ], @foobar_symbols,
 
      Length(foobar_symbols), @foobar_init, @foobar_end);
 
  {$ENDIF}
 
  
 
=== Usage ===
 
=== Usage ===
  
  program foobar_user;
+
<delphi>program foobar_user;
 
    
 
    
  uses
+
uses
    foobardyn;
+
  foobardyn;
 
+
  begin
+
begin
    InitializeFoobar;  // if no paramter is submitted, the default library is loaded
+
  InitializeFoobar;  // if no paramter is submitted, the default library is loaded
    foobar_dosomething(2, 1.3);
+
  foobar_dosomething(2, 1.3);
    ReleaseFoobar;
+
  ReleaseFoobar;
  end.
+
end.</delphi>
  
 
=== Notes ===
 
=== Notes ===
Line 202: Line 201:
 
In Delphi 2010 they introduced "delayed" loading
 
In Delphi 2010 they introduced "delayed" loading
  
  function GetFoobar: Integer; external 'foobar.dll' delayed;
+
<delphi>function GetFoobar: Integer; external 'foobar.dll' delayed;</delphi>
  
 
More Info:
 
More Info:
Line 214: Line 213:
 
* sqlite: packages/sqlite/src/sqlite3.inc
 
* sqlite: packages/sqlite/src/sqlite3.inc
 
* zorba: packages/zorba/src/zorba.inc
 
* zorba: packages/zorba/src/zorba.inc
 +
 +
[[Category:Proposals]]

Revision as of 08:27, 20 December 2010

Abstract

Currently, most library headers in fpc/packages support only static loading. Some exceptions are mysql, ibase, sqlite (and some other database headers). The idea is now to add dynamic loading support for other headers. This page should provide some place to list pro/contra, implementation details, etc...

Since dynamic loading is not supported by the compiler directly, it's required to define some framework, so that all headers are "implemented" in an uniform way and can be used easily.

Pro

  • loading libraries at runtime for plugin support

Contra

  • compatibility breaker (using dynamic loading is just an option, that should be choosen carefully)
  • may produce strange errors, if wrong versions of libraries are loaded.

Suggestion w/o compiler modifications

Framework

dynlibs.pas <delphi>type

 PLibHandler = ^TLibHandler;
  
 TLibEventLoading = function(User: Pointer; Handler: PLibHandler): Boolean;
 TLibEventUnloading = procedure(Handler: PLibHandler);
   
 PPLibSymbol = ^PLibSymbol;
 PLibSymbol = ^TLibSymbol;
 TLibSymbol = record
   pvar: PPointer;  { pointer to Symbol variable }
   name: String;    { name of the Symbol }
   weak: Boolean;   { no error if this symbol don't exist }
 end;
 
 TLibHandler = record
   InterfaceName: String;                { abstract name of the library }
   Defaults     : array of String;       { list of default library filenames }
   Filename     : String;                { handle of the current loaded library }
   Handle       : TLibHandle;            { filename of the current loaded library }
   Loading      : TLibEventLoading;      { loading event, called after the unit is loaded }
   Unloading    : TLibEventUnloading;    { unloading event, called before the unit is unloaded }
   SymCount     : Integer;               { number of symbols }
   Symbols      : PLibSymbol;            { symbol address- and namelist }
   ErrorMsg     : String;                { last error message }
   RefCount     : Integer;               { reference counter }
 end;
   
   

{ handler definition } function LibraryHandler(const InterfaceName: String; const DefaultLibraries: array of String;

 const Symbols: PLibSymbol; const SymCount: Integer; const AfterLoading: TLibEventLoading = nil;
 const BeforeUnloading: TLibEventUnloading = nil): TLibHandler;

{ initialization/finalization } function TryInitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;

 const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;

function TryInitializeLibrary(var Handler: TLibHandler; const LibraryName: String = ;

 const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;

function InitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;

 const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;

function InitializeLibrary(var Handler: TLibHandler; const LibraryName: String = ;

 const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;

function ReleaseLibrary(var Handler: TLibHandler): Integer;

{ errors } procedure AppendLibraryError(var Handler: TLibHandler; const Msg: String); function GetLastLibraryError(var Handler: TLibHandler): String; procedure RaiseLibraryException(var Handler: TLibHandler);

{ symbol load/clear } function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer;

 const ErrorSym: PPLibSymbol = nil): Boolean;

procedure ClearLibrarySymbols(const Symbols: PLibSymbol; const Count: Integer);</delphi>

Naming conventions

For the dynamic headers the suffix "dyn" is added

  • static: foobar.pas
  • dynamic: foobardyn.pas

Header implementation

foobar.pas

<delphi>unit foobar;

{$i foobar.inc}

end.</delphi>

foobardyn.pas

<delphi>unit foobardyn;

{$DEFINE LOAD_DYNAMICALLY} {$i foobar.inc}

end.</delphi>

foobar.inc

<delphi>{$mode objfpc}{$H+} {$MACRO ON} // other possible compiler flags

interface

uses

 ctypes, dynlibs;

{$IFDEF UNIX}

 {$DEFINE extdecl:=cdecl}
 const
   foobarlib = 'libfoobar.'+sharedsuffix;
   foobarvlib = foobarlib+'.0.9.9';

{$ENDIF} {$IFDEF WINDOWS}

 {$DEFINE extdecl:=stdcall}
 const
   foobarlib = 'libfoobar.dll';
   foobarvlib = foobarlib;

{$ENDIF}

{$IFDEF LOAD_DYNAMICALLY}

 {$DEFINE D}

{$ELSE}

 {$DEFINE S}

{$ENDIF}

// header stuff

{$IFDEF S}function{$ELSE}var{$ENDIF}foobar_dosomething{$IFDEF D}: function{$ENDIF}(a:cint; b:cdouble): cint; extdecl;{$IFDEF S}external foobarlib;{$ENDIF} // more external functions


{$IFDEF LOAD_DYNAMICALLY} function InitializeFoobar(const LibraryName: String = ): Integer; function TryInitializeFoobar(const LibraryName: string = ): Integer; function ReleaseFoobar: Integer;

var

 FoobarLibrary: TLibHandler;

{$ENDIF LOAD_DYNAMICALLY}

implementation

{$IFDEF LOAD_DYNAMICALLY} const

 foobar_symbols: array[0..X] of TLibSymbol = (
   (pvar:@foobar_dosomething; name:'foobar_dosomething'; weak:false),  
   // more external functions
 );
 

function foobar_init(User: Pointer; Handler: PLibHandler): Boolean; var

 args: PMysqlArgs absolute User;

begin

 // do some initialization calls

end;

procedure foobar_end(Handler: PLibHandler); begin

 // do some calls before library is unloaded

end;

function TryInitializeFoobar(const LibraryName: string): Integer; begin

 Result := TryInitializeLibrary(FoobarLibrary, LibraryName);

end;

function InitializeFoobar(const LibraryName: String): Integer; begin

 Result := InitializeLibrary(FoobarLibrary, LibraryName);

end;

function ReleaseFoobar: Integer; begin

 Result := ReleaseLibrary(FoobarLibrary);

end;

initialization

 FoobarLibrary := LibraryHandler('foobar', [foobarlib,foobarvlib, {moreversions} ], @foobar_symbols,
   Length(foobar_symbols), @foobar_init, @foobar_end);

{$ENDIF}</delphi>

Usage

<delphi>program foobar_user;

uses

 foobardyn;

begin

 InitializeFoobar;  // if no paramter is submitted, the default library is loaded
 foobar_dosomething(2, 1.3);
 ReleaseFoobar;

end.</delphi>

Notes

Solution of Delphi 2010 (modifying compiler)

In Delphi 2010 they introduced "delayed" loading

<delphi>function GetFoobar: Integer; external 'foobar.dll' delayed;</delphi>

More Info: http://docwiki.embarcadero.com/RADStudio/en/Libraries_and_Packages#Delayed_Loading

Others

Example implementations can be found in

  • mysql: packages/mysql/src/mysql.inc
  • sqlite: packages/sqlite/src/sqlite3.inc
  • zorba: packages/zorba/src/zorba.inc