Sign in Join
Wiki copytemporary

Dynamically loading headers: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Dynamically loading headers

OldNewLine
1+## Abstract
2+
3+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...
4+
5+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.
6+
7+## Pro
8+
9+- loading libraries at runtime for plugin support
10+
11+## Contra
12+
13+- compatibility breaker (using dynamic loading is just an option, that should be choosen carefully)
14+- may produce strange errors, if wrong versions of libraries are loaded.
15+
16+## Suggestion w/o compiler modifications
17+
18+### Framework
19+
20+dynlibs.pas
21+
22+``` pascal
23+type
24+ PLibHandler = ^TLibHandler;
25+
26+ TLibEventLoading = function(User: Pointer; Handler: PLibHandler): Boolean;
27+ TLibEventUnloading = procedure(Handler: PLibHandler);
28+
29+ PPLibSymbol = ^PLibSymbol;
30+ PLibSymbol = ^TLibSymbol;
31+ TLibSymbol = record
32+ pvar: PPointer; { pointer to Symbol variable }
33+ name: String; { name of the Symbol }
34+ weak: Boolean; { no error if this symbol don't exist }
35+ end;
36+
37+ TLibHandler = record
38+ InterfaceName: String; { abstract name of the library }
39+ Defaults : array of String; { list of default library filenames }
40+ Filename : String; { handle of the current loaded library }
41+ Handle : TLibHandle; { filename of the current loaded library }
42+ Loading : TLibEventLoading; { loading event, called after the unit is loaded }
43+ Unloading : TLibEventUnloading; { unloading event, called before the unit is unloaded }
44+ SymCount : Integer; { number of symbols }
45+ Symbols : PLibSymbol; { symbol address- and namelist }
46+ ErrorMsg : String; { last error message }
47+ RefCount : Integer; { reference counter }
48+ end;
49+
50+
51+{ handler definition }
52+function LibraryHandler(const InterfaceName: String; const DefaultLibraries: array of String;
53+ const Symbols: PLibSymbol; const SymCount: Integer; const AfterLoading: TLibEventLoading = nil;
54+ const BeforeUnloading: TLibEventUnloading = nil): TLibHandler;
55+
56+{ initialization/finalization }
57+function TryInitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
58+ const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
59+function TryInitializeLibrary(var Handler: TLibHandler; const LibraryName: String = '';
60+ const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
61+function InitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
62+ const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
63+function InitializeLibrary(var Handler: TLibHandler; const LibraryName: String = '';
64+ const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
65+function ReleaseLibrary(var Handler: TLibHandler): Integer;
66+
67+{ errors }
68+procedure AppendLibraryError(var Handler: TLibHandler; const Msg: String);
69+function GetLastLibraryError(var Handler: TLibHandler): String;
70+procedure RaiseLibraryException(var Handler: TLibHandler);
71+
72+{ symbol load/clear }
73+function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer;
74+ const ErrorSym: PPLibSymbol = nil): Boolean;
75+procedure ClearLibrarySymbols(const Symbols: PLibSymbol; const Count: Integer);
76+```
77+
78+### Naming conventions
79+
80+For the dynamic headers the suffix "dyn" is added
81+
82+- static: foobar.pas
83+- dynamic: foobardyn.pas
84+
85+### Header implementation
86+
87+#### foobar.pas
88+
89+``` pascal
90+unit foobar;
91+
92+{$i foobar.inc}
93+
94+end.
95+```
96+
97+#### foobardyn.pas
98+
99+``` pascal
100+unit foobardyn;
101+
102+{$DEFINE LOAD_DYNAMICALLY}
103+{$i foobar.inc}
104+
105+end.
106+```
107+
108+#### foobar.inc
109+
110+``` pascal
111+{$mode objfpc}{$H+}
112+{$MACRO ON}
113+// other possible compiler flags
114+
115+interface
116+
117+uses
118+ ctypes, dynlibs;
119+
120+{$IFDEF UNIX}
121+ {$DEFINE extdecl:=cdecl}
122+ const
123+ foobarlib = 'libfoobar.'+sharedsuffix;
124+ foobarvlib = foobarlib+'.0.9.9';
125+{$ENDIF}
126+{$IFDEF WINDOWS}
127+ {$DEFINE extdecl:=stdcall}
128+ const
129+ foobarlib = 'libfoobar.dll';
130+ foobarvlib = foobarlib;
131+{$ENDIF}
132+
133+{$IFDEF LOAD_DYNAMICALLY}
134+ {$DEFINE D}
135+{$ELSE}
136+ {$DEFINE S}
137+{$ENDIF}
138+
139+// header stuff
140+
141+{$IFDEF S}function{$ELSE}var{$ENDIF}foobar_dosomething{$IFDEF D}: function{$ENDIF}(a:cint; b:cdouble): cint; extdecl;{$IFDEF S}external foobarlib;{$ENDIF}
142+// more external functions
143+
144+
145+{$IFDEF LOAD_DYNAMICALLY}
146+function InitializeFoobar(const LibraryName: String = ''): Integer;
147+function TryInitializeFoobar(const LibraryName: string = ''): Integer;
148+function ReleaseFoobar: Integer;
149+
150+var
151+ FoobarLibrary: TLibHandler;
152+{$ENDIF LOAD_DYNAMICALLY}
153+
154+implementation
155+
156+{$IFDEF LOAD_DYNAMICALLY}
157+const
158+ foobar_symbols: array[0..X] of TLibSymbol = (
159+ (pvar:@foobar_dosomething; name:'foobar_dosomething'; weak:false),
160+ // more external functions
161+ );
162+
163+function foobar_init(User: Pointer; Handler: PLibHandler): Boolean;
164+var
165+ args: PMysqlArgs absolute User;
166+begin
167+ // do some initialization calls
168+end;
169+
170+procedure foobar_end(Handler: PLibHandler);
171+begin
172+ // do some calls before library is unloaded
173+end;
174+
175+function TryInitializeFoobar(const LibraryName: string): Integer;
176+begin
177+ Result := TryInitializeLibrary(FoobarLibrary, LibraryName);
178+end;
179+
180+function InitializeFoobar(const LibraryName: String): Integer;
181+begin
182+ Result := InitializeLibrary(FoobarLibrary, LibraryName);
183+end;
184+
185+function ReleaseFoobar: Integer;
186+begin
187+ Result := ReleaseLibrary(FoobarLibrary);
188+end;
189+
190+initialization
191+ FoobarLibrary := LibraryHandler('foobar', [foobarlib,foobarvlib, {moreversions} ], @foobar_symbols,
192+ Length(foobar_symbols), @foobar_init, @foobar_end);
193+{$ENDIF}
194+```
195+
196+### Usage
197+
198+``` pascal
199+program foobar_user;
200+
201+uses
202+ foobardyn;
203+
204+begin
205+ InitializeFoobar; // if no paramter is submitted, the default library is loaded
206+ foobar_dosomething(2, 1.3);
207+ ReleaseFoobar;
208+end.
209+```
210+
211+### Notes
212+
213+## Solution of Delphi 2010 (modifying compiler)
214+
215+In Delphi 2010 they introduced "delayed" loading
216+
217+``` pascal
218+function GetFoobar: Integer; external 'foobar.dll' delayed;
219+```
220+
221+More Info: <http://docwiki.embarcadero.com/RADStudio/en/Libraries_and_Packages#Delayed_Loading>
222+
223+## Others
224+
225+Example implementations can be found in
226+
227+- mysql: packages/mysql/src/mysql.inc
228+- sqlite: packages/sqlite/src/sqlite3.inc
229+- zorba: packages/zorba/src/zorba.inc
230+
231+---
232+*From the Free Pascal wiki, page "Dynamically loading headers", as it stood on 2020-02-14 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
233+Category: Proposals

Back to history