diff --git a/rtl/inc/dynlibs.pas b/rtl/inc/dynlibs.pas index e288f9b4f7..49692cb888 100644 --- a/rtl/inc/dynlibs.pas +++ b/rtl/inc/dynlibs.pas @@ -20,6 +20,9 @@ unit dynlibs; interface +uses + SysUtils, RtlConsts, SysConst; + { --------------------------------------------------------------------- Read OS-dependent interface declarations. ---------------------------------------------------------------------} @@ -38,13 +41,68 @@ Function LoadLibrary(Name : AnsiString) : TLibHandle; Function GetProcedureAddress(Lib : TlibHandle; ProcName : AnsiString) : Pointer; Function UnloadLibrary(Lib : TLibHandle) : Boolean; + // Kylix/Delphi compability +Type + HModule = TLibHandle; + Function FreeLibrary(Lib : TLibHandle) : Boolean; Function GetProcAddress(Lib : TlibHandle; ProcName : AnsiString) : Pointer; + +// Dynamic Library Manager + +{ Note: If you look for some code that uses this library handler, take a look at + sqlite3.inc of sqlite package (simple) or + mysql.inc of mysql package (advanced) +} + Type - HModule = TLibHandle; + PLibHandler = ^TLibHandler; + + TLibEventLoading = function(User: Pointer; Handler: PLibHandler; out ErrorMsg: String): Boolean; + TLibEventUnloading = procedure(User: Pointer; Handler: PLibHandler); + + PPLibSymbol = ^PLibSymbol; + PLibSymbol = ^TLibSymbol; + TLibSymbol = record + pvar: PPointer; { pointer to Symbol variable } + name: String; { name of the Symbol } + weak: Boolean; { weak } + end; + + TLibHandler = record + AbstractName : String; + Handle : TLibHandle; + Filename : String; + Loading : TLibEventLoading; + Unloading : TLibEventUnloading; + SymCount : Integer; + Symbols : PLibSymbol; + ErrorMsg : String; + RefCount : Integer; + end; + + +function LibraryHandler(const AbstractName: String; const Symbols: PLibSymbol; const SymCount: Integer; + const AfterLoading: TLibEventLoading = nil; const BeforeUnloading: TLibEventUnloading = nil): TLibHandler; +function TryInitializeLibrary(var Handler: TLibHandler; const Filenames: array of String; + const User: Pointer = nil; const Weak: Boolean = False): Integer; +function TryInitializeLibrary(var Handler: TLibHandler; const Filename: String; + const User: Pointer = nil; const Weak: Boolean = False): Integer; +function InitializeLibrary(var Handler: TLibHandler; const Filenames: array of String; + const User: Pointer = nil; const Weak: Boolean = False): Integer; +function InitializeLibrary(var Handler: TLibHandler; const Filename: String; + const User: Pointer = nil; const Weak: Boolean = False): Integer; +function ReleaseLibrary(var Handler: TLibHandler; User: Pointer = nil): Integer; +function GetLastLibraryError(var Handler: TLibHandler): String; +procedure RaiseLibraryException(var Handler: TLibHandler); + +function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer; + const ErrorSym: PPLibSymbol = nil): Boolean; +procedure ClearLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer); + // these are for easier crossplatform construction of dll names in dynloading libs. Const @@ -57,11 +115,11 @@ Const {$ifdef OS2} SharedSuffix = 'dll'; {$else} - SharedSuffix = 'so'; + SharedSuffix = 'so'; {$endif} {$endif} - {$endif} - + {$endif} + Implementation { --------------------------------------------------------------------- @@ -88,7 +146,6 @@ Function SafeLoadLibrary(Name : AnsiString) : TLibHandle; var w : word; {$endif} - Begin {$ifdef i386} w:=get8087cw; @@ -100,4 +157,171 @@ Begin {$endif} End; +function LibraryHandler(const AbstractName: String; const Symbols: PLibSymbol; const SymCount: Integer; + const AfterLoading: TLibEventLoading; const BeforeUnloading: TLibEventUnloading): TLibHandler; +begin + Result.AbstractName := AbstractName; + Result.Handle := NilHandle; + Result.Filename := ''; + Result.Loading := AfterLoading; + Result.Unloading := BeforeUnloading; + Result.SymCount := SymCount; + Result.Symbols := Symbols; + Result.ErrorMsg := ''; + Result.RefCount := 0; +end; + +function TryInitializeLibrary(var Handler: TLibHandler; const Filenames: array of String; + const User: Pointer; const Weak: Boolean): Integer; +var + I: Integer; +begin + if Length(Filenames) <= 0 then + begin + Handler.ErrorMsg := SVarInvalid; + Result := -1; + Exit; + end; + + for I := 0 to High(Filenames) do + begin + Result := TryInitializeLibrary(Handler, Filenames[I], User, Weak); + if Result > 0 then + Exit; + end; +end; + +function TryInitializeLibrary(var Handler: TLibHandler; const Filename: String; + const User: Pointer; const Weak: Boolean): Integer; +var + ErrSym: PLibSymbol; +begin + Handler.ErrorMsg := ''; + + if (Handler.Filename <> '') and (Handler.Filename <> Filename) then + begin + Handler.ErrorMsg := Format(SLibraryAlreadyLoaded, [Handler.AbstractName, Handler.Filename]); + Result := -1; + Exit; + end; + + Result := InterlockedIncrement(Handler.RefCount); + if Result = 1 then + begin + Handler.Handle := LoadLibrary(Filename); + if Handler.Handle = NilHandle then + begin + Handler.ErrorMsg := Format(SLibraryNotLoaded, [Handler.AbstractName, Filename]); + Handler.RefCount := 0; + Result := -1; + Exit; + end; + + Handler.Filename := Filename; + + if not LoadLibrarySymbols(Handler.Handle, Handler.Symbols, Handler.SymCount, @ErrSym) and not Weak then + begin + UnloadLibrary(Handler.Handle); + Handler.Handle := NilHandle; + Handler.Filename := ''; + Handler.ErrorMsg := Format(SLibraryUnknownSym, [ErrSym^.name, Handler.AbstractName, Filename]); + Handler.RefCount := 0; + Result := -1; + Exit; + end; + + if Assigned(Handler.Loading) and not Handler.Loading(User, @Handler, Handler.ErrorMsg) then + begin + UnloadLibrary(Handler.Handle); + Handler.Handle := NilHandle; + Handler.Filename := ''; + Handler.RefCount := 0; + Result := -1; + Exit; + end else + Handler.ErrorMsg := ''; + end; +end; + +function InitializeLibrary(var Handler: TLibHandler; const Filenames: array of String; + const User: Pointer; const Weak: Boolean): Integer; +begin + Result := TryInitializeLibrary(Handler, Filenames, User, Weak); + RaiseLibraryException(Handler); +end; + +function InitializeLibrary(var Handler: TLibHandler; const Filename: String; + const User: Pointer; const Weak: Boolean): Integer; +begin + Result := TryInitializeLibrary(Handler, Filename, User, Weak); + RaiseLibraryException(Handler); +end; + +function ReleaseLibrary(var Handler: TLibHandler; User: Pointer): Integer; +begin + Handler.ErrorMsg := ''; + + Result := InterlockedDecrement(Handler.RefCount); + if Result = 0 then + begin + if Assigned(Handler.Unloading) then + Handler.Unloading(User, @Handler); + UnloadLibrary(Handler.Handle); + Handler.Handle := NilHandle; + Handler.Filename := ''; + end else + if Result < 0 then + Handler.RefCount := 0; +end; + +function GetLastLibraryError(var Handler: TLibHandler): String; +begin + Result := Handler.ErrorMsg; + Handler.ErrorMsg := ''; +end; + +procedure RaiseLibraryException(var Handler: TLibHandler); +var + Msg: String; +begin + Msg := GetLastLibraryError(Handler); + if Msg <> '' then + raise EInOutError.Create(Msg); +end; + +function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer; + const ErrorSym: PPLibSymbol): Boolean; +var + P,L: PLibSymbol; +begin + P := Symbols; + L := @Symbols[Count]; + while P < L do + begin + P^.pvar^ := GetProcedureAddress(Lib, P^.name); + if not Assigned(P^.pvar^) and not P^.weak then + begin + if Assigned(ErrorSym) then + ErrorSym^ := P; + Result := False; + Exit; + end; + Inc(P); + end; + Result := True; +end; + +procedure ClearLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer); +var + P,L: PLibSymbol; +begin + P := Symbols; + L := @Symbols[Count]; + while P < L do + begin + P^.pvar^ := nil; + Inc(P); + end; +end; + end.